Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE]: Type Coercion in drizzle-zod #776

Closed
rawnly opened this issue Jun 16, 2023 · 5 comments
Closed

[FEATURE]: Type Coercion in drizzle-zod #776

rawnly opened this issue Jun 16, 2023 · 5 comments
Labels
drizzle/zod enhancement New feature or request

Comments

@rawnly
Copy link

rawnly commented Jun 16, 2023

Describe what you want

It would be nice to being able to support zod types coercion when using drizzle-zod.
A real world example below:

Consider the following table:

const users = pgTable('users', {
  id: serial('id').primaryKey(),
  created_at: timestamp('created_at').defaultNow()
})

const insertSchema = createInsertSchema(users)

export type InsertUser = z.infer<typeof insertSchema>
export const InsertUser = insertSchema

Later on my endpoint:

export async function GET(request: NextRequest) {
   const body  = await request.json();
   const payload = InsertUser.parse(body) // throws error since Date is serialised as string / number in http requests
}

Something like the following could help writing less boilerplate code and avoid code duplication:

const users = pgTable('users', {
  id: serial('id').primaryKey(),
  created_at: timestamp('created_at').defaultNow()
})

// in order to avoid breaking the current api it can be overloaded and support both function signs (old and new one)
const insertSchema = createInsertSchema(users, true, { /* refinements */ })

export type InsertUser = z.infer<typeof insertSchema>
export const InsertUser = insertSchema
@rawnly rawnly added the enhancement New feature or request label Jun 16, 2023
@rawnly
Copy link
Author

rawnly commented Jun 16, 2023

If this is something you might consider, I can work on a PR. It shouldn't be too difficult to implement as what I saw

@krishna-404
Copy link

Will be happy to contribute to this if somebody can guide me how...

The issue I am facing is that RHF outputs all values as string.. there is a work around to use valueAsNumber but thats difficult to work with MUI.

Right now I am making it work as below... would be good to have auto coercion setup....

export const insertLicSchema = createInsertSchema(lic_table, {
    mobile_number: z.coerce.number()
})

@wesbos
Copy link

wesbos commented Feb 19, 2024

Would love to see this too - though this refinement API seems to be pretty nice for this type of thing:

export const insertListingSchema = createInsertSchema(listings, {
  price: ({ price }) => z.coerce.number().min(500).pipe(price)
});

@danielsharvey
Copy link
Contributor

In order to handle date coercion, I transform the Zod schemas. This may be useful for someone:

/**
 * Make the following schema changes:
 * - Add coercion to ZodDate's (handling optional/nullable wrappers)
 *
 * @param schema
 * @returns
 */
export function fixType<TSchema extends z.ZodTypeAny>(schema: TSchema): TSchema {
  if(schema._def.typeName === z.ZodFirstPartyTypeKind.ZodNullable) {
    const s = schema as unknown as z.ZodNullable<any>;
    const u = s.unwrap();
    return fixType(u).nullable();
  } else if(schema._def.typeName === z.ZodFirstPartyTypeKind.ZodOptional) {
    const s = schema as unknown as z.ZodOptional<any>;
    const u = s.unwrap();
    return fixType(u).optional();
  } else if(schema._def.typeName === z.ZodFirstPartyTypeKind.ZodDate) {
    return z.coerce.date() as unknown as TSchema;
    // return z.string().datetime({ offset:true });
  } else {
    return schema;
  }
}

/**
 * Transform Zod schema to convert Date's to use coercion to handle/parse
 * string inputs.
 *
 * @param schema
 * @returns
 */
export function transformSchema<TSchema extends z.AnyZodObject>(schema: TSchema) {
  // also see https://github.com/colinhacks/zod/discussions/2050#discussioncomment-5018870
  const entries = Object.entries( schema.shape ) as
        [ keyof TSchema[ 'shape' ], z.ZodTypeAny ][]
  const ret = schema.merge(z.object(
    Object.fromEntries(
      entries.map(([k,v]) => ([k,fixType(v)]))
    ),
  ));

  return ret as TSchema;
}

This is used as follows:

const zodTable = transformSchema(createSelectSchema(table));

@AndriiSherman
Copy link
Member

Available in [email protected]

Release Notes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
drizzle/zod enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

6 participants