From harness-claude
Defines Drizzle ORM schemas using pgTable/mysqlTable/sqliteTable for PostgreSQL, MySQL, SQLite; covers column types, indexes, constraints, enums, composite keys. For new projects or schema design.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Define Drizzle ORM schemas with pgTable/mysqlTable/sqliteTable, column types, indexes, and constraints
Provides production expertise on Drizzle ORM for TypeScript schema design, migrations, relations, relational queries, and edge/serverless deployments.
Defines type-safe database schemas, queries, relations, and migrations using Drizzle ORM in TypeScript for PostgreSQL, MySQL, SQLite, Cloudflare D1, and Durable Objects.
Provides expertise in Drizzle ORM for TypeScript: schema design, relational queries, Drizzle Kit migrations, and serverless integrations with Neon, Supabase, PlanetScale.
Share bugs, ideas, or general feedback.
Define Drizzle ORM schemas with pgTable/mysqlTable/sqliteTable, column types, indexes, and constraints
import { pgTable, text, timestamp, boolean, uuid } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: uuid('id').primaryKey().defaultRandom(),
email: text('email').notNull().unique(),
name: text('name'),
isActive: boolean('is_active').notNull().default(true),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull().defaultNow(),
});
Choose the correct column types:
text(), varchar('col', { length: 255 }), char()integer(), bigint(), real(), doublePrecision(), numeric('col', { precision: 10, scale: 2 })boolean()timestamp(), date(), time()json(), jsonb() (PostgreSQL)bytea() (PostgreSQL)pgEnum('status', ['active', 'inactive'])Add constraints with chained methods:
export const posts = pgTable('posts', {
id: uuid('id').primaryKey().defaultRandom(),
slug: text('slug').notNull().unique(),
title: text('title').notNull(),
authorId: uuid('author_id')
.notNull()
.references(() => users.id),
viewCount: integer('view_count').notNull().default(0),
});
export const posts = pgTable(
'posts',
{
// ... columns
},
(table) => [
index('posts_author_idx').on(table.authorId),
uniqueIndex('posts_slug_idx').on(table.slug),
index('posts_author_created_idx').on(table.authorId, table.createdAt),
]
);
import { pgEnum } from 'drizzle-orm/pg-core';
export const roleEnum = pgEnum('role', ['user', 'admin', 'moderator']);
export const users = pgTable('users', {
role: roleEnum('role').notNull().default('user'),
});
import { primaryKey } from 'drizzle-orm/pg-core';
export const postTags = pgTable(
'post_tags',
{
postId: uuid('post_id')
.notNull()
.references(() => posts.id),
tagId: uuid('tag_id')
.notNull()
.references(() => tags.id),
},
(table) => [primaryKey({ columns: [table.postId, table.tagId] })]
);
import { InferSelectModel, InferInsertModel } from 'drizzle-orm';
export type User = InferSelectModel<typeof users>;
export type NewUser = InferInsertModel<typeof users>;
db/schema.ts file that the Drizzle config references.Drizzle schemas are pure TypeScript — no code generation step, no DSL. The schema IS the TypeScript code, which means you get autocompletion, refactoring, and type inference for free.
Provider differences: pgTable (PostgreSQL), mysqlTable (MySQL), sqliteTable (SQLite) each support their native column types. You cannot mix providers in one schema. SQLite has the fewest column types; PostgreSQL has the most (arrays, jsonb, custom types).
Column naming convention: The first argument to each column function is the database column name. Use snake_case for database names while keeping TypeScript property names in camelCase:
// TypeScript: user.createdAt → Database: created_at
createdAt: timestamp('created_at').notNull().defaultNow(),
Default values: .default(value) sets a JavaScript default. .defaultNow() and .defaultRandom() generate SQL defaults (DEFAULT NOW(), DEFAULT gen_random_uuid()). Use SQL defaults when records may be inserted outside Drizzle.
References and foreign keys: .references(() => otherTable.column) creates a foreign key constraint. The arrow function is required to handle circular references between tables. Add { onDelete: 'cascade' } for cascade behavior.
Trade-offs:
https://orm.drizzle.team/docs/column-types/pg