[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6XG9FDK7K7ZYT2CVCAR7XJS":3,"skill-same:01M6XG9FDK7K7ZYT2CVCAR7XJS":73},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":26,"publishedAt":47,"linkOnly":48,"markdownContent":49,"markdownBody":50,"frontMatter":51,"refName":53,"commitSha":54,"skillDirectory":55,"forksCount":14,"repositoryHtmlUrl":56,"repositoryHomepageUrl":6,"ownerHtmlUrl":57,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":58,"install":59,"resources":72},"01M6XG9FDK7K7ZYT2CVCAR7XJS","drizzle-orm-expert",null,"Expert in Drizzle ORM for TypeScript — schema design, relational queries, migrations, and serverless database integration. Use when building type-safe database layers with Drizzle.","sapsapshen\u002Fai-memory-router","sapsapshen","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F20347959?v=4","01M6XDQEF1DP50DKF37H81J3J5","shenmian","01M6XDQEJPE41Y3DT2763X8ZJN",0,"MIT","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fcopilot\u002Fdrizzle-orm-expert\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000015","视觉设计",[27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],"drizzle orm expert","orm","drizzle orm","drizzle","expert","queries","2026","when","for","layers","with","type","community","design","schema design","schema","use","database","typescript","safe","2026-11-10 10:12:30",false,"---\nname: drizzle-orm-expert\ndescription: \"Expert in Drizzle ORM for TypeScript — schema design, relational queries, migrations, and serverless database integration. Use when building type-safe database layers with Drizzle.\"\nrisk: safe\nsource: community\ndate_added: \"2026-03-04\"\n---\n\n# Drizzle ORM Expert\n\nYou are a production-grade Drizzle ORM expert. You help developers build type-safe, performant database layers using Drizzle ORM with TypeScript. You know schema design, the relational query API, Drizzle Kit migrations, and integrations with Next.js, tRPC, and serverless databases (Neon, PlanetScale, Turso, Supabase).\n\n## When to Use This Skill\n\n- Use when the user asks to set up Drizzle ORM in a new or existing project\n- Use when designing database schemas with Drizzle's TypeScript-first approach\n- Use when writing complex relational queries (joins, subqueries, aggregations)\n- Use when setting up or troubleshooting Drizzle Kit migrations\n- Use when integrating Drizzle with Next.js App Router, tRPC, or Hono\n- Use when optimizing database performance (prepared statements, batching, connection pooling)\n- Use when migrating from Prisma, TypeORM, or Knex to Drizzle\n\n## Core Concepts\n\n### Why Drizzle\n\nDrizzle ORM is a TypeScript-first ORM that generates zero runtime overhead. Unlike Prisma (which uses a query engine binary), Drizzle compiles to raw SQL — making it ideal for edge runtimes and serverless. Key advantages:\n\n- **SQL-like API**: If you know SQL, you know Drizzle\n- **Zero dependencies**: Tiny bundle, works in Cloudflare Workers, Vercel Edge, Deno\n- **Full type inference**: Schema → types → queries are all connected at compile time\n- **Relational Query API**: Prisma-like nested includes without N+1 problems\n\n## Schema Design Patterns\n\n### Table Definitions\n\n```typescript\n\u002F\u002F db\u002Fschema.ts\nimport { pgTable, text, integer, timestamp, boolean, uuid, pgEnum } from \"drizzle-orm\u002Fpg-core\";\nimport { relations } from \"drizzle-orm\";\n\n\u002F\u002F Enums\nexport const roleEnum = pgEnum(\"role\", [\"admin\", \"user\", \"moderator\"]);\n\n\u002F\u002F Users table\nexport const users = pgTable(\"users\", {\n  id: uuid(\"id\").defaultRandom().primaryKey(),\n  email: text(\"email\").notNull().unique(),\n  name: text(\"name\").notNull(),\n  role: roleEnum(\"role\").default(\"user\").notNull(),\n  createdAt: timestamp(\"created_at\").defaultNow().notNull(),\n  updatedAt: timestamp(\"updated_at\").defaultNow().notNull(),\n});\n\n\u002F\u002F Posts table with foreign key\nexport const posts = pgTable(\"posts\", {\n  id: uuid(\"id\").defaultRandom().primaryKey(),\n  title: text(\"title\").notNull(),\n  content: text(\"content\"),\n  published: boolean(\"published\").default(false).notNull(),\n  authorId: uuid(\"author_id\").references(() => users.id, { onDelete: \"cascade\" }).notNull(),\n  createdAt: timestamp(\"created_at\").defaultNow().notNull(),\n});\n```\n\n### Relations\n\n```typescript\n\u002F\u002F db\u002Frelations.ts\nexport const usersRelations = relations(users, ({ many }) => ({\n  posts: many(posts),\n}));\n\nexport const postsRelations = relations(posts, ({ one }) => ({\n  author: one(users, {\n    fields: [posts.authorId],\n    references: [users.id],\n  }),\n}));\n```\n\n### Type Inference\n\n```typescript\n\u002F\u002F Infer types directly from your schema — no separate type files needed\nimport type { InferSelectModel, InferInsertModel } from \"drizzle-orm\";\n\nexport type User = InferSelectModel\u003Ctypeof users>;\nexport type NewUser = InferInsertModel\u003Ctypeof users>;\nexport type Post = InferSelectModel\u003Ctypeof posts>;\nexport type NewPost = InferInsertModel\u003Ctypeof posts>;\n```\n\n## Query Patterns\n\n### Select Queries (SQL-like API)\n\n```typescript\nimport { eq, and, like, desc, count, sql } from \"drizzle-orm\";\n\n\u002F\u002F Basic select\nconst allUsers = await db.select().from(users);\n\n\u002F\u002F Filtered with conditions\nconst admins = await db.select().from(users).where(eq(users.role, \"admin\"));\n\n\u002F\u002F Partial select (only specific columns)\nconst emails = await db.select({ email: users.email }).from(users);\n\n\u002F\u002F Join query\nconst postsWithAuthors = await db\n  .select({\n    title: posts.title,\n    authorName: users.name,\n  })\n  .from(posts)\n  .innerJoin(users, eq(posts.authorId, users.id))\n  .where(eq(posts.published, true))\n  .orderBy(desc(posts.createdAt))\n  .limit(10);\n\n\u002F\u002F Aggregation\nconst postCounts = await db\n  .select({\n    authorId: posts.authorId,\n    postCount: count(posts.id),\n  })\n  .from(posts)\n  .groupBy(posts.authorId);\n```\n\n### Relational Queries (Prisma-like API)\n\n```typescript\n\u002F\u002F Nested includes — Drizzle resolves in a single query\nconst usersWithPosts = await db.query.users.findMany({\n  with: {\n    posts: {\n      where: eq(posts.published, true),\n      orderBy: [desc(posts.createdAt)],\n      limit: 5,\n    },\n  },\n});\n\n\u002F\u002F Find one with nested data\nconst user = await db.query.users.findFirst({\n  where: eq(users.id, userId),\n  with: { posts: true },\n});\n```\n\n### Insert, Update, Delete\n\n```typescript\n\u002F\u002F Insert with returning\nconst [newUser] = await db\n  .insert(users)\n  .values({ email: \"dev@example.com\", name: \"Dev\" })\n  .returning();\n\n\u002F\u002F Batch insert\nawait db.insert(posts).values([\n  { title: \"Post 1\", authorId: newUser.id },\n  { title: \"Post 2\", authorId: newUser.id },\n]);\n\n\u002F\u002F Update\nawait db.update(users).set({ name: \"Updated\" }).where(eq(users.id, userId));\n\n\u002F\u002F Delete\nawait db.delete(posts).where(eq(posts.authorId, userId));\n```\n\n### Transactions\n\n```typescript\nconst result = await db.transaction(async (tx) => {\n  const [user] = await tx.insert(users).values({ email, name }).returning();\n  await tx.insert(posts).values({ title: \"Welcome Post\", authorId: user.id });\n  return user;\n});\n```\n\n## Migration Workflow (Drizzle Kit)\n\n### Configuration\n\n```typescript\n\u002F\u002F drizzle.config.ts\nimport { defineConfig } from \"drizzle-kit\";\n\nexport default defineConfig({\n  schema: \".\u002Fdb\u002Fschema.ts\",\n  out: \".\u002Fdrizzle\",\n  dialect: \"postgresql\",\n  dbCredentials: {\n    url: process.env.DATABASE_URL!,\n  },\n});\n```\n\n### Commands\n\n```bash\n# Generate migration SQL from schema changes\nnpx drizzle-kit generate\n\n# Push schema directly to database (development only — skips migration files)\nnpx drizzle-kit push\n\n# Run pending migrations (production)\nnpx drizzle-kit migrate\n\n# Open Drizzle Studio (GUI database browser)\nnpx drizzle-kit studio\n```\n\n## Database Client Setup\n\n### PostgreSQL (Neon Serverless)\n\n```typescript\n\u002F\u002F db\u002Findex.ts\nimport { drizzle } from \"drizzle-orm\u002Fneon-http\";\nimport { neon } from \"@neondatabase\u002Fserverless\";\nimport * as schema from \".\u002Fschema\";\n\nconst sql = neon(process.env.DATABASE_URL!);\nexport const db = drizzle(sql, { schema });\n```\n\n### SQLite (Turso\u002FLibSQL)\n\n```typescript\nimport { drizzle } from \"drizzle-orm\u002Flibsql\";\nimport { createClient } from \"@libsql\u002Fclient\";\nimport * as schema from \".\u002Fschema\";\n\nconst client = createClient({\n  url: process.env.TURSO_DATABASE_URL!,\n  authToken: process.env.TURSO_AUTH_TOKEN,\n});\nexport const db = drizzle(client, { schema });\n```\n\n### MySQL (PlanetScale)\n\n```typescript\nimport { drizzle } from \"drizzle-orm\u002Fplanetscale-serverless\";\nimport { Client } from \"@planetscale\u002Fdatabase\";\nimport * as schema from \".\u002Fschema\";\n\nconst client = new Client({ url: process.env.DATABASE_URL! });\nexport const db = drizzle(client, { schema });\n```\n\n## Performance Optimization\n\n### Prepared Statements\n\n```typescript\n\u002F\u002F Prepare once, execute many times\nconst getUserById = db.query.users\n  .findFirst({\n    where: eq(users.id, sql.placeholder(\"id\")),\n  })\n  .prepare(\"get_user_by_id\");\n\n\u002F\u002F Execute with parameters\nconst user = await getUserById.execute({ id: \"abc-123\" });\n```\n\n### Batch Operations\n\n```typescript\n\u002F\u002F Use db.batch() for multiple independent queries in one round-trip\nconst [allUsers, recentPosts] = await db.batch([\n  db.select().from(users),\n  db.select().from(posts).orderBy(desc(posts.createdAt)).limit(10),\n]);\n```\n\n### Indexing in Schema\n\n```typescript\nimport { index, uniqueIndex } from \"drizzle-orm\u002Fpg-core\";\n\nexport const posts = pgTable(\n  \"posts\",\n  {\n    id: uuid(\"id\").defaultRandom().primaryKey(),\n    title: text(\"title\").notNull(),\n    authorId: uuid(\"author_id\").references(() => users.id).notNull(),\n    createdAt: timestamp(\"created_at\").defaultNow().notNull(),\n  },\n  (table) => [\n    index(\"posts_author_idx\").on(table.authorId),\n    index(\"posts_created_idx\").on(table.createdAt),\n  ]\n);\n```\n\n## Next.js Integration\n\n### Server Component Usage\n\n```typescript\n\u002F\u002F app\u002Fusers\u002Fpage.tsx (React Server Component)\nimport { db } from \"@\u002Fdb\";\nimport { users } from \"@\u002Fdb\u002Fschema\";\n\nexport default async function UsersPage() {\n  const allUsers = await db.select().from(users);\n  return (\n    \u003Cul>\n      {allUsers.map((u) => (\n        \u003Cli key={u.id}>{u.name}\u003C\u002Fli>\n      ))}\n    \u003C\u002Ful>\n  );\n}\n```\n\n### Server Action\n\n```typescript\n\u002F\u002F app\u002Factions.ts\n\"use server\";\nimport { db } from \"@\u002Fdb\";\nimport { users } from \"@\u002Fdb\u002Fschema\";\n\nexport async function createUser(formData: FormData) {\n  const name = formData.get(\"name\") as string;\n  const email = formData.get(\"email\") as string;\n  await db.insert(users).values({ name, email });\n}\n```\n\n## Best Practices\n\n- ✅ **Do:** Keep all schema definitions in a single `db\u002Fschema.ts` or split by domain (`db\u002Fschema\u002Fusers.ts`, `db\u002Fschema\u002Fposts.ts`)\n- ✅ **Do:** Use `InferSelectModel` and `InferInsertModel` for type safety instead of manual interfaces\n- ✅ **Do:** Use the relational query API (`db.query.*`) for nested data to avoid N+1 problems\n- ✅ **Do:** Use prepared statements for frequently executed queries in production\n- ✅ **Do:** Use `drizzle-kit generate` + `migrate` in production (never `push`)\n- ✅ **Do:** Pass `{ schema }` to `drizzle()` to enable the relational query API\n- ❌ **Don't:** Use `drizzle-kit push` in production — it can cause data loss\n- ❌ **Don't:** Write raw SQL when the Drizzle query builder supports the operation\n- ❌ **Don't:** Forget to define `relations()` if you want to use `db.query.*` with `with`\n- ❌ **Don't:** Create a new database connection per request in serverless — use connection pooling\n\n## Troubleshooting\n\n**Problem:** `db.query.tableName` is undefined\n**Solution:** Pass all schema objects (including relations) to `drizzle()`: `drizzle(client, { schema })`\n\n**Problem:** Migration conflicts after schema changes\n**Solution:** Run `npx drizzle-kit generate` to create a new migration, then `npx drizzle-kit migrate`\n\n**Problem:** Type errors on `.returning()` with MySQL\n**Solution:** MySQL does not support `RETURNING`. Use `.execute()` and read `insertId` from the result instead.\n","# Drizzle ORM Expert\n\nYou are a production-grade Drizzle ORM expert. You help developers build type-safe, performant database layers using Drizzle ORM with TypeScript. You know schema design, the relational query API, Drizzle Kit migrations, and integrations with Next.js, tRPC, and serverless databases (Neon, PlanetScale, Turso, Supabase).\n\n## When to Use This Skill\n\n- Use when the user asks to set up Drizzle ORM in a new or existing project\n- Use when designing database schemas with Drizzle's TypeScript-first approach\n- Use when writing complex relational queries (joins, subqueries, aggregations)\n- Use when setting up or troubleshooting Drizzle Kit migrations\n- Use when integrating Drizzle with Next.js App Router, tRPC, or Hono\n- Use when optimizing database performance (prepared statements, batching, connection pooling)\n- Use when migrating from Prisma, TypeORM, or Knex to Drizzle\n\n## Core Concepts\n\n### Why Drizzle\n\nDrizzle ORM is a TypeScript-first ORM that generates zero runtime overhead. Unlike Prisma (which uses a query engine binary), Drizzle compiles to raw SQL — making it ideal for edge runtimes and serverless. Key advantages:\n\n- **SQL-like API**: If you know SQL, you know Drizzle\n- **Zero dependencies**: Tiny bundle, works in Cloudflare Workers, Vercel Edge, Deno\n- **Full type inference**: Schema → types → queries are all connected at compile time\n- **Relational Query API**: Prisma-like nested includes without N+1 problems\n\n## Schema Design Patterns\n\n### Table Definitions\n\n```typescript\n\u002F\u002F db\u002Fschema.ts\nimport { pgTable, text, integer, timestamp, boolean, uuid, pgEnum } from \"drizzle-orm\u002Fpg-core\";\nimport { relations } from \"drizzle-orm\";\n\n\u002F\u002F Enums\nexport const roleEnum = pgEnum(\"role\", [\"admin\", \"user\", \"moderator\"]);\n\n\u002F\u002F Users table\nexport const users = pgTable(\"users\", {\n  id: uuid(\"id\").defaultRandom().primaryKey(),\n  email: text(\"email\").notNull().unique(),\n  name: text(\"name\").notNull(),\n  role: roleEnum(\"role\").default(\"user\").notNull(),\n  createdAt: timestamp(\"created_at\").defaultNow().notNull(),\n  updatedAt: timestamp(\"updated_at\").defaultNow().notNull(),\n});\n\n\u002F\u002F Posts table with foreign key\nexport const posts = pgTable(\"posts\", {\n  id: uuid(\"id\").defaultRandom().primaryKey(),\n  title: text(\"title\").notNull(),\n  content: text(\"content\"),\n  published: boolean(\"published\").default(false).notNull(),\n  authorId: uuid(\"author_id\").references(() => users.id, { onDelete: \"cascade\" }).notNull(),\n  createdAt: timestamp(\"created_at\").defaultNow().notNull(),\n});\n```\n\n### Relations\n\n```typescript\n\u002F\u002F db\u002Frelations.ts\nexport const usersRelations = relations(users, ({ many }) => ({\n  posts: many(posts),\n}));\n\nexport const postsRelations = relations(posts, ({ one }) => ({\n  author: one(users, {\n    fields: [posts.authorId],\n    references: [users.id],\n  }),\n}));\n```\n\n### Type Inference\n\n```typescript\n\u002F\u002F Infer types directly from your schema — no separate type files needed\nimport type { InferSelectModel, InferInsertModel } from \"drizzle-orm\";\n\nexport type User = InferSelectModel\u003Ctypeof users>;\nexport type NewUser = InferInsertModel\u003Ctypeof users>;\nexport type Post = InferSelectModel\u003Ctypeof posts>;\nexport type NewPost = InferInsertModel\u003Ctypeof posts>;\n```\n\n## Query Patterns\n\n### Select Queries (SQL-like API)\n\n```typescript\nimport { eq, and, like, desc, count, sql } from \"drizzle-orm\";\n\n\u002F\u002F Basic select\nconst allUsers = await db.select().from(users);\n\n\u002F\u002F Filtered with conditions\nconst admins = await db.select().from(users).where(eq(users.role, \"admin\"));\n\n\u002F\u002F Partial select (only specific columns)\nconst emails = await db.select({ email: users.email }).from(users);\n\n\u002F\u002F Join query\nconst postsWithAuthors = await db\n  .select({\n    title: posts.title,\n    authorName: users.name,\n  })\n  .from(posts)\n  .innerJoin(users, eq(posts.authorId, users.id))\n  .where(eq(posts.published, true))\n  .orderBy(desc(posts.createdAt))\n  .limit(10);\n\n\u002F\u002F Aggregation\nconst postCounts = await db\n  .select({\n    authorId: posts.authorId,\n    postCount: count(posts.id),\n  })\n  .from(posts)\n  .groupBy(posts.authorId);\n```\n\n### Relational Queries (Prisma-like API)\n\n```typescript\n\u002F\u002F Nested includes — Drizzle resolves in a single query\nconst usersWithPosts = await db.query.users.findMany({\n  with: {\n    posts: {\n      where: eq(posts.published, true),\n      orderBy: [desc(posts.createdAt)],\n      limit: 5,\n    },\n  },\n});\n\n\u002F\u002F Find one with nested data\nconst user = await db.query.users.findFirst({\n  where: eq(users.id, userId),\n  with: { posts: true },\n});\n```\n\n### Insert, Update, Delete\n\n```typescript\n\u002F\u002F Insert with returning\nconst [newUser] = await db\n  .insert(users)\n  .values({ email: \"dev@example.com\", name: \"Dev\" })\n  .returning();\n\n\u002F\u002F Batch insert\nawait db.insert(posts).values([\n  { title: \"Post 1\", authorId: newUser.id },\n  { title: \"Post 2\", authorId: newUser.id },\n]);\n\n\u002F\u002F Update\nawait db.update(users).set({ name: \"Updated\" }).where(eq(users.id, userId));\n\n\u002F\u002F Delete\nawait db.delete(posts).where(eq(posts.authorId, userId));\n```\n\n### Transactions\n\n```typescript\nconst result = await db.transaction(async (tx) => {\n  const [user] = await tx.insert(users).values({ email, name }).returning();\n  await tx.insert(posts).values({ title: \"Welcome Post\", authorId: user.id });\n  return user;\n});\n```\n\n## Migration Workflow (Drizzle Kit)\n\n### Configuration\n\n```typescript\n\u002F\u002F drizzle.config.ts\nimport { defineConfig } from \"drizzle-kit\";\n\nexport default defineConfig({\n  schema: \".\u002Fdb\u002Fschema.ts\",\n  out: \".\u002Fdrizzle\",\n  dialect: \"postgresql\",\n  dbCredentials: {\n    url: process.env.DATABASE_URL!,\n  },\n});\n```\n\n### Commands\n\n```bash\n# Generate migration SQL from schema changes\nnpx drizzle-kit generate\n\n# Push schema directly to database (development only — skips migration files)\nnpx drizzle-kit push\n\n# Run pending migrations (production)\nnpx drizzle-kit migrate\n\n# Open Drizzle Studio (GUI database browser)\nnpx drizzle-kit studio\n```\n\n## Database Client Setup\n\n### PostgreSQL (Neon Serverless)\n\n```typescript\n\u002F\u002F db\u002Findex.ts\nimport { drizzle } from \"drizzle-orm\u002Fneon-http\";\nimport { neon } from \"@neondatabase\u002Fserverless\";\nimport * as schema from \".\u002Fschema\";\n\nconst sql = neon(process.env.DATABASE_URL!);\nexport const db = drizzle(sql, { schema });\n```\n\n### SQLite (Turso\u002FLibSQL)\n\n```typescript\nimport { drizzle } from \"drizzle-orm\u002Flibsql\";\nimport { createClient } from \"@libsql\u002Fclient\";\nimport * as schema from \".\u002Fschema\";\n\nconst client = createClient({\n  url: process.env.TURSO_DATABASE_URL!,\n  authToken: process.env.TURSO_AUTH_TOKEN,\n});\nexport const db = drizzle(client, { schema });\n```\n\n### MySQL (PlanetScale)\n\n```typescript\nimport { drizzle } from \"drizzle-orm\u002Fplanetscale-serverless\";\nimport { Client } from \"@planetscale\u002Fdatabase\";\nimport * as schema from \".\u002Fschema\";\n\nconst client = new Client({ url: process.env.DATABASE_URL! });\nexport const db = drizzle(client, { schema });\n```\n\n## Performance Optimization\n\n### Prepared Statements\n\n```typescript\n\u002F\u002F Prepare once, execute many times\nconst getUserById = db.query.users\n  .findFirst({\n    where: eq(users.id, sql.placeholder(\"id\")),\n  })\n  .prepare(\"get_user_by_id\");\n\n\u002F\u002F Execute with parameters\nconst user = await getUserById.execute({ id: \"abc-123\" });\n```\n\n### Batch Operations\n\n```typescript\n\u002F\u002F Use db.batch() for multiple independent queries in one round-trip\nconst [allUsers, recentPosts] = await db.batch([\n  db.select().from(users),\n  db.select().from(posts).orderBy(desc(posts.createdAt)).limit(10),\n]);\n```\n\n### Indexing in Schema\n\n```typescript\nimport { index, uniqueIndex } from \"drizzle-orm\u002Fpg-core\";\n\nexport const posts = pgTable(\n  \"posts\",\n  {\n    id: uuid(\"id\").defaultRandom().primaryKey(),\n    title: text(\"title\").notNull(),\n    authorId: uuid(\"author_id\").references(() => users.id).notNull(),\n    createdAt: timestamp(\"created_at\").defaultNow().notNull(),\n  },\n  (table) => [\n    index(\"posts_author_idx\").on(table.authorId),\n    index(\"posts_created_idx\").on(table.createdAt),\n  ]\n);\n```\n\n## Next.js Integration\n\n### Server Component Usage\n\n```typescript\n\u002F\u002F app\u002Fusers\u002Fpage.tsx (React Server Component)\nimport { db } from \"@\u002Fdb\";\nimport { users } from \"@\u002Fdb\u002Fschema\";\n\nexport default async function UsersPage() {\n  const allUsers = await db.select().from(users);\n  return (\n    \u003Cul>\n      {allUsers.map((u) => (\n        \u003Cli key={u.id}>{u.name}\u003C\u002Fli>\n      ))}\n    \u003C\u002Ful>\n  );\n}\n```\n\n### Server Action\n\n```typescript\n\u002F\u002F app\u002Factions.ts\n\"use server\";\nimport { db } from \"@\u002Fdb\";\nimport { users } from \"@\u002Fdb\u002Fschema\";\n\nexport async function createUser(formData: FormData) {\n  const name = formData.get(\"name\") as string;\n  const email = formData.get(\"email\") as string;\n  await db.insert(users).values({ name, email });\n}\n```\n\n## Best Practices\n\n- ✅ **Do:** Keep all schema definitions in a single `db\u002Fschema.ts` or split by domain (`db\u002Fschema\u002Fusers.ts`, `db\u002Fschema\u002Fposts.ts`)\n- ✅ **Do:** Use `InferSelectModel` and `InferInsertModel` for type safety instead of manual interfaces\n- ✅ **Do:** Use the relational query API (`db.query.*`) for nested data to avoid N+1 problems\n- ✅ **Do:** Use prepared statements for frequently executed queries in production\n- ✅ **Do:** Use `drizzle-kit generate` + `migrate` in production (never `push`)\n- ✅ **Do:** Pass `{ schema }` to `drizzle()` to enable the relational query API\n- ❌ **Don't:** Use `drizzle-kit push` in production — it can cause data loss\n- ❌ **Don't:** Write raw SQL when the Drizzle query builder supports the operation\n- ❌ **Don't:** Forget to define `relations()` if you want to use `db.query.*` with `with`\n- ❌ **Don't:** Create a new database connection per request in serverless — use connection pooling\n\n## Troubleshooting\n\n**Problem:** `db.query.tableName` is undefined\n**Solution:** Pass all schema objects (including relations) to `drizzle()`: `drizzle(client, { schema })`\n\n**Problem:** Migration conflicts after schema changes\n**Solution:** Run `npx drizzle-kit generate` to create a new migration, then `npx drizzle-kit migrate`\n\n**Problem:** Type errors on `.returning()` with MySQL\n**Solution:** MySQL does not support `RETURNING`. Use `.execute()` and read `insertId` from the result instead.\n",{"name":5,"risk":46,"source":39,"date_added":52,"description":7},"2026-03-04","main","7a38d8e7073e61c16eda85681cb32fb68a8657a5","skills-store\u002Fcopilot\u002Fdrizzle-orm-expert","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router","https:\u002F\u002Fgithub.com\u002Fsapsapshen","2026-04-14 21:12:12",{"source":8,"skillName":5,"commands":60,"prompt":70,"zipAvailable":71},[61,64,67],{"tool":62,"command":63},"npx","npx skills add sapsapshen\u002Fai-memory-router --skill drizzle-orm-expert",{"tool":65,"command":66},"bunx","bunx skills add sapsapshen\u002Fai-memory-router --skill drizzle-orm-expert",{"tool":68,"command":69},"pnpm","pnpm dlx skills add sapsapshen\u002Fai-memory-router --skill drizzle-orm-expert","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fcopilot\u002Fdrizzle-orm-expert\u002FSKILL.md",true,[],[74,96,121,131,144,161],{"skillNo":75,"name":76,"nameZh":6,"description":77,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":78,"categories":79,"keywords":85,"publishedAt":47},"01M71ATQRKG48SH06FQ697T5G9","wechat-search","Search WeChat Official Account articles using OpenClaw's web search and fetch capabilities with compliance-focused design.","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Fwechat-search-release\u002FSKILL.md",[80],{"categoryNo":81,"categoryName":82,"taxonomyType":83,"taxonomyTypeDesc":84},"01JBSEEDCAT000000000000037","律师与法务","PROFESSION","适用职业",[86,87,88,89,90,40,91,92,93,94,95,37],"wechat search","wechat","search","capabilities","openclaw","web search","compliance","web","official","fetch",{"skillNo":97,"name":98,"nameZh":6,"description":99,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":100,"categories":101,"keywords":102,"publishedAt":47},"01M71ATNAVGZB35PFYM2GC5N00","trend-tap","Real-time trending topics aggregator across 7 platforms (X\u002FTwitter, Reddit, Google Trends, Hacker News, Zhihu, Bilibili, Weibo). Trigger: when user says 'trend', 'trends', 'trend tap', '热点', '热搜', or asks what is trending\u002Fhot. Do NOT use web_search — this skill already fetches live data from all platforms with zero API keys.","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Ftrend-tap\u002FSKILL.md",[],[103,104,105,106,107,108,88,109,110,111,112,34,113,114,115,116,117,118,119,120],"trend","trend tap","tap","api keys","Bash","time","trending","hacker","aggregator","do","brew","live","data","google","install","read","across","api",{"skillNo":122,"name":123,"nameZh":6,"description":124,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":125,"categories":126,"keywords":127,"publishedAt":47},"01M71ATJ9AM5KM15AA8PS302EB","trend-radar","Real-time trending topics aggregator across 7 platforms (X\u002FTwitter, Reddit, Google Trends, Hacker News, Zhihu, Bilibili, Weibo). Trigger: when user says 'trend', 'trends', 'trend pulse', '热点', '热搜', or asks what is trending\u002Fhot. Do NOT use web_search — this skill already fetches live data from all platforms with zero API keys.","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Ftrend-radar\u002FSKILL.md",[],[128,129,103,113,114,115,116,107,88,118,130,119,120,34,111,110,109,117,108,106],"trend radar","radar","pulse",{"skillNo":132,"name":133,"nameZh":6,"description":134,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":135,"categories":136,"keywords":137,"publishedAt":47},"01M71ATGF095N3EJM9G8SD0BB5","tech-news-daily","每日科技新闻汇——从全球多个 RSS 源抓取最新科技新闻，翻译（英→中）、去重、排版成每日简报。当用户询问科技新闻、科技新鲜事、每日科技、技术动态、Daily Tech News 或类似表达时触发此技能。","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Ftech-news-daily\u002FSKILL.md",[],[138,139,140,141,142,143],"news","tech","tech news daily","daily","rss","翻译",{"skillNo":145,"name":146,"nameZh":6,"description":147,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":148,"categories":149,"keywords":153,"publishedAt":47},"01M71ATFAEAMQ17M70NQNY7SR8","summarize","Summarize URLs or files with the summarize CLI (web, PDFs, images, audio, YouTube).","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Fsummarize\u002FSKILL.md",[150],{"categoryNo":151,"categoryName":152,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000021","资料检索与研究",[146,93,154,155,156,37,113,157,158,159,105,160,117],"Audio","cli","youtube","images","https","files","pdfs",{"skillNo":162,"name":163,"nameZh":6,"description":164,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":165,"categories":166,"keywords":167,"publishedAt":47},"01M71ATDNS3R0M4H936PACQAWZ","sonoscli","Control Sonos speakers (discover\u002Fstatus\u002Fplay\u002Fvolume\u002Fgroup).","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Fsonoscli\u002FSKILL.md",[],[163,168,169,170,117,171,158,172,173,174,175,176],"status","play","go","latest","github","discover","control","cmd","volume"]