chrishoermann / zod-prisma-types

Generator creates zod types for your prisma models with advanced validation
Other
579 stars 43 forks source link

Patterns for Example Usage? #172

Closed makeitrein closed 11 months ago

makeitrein commented 11 months ago

Amazing library, but I'm actually a bit overwhelmed with how many zod schemas it's generated... I've got 12,000 lines of zod schemas from a fairly small prisma.schema file!

This library in conjunction with z.infer<typeof SomeGeneratedZodSchema> seems to offer a nice blend of runtime and compile time type safety, but would definitely appreciate some guidance on how to best use the schemas for maximum benefit and clarity.

For example, here's some generated code for my AbiItem prisma model...

image

Which of the 75 generated AbiItem schemas should I make my CRUD functions conform to?

nordowl commented 11 months ago

I'm using zod-prisma-types together with tRPC, and I have come up with this pattern:

I have so called entities, for example this user entity, which contains some reusable functions

import { db } from "../../config/initialize/prisma.init"
import { UserFindUniqueArgsSchema, UserFindManyArgsSchema } from "mypackage/types"
import { z } from "zod"

const user = {
    // Get a single user
    async getOne(opts: z.infer<typeof UserFindUniqueArgsSchema>) {
        const user = await db.user.findUnique({
            ...opts,
        })
        return user
    },
    // Get many users
    async getMany(opts: z.infer<typeof UserFindManyArgsSchema>) {
        const users = await db.user.findMany({
            ...opts,
        })
        return users
    },
}

export default user

Not sure why I decided to spread the opts. You could just pass opts to the Prisma functions.

And then I have my routers which use those entities

import { t, adminProcedure } from "@/trpc"
import { user } from "@/entities"
import { z } from "zod"
import { UserFindUniqueArgsSchema, UserFindManyArgsSchema } from "mypackage/types"

// All endpoints for user
export const userRouter = t.router({
    // Get one user
    getOne: adminProcedure.input(UserFindUniqueArgsSchema).query(async ({ input }) => {
        const userdata = await user.getOne(input)
        return userdata
    }),

    // Get many users
    getMany: adminProcedure.input(UserFindManyArgsSchema).query(async ({ input }) => {
        const users = await user.getMany(input)
        return users
    }),
})

And writing queries in my Next.js frontend I get full type safety.

const { data } = trpc.user.getOne.useQuery({
    where: { id: "whatever" }
})
chrishoermann commented 11 months ago

As mentioned by @nordowl most of the types are useful when using trpc or when you need to expose the types of prisma queries and mutations to a fetch library like @tanstack/query or similar. The model types also can be useful when validating form inputs in react-hook-form or other form management libraries that allow zod schemas as validation source.