https://github.com/briefhq/drizzle-zero
Generate Zero schemas from Drizzle ORM schemas.
https://github.com/briefhq/drizzle-zero
drizzle-orm sync zero
Last synced: about 1 month ago
JSON representation
Generate Zero schemas from Drizzle ORM schemas.
- Host: GitHub
- URL: https://github.com/briefhq/drizzle-zero
- Owner: BriefHQ
- License: unlicense
- Created: 2024-12-20T17:32:19.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2025-05-06T23:17:15.000Z (about 1 month ago)
- Last Synced: 2025-05-06T23:22:38.900Z (about 1 month ago)
- Topics: drizzle-orm, sync, zero
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/drizzle-zero
- Size: 849 KB
- Stars: 198
- Watchers: 2
- Forks: 10
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# drizzle-zero
Generate [Zero](https://zero.rocicorp.dev/) schemas from [Drizzle ORM](https://orm.drizzle.team) schemas.
## Installation
```bash
npm install drizzle-zero
# or
yarn add drizzle-zero
# or
pnpm add drizzle-zero
```## Usage
Here's an example of how to convert a Drizzle schema to a Zero schema with bidirectional relationships:
### Define Drizzle schema
You should have an existing Drizzle schema, e.g.:
```ts
import { relations } from "drizzle-orm";
import { pgTable, text, jsonb } from "drizzle-orm/pg-core";export const users = pgTable("user", {
id: text("id").primaryKey(),
name: text("name"),
// custom types are supported for any column type!
email: text("email").$type<`${string}@${string}`>().notNull(),
});export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));export const posts = pgTable("post", {
id: text("id").primaryKey(),
// this JSON type will be passed to Zero
content: jsonb("content").$type<{ textValue: string }>().notNull(),
authorId: text("author_id").references(() => users.id),
});export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));
```See the [integration test's `schema.ts`](integration/drizzle/schema.ts)
for more examples of how to define Drizzle schemas with custom types.### Add schema generation script
You can then add the schema generation script to your `package.json`:
```json
{
"scripts": {
"generate": "drizzle-zero generate --format",
"postinstall": "npm generate"
}
}
```This command will look for a Drizzle Kit config at `drizzle.config.ts` in the current directory
and use the Drizzle schema defined in it. _This must be a single TS file and
not a folder/glob for type resolution to work_. It will also use the
casing defined in your drizzle config.You can change this behavior with `-s, --schema `
as the path to your Drizzle schema file, or
`-k, --drizzle-kit-config ` with the path to your `drizzle.config.ts` file.By default, it will output your schema to `zero-schema.gen.ts`.
You can customize the generated file path with `-o, --output `.If you have Prettier installed, you can use it to format the generated output
with `-f, --format`.To specify a custom tsconfig file, use `-t, --tsconfig `.
It will, by default, look for one in the current directory.**Important:** the Drizzle schema **must be included in the tsconfig** for
type resolution to work. If they are not included, there will be an error similar to
`Failed to find type definitions`.### Define Zero schema file
You can then import the `zero-schema.gen.ts` schema from your Zero `schema.ts`
and define permissions on top of it.```ts
import { type Row, definePermissions } from "@rocicorp/zero";
import { schema, type Schema } from "./zero-schema.gen";export { schema, type Schema };
export type User = Row;
// ^ {
// readonly id: string;
// readonly name: string;
// readonly email: `${string}@${string}`;
// }export const permissions = definePermissions<{}, Schema>(schema, () => {
// ...further permissions definitions
});
```Use the generated Zero schema:
```tsx
import { useQuery, useZero } from "@rocicorp/zero/react";function PostList() {
const z = useZero();// Build a query for posts with their authors
const postsQuery = z.query.post.related("author").limit(10);const [posts] = useQuery(postsQuery);
return (
{posts.map((post) => (
{/* Access the JSON content from Drizzle */}
{post.content.textValue}
By: {post.author?.name}
Email: {post.author?.email}
))}
);
}
```### Customize with `drizzle-zero.config.ts`
If you want to customize the tables/columns that are synced by Zero, you can optionally
create a new config file at `drizzle-zero.config.ts` specifying the columns you want to
include in the CLI output:```ts
import { drizzleZeroConfig } from "drizzle-zero";
import * as drizzleSchema from "./drizzle-schema";// Define your configuration file for the CLI
export default drizzleZeroConfig(drizzleSchema, {
// Specify which tables and columns to include in the Zero schema.
// This allows for the "expand/migrate/contract" pattern recommended in the Zero docs.
// When a column is first added, it should be set to false, and then changed to true
// once the migration has been run.// All tables/columns must be defined, but can be set to false to exclude them from the Zero schema.
// Column names match your Drizzle schema definitions
tables: {
// this can be set to false
// e.g. user: false,
users: {
id: true,
name: true,
email: true,
},
posts: {
// or this can be set to false
// e.g. id: false,
id: true,
content: true,
// Use the JavaScript field name (authorId), not the DB column name (author_id)
authorId: true,
},
},// Specify the casing style to use for the schema.
// This is useful for when you want to use a different casing style than the default.
// This works in the same way as the `casing` option in the Drizzle ORM.
//
// @example
// casing: "snake_case",
});
```You can customize this config file path with `-c, --config `.
**Important:** the `drizzle-zero.config.ts` file **must be included in the tsconfig**
for the type resolution to work. If they are not included, there will be an error similar to
`Failed to find type definitions`.## Many-to-Many Relationships
drizzle-zero supports many-to-many relationships with a junction table. You can configure them in two ways:
### Simple Configuration
```ts
export default drizzleZeroConfig(drizzleSchema, {
tables: {
user: {
id: true,
name: true,
},
usersToGroup: {
userId: true,
groupId: true,
},
group: {
id: true,
name: true,
},
},
manyToMany: {
user: {
// Simple format: [junction table, target table]
// Do not use the same name as any existing relationships
groups: ["usersToGroup", "group"],
},
},
});
```Then query as usual, skipping the junction table:
```tsx
const userQuery = z.query.user.where("id", "=", "1").related("groups").one();const [user] = useQuery(userQuery);
console.log(user);
// {
// id: "user_1",
// name: "User 1",
// groups: [
// { id: "group_1", name: "Group 1" },
// { id: "group_2", name: "Group 2" },
// ],
// }
```### Extended Configuration
For more complex scenarios like self-referential relationships:
```ts
export default drizzleZeroConfig(drizzleSchema, {
tables: {
user: {
id: true,
name: true,
},
friendship: {
requestingId: true,
acceptingId: true,
},
},
manyToMany: {
user: {
// Extended format with explicit field mappings
friends: [
{
sourceField: ["id"],
destTable: "friendship",
destField: ["requestingId"],
},
{
sourceField: ["acceptingId"],
destTable: "user",
destField: ["id"],
},
],
},
},
});
```## Features
- Output static schemas from the CLI
- Convert Drizzle ORM schemas to Zero schemas
- Sync a subset of tables and columns
- Handles all Drizzle column types that are supported by Zero
- Type-safe schema generation with inferred types from Drizzle
- Supports relationships:
- One-to-one relationships
- One-to-many relationships
- Many-to-many relationships with simple or extended configuration
- Self-referential relationships
- Handles custom schemas and column mappings## License
[Unlicense](LICENSE)