drizzle-team / drizzle-team/drizzle-orm

Properly use OpenAPI and zod type refinements for schemas

Open
#4,361 10 comments 0 reactions 0 assignees View on GitHub
drizzle/zod enhancement
Dominant language
TypeScript
Stars
35.8k
Forks
1.6k
Avg merge
2d 7h
Merged PRs (30d)
4

Description

Right now, in order to streamline the use of:
- defining a pgTable schema
- then adding zod-openapi description and type refinement if needed

I'm doing this convoluted thing, not sure if that's a good way to do it or if there's a better way?

the main use case is so that when I add names and descriptions for params, or specific rules, I don't have to repeat them in each of the createSelectSchema, createInsertSchema like `const mySelectSchema = createSelectSchema(myTable, {myField: (z) => z.openapi(...)})`, `const myInsertSchema = createInsertSchema(myTable, {myField: (z) => z.openapi(...)})` etc)

```typescript
// Helper to type fields of pgTables
export type InferTableTransform = Partial<
Record ZodSchema>
>;
export const createTypedSchemas = (
table: TTable,
transforms?: InferTableTransform,
) => {
const { createSelectSchema, createInsertSchema, createUpdateSchema } =
createSchemaFactory({});
return {
select: createSelectSchema(
table,
transforms as
| NoUnknownKeys<
BuildRefine,
TTable["$inferSelect"]
>
| undefined,
),
insert: createInsertSchema(
table,
transforms as
| NoUnknownKeys<
BuildRefine<
Pick
>,
TTable["$inferInsert"]
>
| undefined,
),
patch: createUpdateSchema(
table,
transforms as
| BuildRefine<
Pick
>
| undefined,
),
};
};

export const assemblyTypesTable = entities.table("assembly_types", {
id: uuidIdColumn,
name: text("name").notNull(),
description: text("description"),
allowed_unit_parts: jsonb("allowed_unit_parts")
.notNull()
.$type>>(),
allowed_batch_parts: jsonb("allowed_batch_parts")
.notNull()
.$type>>(),
active: boolean("active").notNull().default(true),
...createdByColumns,
...updatedByColumns,
});

export const {
select: selectAssemblyTypeSchema,
insert: insertAssemblyTypeSchema,
patch: patchAssemblyTypeSchema,
} = createTypedSchemas(assemblyTypesTable, {
id: (schema: ZodSchema) => schema.openapi({ description: "The assembly id" }),
allowed_unit_parts: (schema: ZodSchema) =>
z
.object({
[UnitTrackedPartType.BMS]: z.array(z.string()).optional(),
[UnitTrackedPartType.CASING]: z.array(z.string()).optional(),
})
.openapi({ description: "The allowed types for unit parts" }),
allowed_batch_parts: (schema: ZodSchema) =>
z
.object({
[BatchTrackedPartType.CELL]: z.array(z.string()).optional(),
[BatchTrackedPartType.CONTACT]: z.array(z.string()).optional(),
[BatchTrackedPartType.DCDC]: z.array(z.string()).optional(),
[BatchTrackedPartType.IO]: z.array(z.string()).optional(),
})
.openapi({
description: "The assembly types for batch-tracked parts",
}),
});

```

(don't mind the specific table fields or whatever, those are for my use-case)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.