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.

Stars 4,333
Forks 311

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>() and customType for column definitions.
  • Remove identity toDriver/fromDriver conversions 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:

typescript
// 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

typescript
// 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

typescript
// Zero runtime overhead - pure type assertion
text().$type<DateTimeString>();

$type<T>() is a compile-time-only type override:

typescript
// 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:

typescript
// 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:

typescript
// 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:

typescript
// 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);

Expand your agent's capabilities with these related and highly-rated skills.

EpicenterHQ/epicenter

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.

4,333 311
Explore
EpicenterHQ/epicenter

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.

4,333 311
Explore
EpicenterHQ/epicenter

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.

4,333 311
Explore
EpicenterHQ/epicenter

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.

4,333 311
Explore
EpicenterHQ/epicenter

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.

4,333 311
Explore
EpicenterHQ/epicenter

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.

4,333 311
Explore

Didn't find tool you were looking for?

Be as detailed as possible for better results