drizzle-orm
Provides expert guidance on Drizzle ORM schema design, migration management, and type-safe query building.
Install
mkdir -p .claude/skills/drizzle-orm && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/1104" && unzip -o skill.zip -d .claude/skills/drizzle-orm && rm skill.zipInstalls to .claude/skills/drizzle-orm
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Drizzle ORM patterns: schema definitions, Drizzle Kit migrations, query builders, type branding, custom types, SQLite, Postgres, D1, and Turso/libSQL boundaries. Use when mentioning Drizzle, drizzle-orm, DB schemas, migrations, branded column types, or typed SQL queries.Key capabilities
- →Define SQL schemas with explicit table relations
- →Configure Drizzle Kit migrations using snapshot diffs
- →Implement branded TypeScript types using $type<T>()
- →Build type-safe SQL queries with the query builder
- →Perform custom type conversions for non-identity data
How it works
It uses a typed query builder for application queries and provides schema-level utilities to define database structures. It enforces type safety by mapping database columns to TypeScript types, including branded types for compile-time validation.
Inputs & outputs
When to use drizzle-orm
- →Define a new database schema
- →Configure Drizzle Kit migrations
- →Build type-safe SQL queries
- →Implement custom column type conversions
About this skill
Drizzle ORM Guidelines
Reference Repositories
- Drizzle ORM : TypeScript ORM with SQL-like query builder
- Turso : Edge-hosted LibSQL database (Epicenter's database)
Upstream Grounding
When Drizzle schema definitions, migration snapshots, query builder APIs, column typing, custom types, or driver integration affect correctness, use source-backed grounding before relying on memory. If DeepWiki MCP is available, ask a narrow question against drizzle-team/drizzle-orm; for libSQL, Turso sync, embedded replicas, D1 compatibility, or remote SQLite behavior, ask against tursodatabase/turso. If DeepWiki is unavailable or the repo is not indexed, use upstream source or official docs directly. Treat DeepWiki as orientation, then verify decisive details against local installed types, generated migrations, source, driver versions, or official docs before changing code.
Skip DeepWiki for repo-local schema naming and storage-boundary conventions already documented below.
Schema And Migration Rules
- Export a single schema object from the app's database module and pass that same object to
drizzle(...)and Drizzle Kit config. - Keep table definitions, relations, and schema exports explicit. Avoid dynamic schema construction that Drizzle Kit cannot statically inspect.
- Treat Drizzle Kit snapshots as the diff source of truth. Review generated SQL and snapshot changes together.
- Pick a casing strategy once per database. Do not mix app-level camelCase with ad hoc SQL aliases unless the boundary owns that mapping.
- Use
drizzle-zod,drizzle-valibot, or a local schema parser at IO boundaries when external input becomes a row. Do not treat inferred insert types as runtime validation.
Query Builder Rules
- Prefer the typed query builder for application queries. Use raw SQL only for expressions the query builder cannot express cleanly.
- Keep joins and selected shapes near the caller that owns the response contract.
- Add indexes in schema beside the query pattern that needs them.
Driver Boundaries
bun:sqliteandbetter-sqlite3are local synchronous SQLite drivers. Do not use them in Cloudflare Workers.- D1 is a Cloudflare binding with Worker-specific behavior. Keep it behind Worker code and generated bindings.
- libSQL and Turso are SQLite-compatible but have network, sync, and compatibility details that are not generic SQLite. Use the
tursoskill for those decisions.
Use $type<T>() for Branded Strings, Not customType
When you need a column with a branded TypeScript type but no actual data transformation, use $type<T>() instead of customType.
The Rule
If toDriver and fromDriver would be identity functions (x) => x, use $type<T>() instead.
Why
Even with identity functions, customType still invokes mapFromDriverValue on every row:
// drizzle-orm/src/utils.ts - runs for EVERY column of EVERY row
const rawValue = row[columnIndex]!;
const value = rawValue === null ? null : decoder.mapFromDriverValue(rawValue);
Query 1000 rows with 3 date columns = 3000 function calls doing nothing.
Bad Pattern
// Runtime overhead for identity functions
customType<{ data: DateTimeString; driverParam: DateTimeString }>({
dataType: () => 'text',
toDriver: (value) => value, // called on every write
fromDriver: (value) => value, // called on every read
});
Good Pattern
// Zero runtime overhead - pure type assertion
text().$type<DateTimeString>();
$type<T>() is a compile-time-only type override:
// drizzle-orm/src/column-builder.ts
$type<TType>(): $Type<this, TType> {
return this as $Type<this, TType>;
}
When to Use customType
Only when data genuinely transforms between app and database:
// JSON: object ↔ string - actual transformation
customType<{ data: UserPrefs; driverParam: string }>({
toDriver: (value) => JSON.stringify(value),
fromDriver: (value) => JSON.parse(value),
});
Keep Data in Intermediate Representation
Prefer keeping data serialized (strings) through the system, parsing only at the edges (UI components).
The principle: If data enters serialized and leaves serialized, keep it serialized in the middle. Parse at the edges where you actually need the rich representation.
Example: DateTimeString
Instead of parsing DateTimeString into Temporal.ZonedDateTime at the database layer:
// Bad: parse on every read, re-serialize at API boundaries
customType<{ data: Temporal.ZonedDateTime; driverParam: string }>({
fromDriver: (value) => fromDateTimeString(value),
});
Keep it as a string until the UI actually needs it:
// Good: string stays string, parse only in date-picker component
text().$type<DateTimeString>();
// In UI component:
const temporal = fromDateTimeString(row.createdAt);
// After edit:
const updated = toDateTimeString(temporal);
When not to use it
- →Using customType for identity functions where $type<T>() suffices
- →Dynamic schema construction that Drizzle Kit cannot inspect
- →Mixing app-level casing with ad hoc SQL aliases
Prerequisites
Limitations
- →Requires explicit schema definitions for static inspection
- →Performance overhead if customType is used for identity functions
How it compares
Unlike manual SQL string concatenation, this approach provides compile-time type checking and automated migration management.
Compared to similar skills
drizzle-orm side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| drizzle-orm (this skill) | 32 | 2mo | No flags | Intermediate |
| backend-dev | 1 | 6mo | No flags | Intermediate |
| prisma-database | 0 | 6mo | Review | Intermediate |
| ck:databases | 0 | 3mo | Review | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by EpicenterHQ
View all by EpicenterHQ →You might also like
backend-dev
marmelab
Coding practices for backend development in Atomic CRM. Use when deciding whether backend logic is needed, or when creating/modifying database migrations, views, triggers, RLS policies, edge functions, or custom dataProvider methods that call Supabase APIs.
prisma-database
slashwhy
Prisma schema conventions, migrations, seeding, and query patterns. Use when modifying database schema, creating migrations, or writing complex queries.
ck:databases
lengo0951
Design schemas, write queries for MongoDB and PostgreSQL. Use for database design, SQL/NoSQL queries, aggregation pipelines, indexes, migrations, replication, performance optimization, psql CLI.
event-store-design
wshobson
Design and implement event stores for event-sourced systems. Use when building event sourcing infrastructure, choosing event store technologies, or implementing event persistence patterns.
database-design
davila7
Database design principles and decision-making. Schema design, indexing strategy, ORM selection, serverless databases.
backend-development
skillcreatorai
Backend API design, database architecture, microservices patterns, and test-driven development. Use for designing APIs, database schemas, or backend system architecture.