Skip to content
Using Rent

Define a schema

A blog needs users and posts, with each post pointing to its author. Describe those models in rent/schema.rsl. Rent uses them to generate both the Rust client and database migrations:

datasource db {
  provider = "postgresql"
}

model User {
  id      BigInt @id @default(autoincrement())
  email   String @unique @db.VarChar(320) @rent.sensitive
  version UInt   @rent.version
  posts   Post[]
}

model Post {
  id        BigInt @id @default(autoincrement())
  author_id BigInt
  author    User   @relation(fields: [author_id], references: [id], onDelete: Cascade)
  title     String

  @@index([author_id])
}

User, User?, and User[] express required to-one, optional to-one, and to-many relationships. Rent resolves both sides of a relationship, synthesizes a correctly typed foreign key for compact declarations, and chooses stable join storage for implicit many-to-many relationships.

Naming

Prefer Rust's idiom and the schema needs almost no naming annotations. A model's table and a field's column are both derived, so the declaration above needs neither @@map nor @map:

  • model User stores to users, the model name lowercased with an s. Use @@map("...") for a name that rule does not produce, including an irregular plural.
  • author_id stores to the column author_id. Use @map("...") only when the column genuinely differs, as in a database you did not create.
  • An enum variant stores its own snake_case form, so DRAFT stores draft without @map.

Generated code is Rust, so identifiers are snake_case whatever the schema writes. A field declared authorId still generates author_id and stores to author_id, which keeps a schema ported from another tool working unchanged. Prefer snake_case in new schemas anyway: it is what the generated code will read as, so the schema and the code agree. Two members that converge on one identifier, such as authorId beside author_id, are rejected rather than silently merged.

Run these commands while editing:

rent validate
rent format
rent describe
rent generate
rent migrate preflight

rent generate reads and validates the schema, then produces Rust models, query builders, mutation methods, relationship loaders, and the typed client. Import those generated types in application code instead of writing a second set of model structs. Rent-specific options use the rent namespace.

For a larger codebase, set schema in rent.toml to a directory. Rent recursively merges .rsl files in lexical path order and resolves relationships across file boundaries.

Repeated fields and model attributes can live in a mixin, applied with use Name inside each model. Mixins can share creator and workspace relations, timestamps, or composite keys while each model keeps its own table. They are optional and independent of permissions. See Model mixins.

Rent-specific database objects remain beside the model they affect:

view ActiveUser {
  id    BigInt
  email String

  @@rent.sql("""
SELECT id, email FROM users WHERE active
""")
}

extension vector {
  name    = "vector"
  version = "0.8.1"
  schema  = "extensions"
}

Triple-quoted strings are an RSL extension intended for readable SQL. Dotted native attributes such as @db.Uuid remain valid, while differentiated features use @rent.version, @rent.generated, @rent.join, @@rent.check, @@rent.policy, and @@rent.rowLevelSecurity. Database connection URLs remain in Rent's project configuration and environment rather than the RSL datasource block, keeping the data model separate from runtime configuration.

Native PostgreSQL enums

Enums normally use portable text storage with generated Rust variants. Opt into a managed PostgreSQL enum when you also want the database to enforce its labels and comparison order:

enum PostState {
  DRAFT
  REVIEW
  PUBLISHED

  @@rent.native
  @@schema("publishing")
}

model Post {
  id    BigInt    @id
  state PostState @default(DRAFT)
}

The model requires PostgreSQL. Rent creates the namespace and type before its table, binds enum values with the correct SQL type, and inspects ordered labels for drift. PostState? permits SQL NULL. PostState[] remains a typed JSON collection rather than a native SQL array. Keep ordinary scalar fields as model identities. A unique native enum field can be the referenced key of a relationship. Eager loading, traversal, connect helpers, upserts, and tenant scoping treat it like any other key.

Adding labels is supported without rewriting existing rows. Put enum additions in their own migration and apply it before a migration that uses the new label in a default or data change. PostgreSQL requires that commit boundary. Removing or reordering existing labels requires a reviewed data migration. Rent does not silently replace the type or discard stored values.