Agent skill
drizzle-orm
Drizzle ORM patterns for type branding, custom types, and SQLite column definitions. Use when the user mentions Drizzle, drizzle-orm, or when working with database schemas, branded column types, or custom type conversions in Drizzle.
Install this agent skill to your Project
npx add-skill https://github.com/EpicenterHQ/epicenter/tree/main/.agents/skills/drizzle-orm
Metadata
Additional technical details for this skill
- author
- epicenter
- version
- 1.0
SKILL.md
Drizzle ORM Guidelines
Reference Repositories
- Drizzle ORM — TypeScript ORM with SQL-like query builder
- Turso — Edge-hosted LibSQL database (Epicenter's database)
When to Apply This Skill
Use this pattern when you need to:
- Define Drizzle columns that use branded TypeScript string types.
- Choose between
$type<T>()andcustomTypefor column definitions. - Remove identity
toDriver/fromDriverconversions that add runtime overhead. - Keep data serialized through the storage layer and parse at UI edges.
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);
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
svelte
Svelte 5 patterns including runes ($state, $derived, $props), TanStack Query, SvelteMap reactive state, shadcn-svelte components, and component composition. Use when the user mentions .svelte files, Svelte components, or when using TanStack Query, fromTable/fromKv, or shadcn-svelte UI.
autumn
Integrate Autumn billing—define features/plans in autumn.config.ts, use autumn-js SDK for credit checks/tracking, manage the atmn CLI for push/pull. Use when working on billing, pricing, credits, plan gating, or metered usage.
handoff-prompt
Draft a self-contained implementation prompt that an agent can execute with zero prior context. Use when the user says "draft a prompt", "write a handoff", "make a prompt I can copy-paste", "create a delegation brief", or wants to hand off a task to another agent, tool, or conversation.
typebox
TypeBox and TypeMap patterns for runtime schema validation and JSON Schema generation. Use when the user mentions TypeBox, TypeMap, Standard Schema, or when working with runtime type validation, JSON Schema, or schema-based validation.
factory-function-composition
Apply factory function patterns to compose clients and services with proper separation of concerns. Use when creating functions that depend on external clients, wrapping resources with domain-specific methods, or refactoring code that mixes client/service/method options together.
progress-summary
This skill should be used when the user asks questions like "can you summarize", "what happened", "what did we do", "what's the situation", "where are we at", "explain what's going on", "give me an overview", "what's been done", "tell me about this", "walk me through what happened", or any question asking to understand the current state of work or changes. Provides conversational, PR-style summaries with visual diagrams.
Didn't find tool you were looking for?