Files
gh-jezweb-claude-skills-ski…/references/schema-patterns.md
2025-11-30 08:24:43 +08:00

3.4 KiB

Schema Patterns

Complete reference for Drizzle schema definition with SQLite/D1.


Column Types

Text

text('column_name')
text('column_name', { length: 255 }) // Max length (not enforced by SQLite)

Integer

integer('column_name') // JavaScript number
integer('column_name', { mode: 'number' }) // Explicit number (default)
integer('column_name', { mode: 'boolean' }) // Boolean (0/1)
integer('column_name', { mode: 'timestamp' }) // JavaScript Date
integer('column_name', { mode: 'timestamp_ms' }) // Milliseconds

Real

real('column_name') // Floating point

Blob

blob('column_name') // Binary data
blob('column_name', { mode: 'buffer' }) // Node.js Buffer
blob('column_name', { mode: 'json' }) // JSON as blob

Constraints

NOT NULL

text('name').notNull()

UNIQUE

text('email').unique()

DEFAULT (static)

integer('status').default(0)
text('role').default('user')

DEFAULT (dynamic)

integer('created_at', { mode: 'timestamp' })
  .$defaultFn(() => new Date())

PRIMARY KEY

integer('id').primaryKey()
integer('id').primaryKey({ autoIncrement: true })

FOREIGN KEY

integer('user_id')
  .notNull()
  .references(() => users.id)

// With cascade
integer('user_id')
  .notNull()
  .references(() => users.id, { onDelete: 'cascade' })

Indexes

export const users = sqliteTable(
  'users',
  {
    id: integer('id').primaryKey({ autoIncrement: true }),
    email: text('email').notNull().unique(),
    name: text('name').notNull(),
  },
  (table) => {
    return {
      // Single column index
      emailIdx: index('users_email_idx').on(table.email),

      // Composite index
      nameEmailIdx: index('users_name_email_idx').on(table.name, table.email),
    };
  }
);

Relations

One-to-Many

export const usersRelations = relations(users, ({ many }) => ({
  posts: many(posts),
}));

export const postsRelations = relations(posts, ({ one }) => ({
  author: one(users, {
    fields: [posts.authorId],
    references: [users.id],
  }),
}));

Many-to-Many

export const postsToTags = sqliteTable('posts_to_tags', {
  postId: integer('post_id')
    .notNull()
    .references(() => posts.id),
  tagId: integer('tag_id')
    .notNull()
    .references(() => tags.id),
});

export const postsRelations = relations(posts, ({ many }) => ({
  postsToTags: many(postsToTags),
}));

export const tagsRelations = relations(tags, ({ many }) => ({
  postsToTags: many(postsToTags),
}));

TypeScript Types

import { InferSelectModel, InferInsertModel } from 'drizzle-orm';

export type User = InferSelectModel<typeof users>;
export type NewUser = InferInsertModel<typeof users>;

Common Patterns

Timestamps

createdAt: integer('created_at', { mode: 'timestamp' })
  .$defaultFn(() => new Date()),
updatedAt: integer('updated_at', { mode: 'timestamp' }),

Soft Deletes

deletedAt: integer('deleted_at', { mode: 'timestamp' }),

JSON Fields

// Option 1: text with JSON
metadata: text('metadata', { mode: 'json' }),

// Option 2: blob with JSON
settings: blob('settings', { mode: 'json' }),

Enums (Text)

role: text('role', { enum: ['user', 'admin', 'moderator'] }).notNull(),