en un clic
drizzle-database
// Work with Drizzle ORM database schema, migrations, relations, and queries in PostgreSQL. Use when creating tables, modifying schema, writing migrations, defining relations, or querying the database.
// Work with Drizzle ORM database schema, migrations, relations, and queries in PostgreSQL. Use when creating tables, modifying schema, writing migrations, defining relations, or querying the database.
| name | drizzle-database |
| description | Work with Drizzle ORM database schema, migrations, relations, and queries in PostgreSQL. Use when creating tables, modifying schema, writing migrations, defining relations, or querying the database. |
Package: packages/database (@chatbotx.io/database)
Tables use pgTable with sharedColumns spread for consistent id, createdAt, updatedAt:
import { pgTable, text, index, uniqueIndex } from "drizzle-orm/pg-core"
import { sharedColumns, bigintAsString, timestampConfig } from "../partials/shared"
import { otherModel } from "./other"
export const myModel = pgTable(
"MyModel",
{
...sharedColumns,
name: text().notNull(),
description: text(),
workspaceId: bigintAsString()
.notNull()
.references(() => workspaceModel.id, {
onDelete: "cascade",
onUpdate: "cascade",
}),
},
(table) => [
index("MyModel_workspaceId_idx").using(
"btree",
table.workspaceId.asc().nullsLast(),
),
uniqueIndex("MyModel_name_workspaceId_key").on(
table.name,
table.workspaceId,
),
],
)
PascalCase string matching the SQL table namecamelCaseModel (e.g. contactModel, workspaceModel)sharedColumns provides: id (bigint as string, auto-generated), createdAt, updatedAtbigintAsString() custom type: stores bigint in DB, exposes as string in app.references(() => otherModel.id, { onDelete, onUpdate })TableName_columnName_idx or TableName_column_key for uniqueUse pgEnum backed by Zod enums from partials/:
import { pgEnum } from "drizzle-orm/pg-core"
import { myStatusTypes } from "../partials/my-feature"
export const myStatus = pgEnum(
"myStatus",
myStatusTypes.options as [string, ...string[]],
)
Define in src/relations/<domain>.ts using defineRelationsPart:
import { defineRelationsPart } from "drizzle-orm"
import * as schema from "../schema"
export const myModelRelations = defineRelationsPart(schema, (r) => ({
myModel: {
workspace: r.one.workspaceModel({
from: r.myModel.workspaceId,
to: r.workspaceModel.id,
}),
items: r.many.myItemModel({
from: r.myModel.id,
to: r.myItemModel.myModelId,
}),
},
}))
Then add the export to src/relations/index.ts.
r.one.targetModel({ from, to }) — belongs-tor.many.targetModel({ from, to }) — has-manyr.one.through(r.junctionModel.fk) — many-to-many via junctionsrc/schema/ (and src/relations/ if needed)pnpm --filter database make:migration <descriptive_name>pnpm --filter database db:migratepnpm --filter database db:studioMigrations output to packages/database/drizzle/<timestamp>_<name>/migration.sql.
After creating a new table, update 3 files (do all in one batch):
| # | File | Edit |
|---|---|---|
| 1 | src/schema/index.ts | export * from "./<file>" |
| 2 | src/types.ts | export type MyModel = typeof schema.myModel.$inferSelect |
| 3 | src/relations/index.ts | TWO edits: import at top + spread in relations object |
relations/index.ts requires TWO edits:// 1. Add import at top of file (near other imports)
import { myModelRelations } from "./<file>"
// 2. Add spread inside the relations object
export const relations = {
...existingRelations,
...myModelRelations, // ← add this
}
After editing, always read back the file to verify both the import line AND the spread exist. It is very common for one to be added but not the other.
When adding a new channel or integration type:
| File | Edit |
|---|---|
src/partials/channel.ts | Add value to channelTypes z.enum |
src/partials/integration.ts | Add value to integrationTypes z.enum |
CRITICAL cascade: Adding a value to channelTypes causes compile errors in every Record<ChannelType, ...> that doesn't include the new key. Always grep Record<ChannelType across the codebase and fix ALL hits.
import { db } from "@chatbotx.io/database/client"
const items = await db.query.myModel.findMany({
where: { workspaceId },
with: { workspace: true },
columns: { id: true, name: true },
})
const item = await db.query.myModel.findFirst({
where: { id: itemId, workspaceId },
})
import { db, eq, and, inArray } from "@chatbotx.io/database/client"
import { myModel } from "@chatbotx.io/database/schema"
await db
.update(myModel)
.set({ name: "new name" })
.where(and(eq(myModel.id, id), eq(myModel.workspaceId, workspaceId)))
await db.insert(myModel).values({ name, workspaceId })
import { findOrFail } from "@chatbotx.io/database/client"
import { myModel } from "@chatbotx.io/database/schema"
const item = await findOrFail({ table: myModel, where: { id } })
| What | Import from |
|---|---|
db, eq, and, inArray, etc. | @chatbotx.io/database/client |
| Table models | @chatbotx.io/database/schema |
| TypeScript types | @chatbotx.io/database/types |
| Partials, Zod enums | @chatbotx.io/database/partials |
sharedColumns, bigintAsString | ../partials/shared (within package) |
Create and modify integration channels (messenger, whatsapp, zalo, webchat, etc.) for the chatbot platform. Use when adding a new channel integration, modifying webhook handlers, working with message send/receive, or connecting external platforms.
Scaffold new features following project conventions for the builder app. Use when creating a new feature, page, component, server action, query, or adding a new section to the web application.
Create and modify oRPC API routers, procedures, and middleware for the builder app. Use when adding API endpoints, creating routers, defining procedures, working with oRPC middleware, or building OpenAPI routes.
Manage turborepo monorepo development workflow including dev servers, builds, linting, and package management. Use when running dev, build, lint, deploy, or managing workspace packages in this pnpm + turbo monorepo.
Create and manage background workers, BullMQ queues, Kafka consumers, and scheduled jobs. Use when adding new workers, creating queues, defining job types, building scheduled tasks, or working with async processing.