Skip to main content
One idea at a time

Drizzle connects TypeScript to SQL

Drizzle lets Taskboard describe PostgreSQL tables in TypeScript and build queries using those table definitions. It makes database code easier to navigate without removing the need to understand SQL.

The schema names columns, types, relationships, indexes, and constraints. Drizzle can infer application types from those definitions. A task insert type differs from a selected task type because defaults can supply fields during insertion.

Think of this as a connection between the compiler and a database definition. It is not runtime validation of an HTTP body.

A query still has SQL behavior​

The following snippet illustrates a scoped task lookup:

// Illustrative Drizzle query.
const rows = await db.select().from(tasks).where(and(
eq(tasks.workspaceId, scope.workspaceId),
eq(tasks.id, taskId),
));

eq builds an equality expression. and combines conditions. where becomes a SQL restriction. The query produces an array, even if the application expects at most one row.

The table's primary key establishes the uniqueness. Drizzle's return type alone does not establish that the current caller can access the task. The authorized scope and workspace predicate establish that part of the operation.

Drizzle passes ordinary values as parameters. SQL fragments still need care, especially when building dynamic identifiers or conditions. A query builder cannot infer whether your missing workspace condition was intentional.

There are separate sources of truth​

The TypeScript schema describes the intended database structure. Migration files describe the changes that create or alter the real structure. The running database reflects the migrations that have actually completed.

Editing schema.ts does not automatically change a database already running elsewhere. A successful TypeScript build can therefore coexist with a missing database column.

Runtime validation protects external input. TypeScript helps internal code use that validated input. PostgreSQL constraints protect persisted relationships under concurrency. Drizzle connects the application to the database, but those responsibilities remain separate.

A common mistake is introducing an ORM and then treating database behavior as an implementation detail. You still need to understand transactions, locks, query plans, NULL, and indexes to diagnose production failures.

If Drizzle infers that a title is a string, can the API skip validating JSON?

No. Incoming JSON is untrusted runtime data. The inferred type describes how application code should use a value after validation.

Read Drizzle alongside the SQL it represents. The compiler catches some mistakes before execution, while PostgreSQL enforces the committed data rules.

Read api/src/schema.ts Read api/src/tasks.ts