Understanding schema.ts
How Drizzle defines database tables and uses them in TypeScript queries.
The schema.ts file describes your database structure in TypeScript:
which tables exist, which columns they contain, and what values those
columns accept.
Drizzle uses these definitions to build queries and infer TypeScript types. Drizzle Kit also reads them when generating database migrations.
A basic schema
Imagine an application that stores users. Its schema might look like this:
import { integer, pgTable, text } from "drizzle-orm/pg-core"
export const users = pgTable("users", {
id: integer("id").primaryKey().generatedAlwaysAsIdentity(),
name: text("name").notNull(),
email: text("email").notNull().unique(),
})This describes a PostgreSQL table named users with three columns:
| Column | Purpose |
|---|---|
id | A unique number generated by the database for each user. |
name | The user's name, stored as text. |
email | The user's email address, stored as text. Duplicate values are rejected. |
.notNull() means the column cannot contain null. It doesn't prevent
an empty string or check whether an email address is valid.
The exported users object is a table definition that you import when
writing queries. The actual user records live in the database.
Editing schema.ts alone doesn't change the database. Apply the changes
through your project's migration workflow.
Querying the table
Once the table exists, you can use Drizzle ORM to read its records.
This example assumes src/db/index.ts exports an already configured
Drizzle database client named db. Run this code on the server.
import { eq } from "drizzle-orm"
import { db } from "./index"
import { users } from "./schema"
export async function getUserById(id: number) {
const result = await db
.select()
.from(users)
.where(eq(users.id, id))
return result
}The query reads as:
.select()— retrieve all columns..from(users)— read from the users table..where(eq(users.id, id))— only include the user with the requested ID.
What it returns
Calling the function:
const result = await getUserById(1)Might produce this value, assuming that user already exists:
[
{
id: 1,
name: "Alex",
email: "[email protected]",
},
]The result is an array, even when only one user matches. If no user has
that ID, the result is [].
Drizzle automatically infers the result's TypeScript type from the schema:
{
id: number
name: string
email: string
}[]You don't need to write that type yourself. Your editor already knows which fields are available and what types they contain.