oscklm
oscklm17mo ago

Schemas, types... using Convex with React Hook Forms & Zod

Heyo 👋 So i'm basically trying to improve the way i integrate and use convex mutation in correlation with my react-hook-forms that use Zod for validation. What i'm doing: I have integrated convex into a monorepo using Turborepo, and i define everything convex and schema related within a db package, that i then use in my apps (web, native) So db folder looks like this:
/db
/convex
/_generated
/functions
/dashboard
getStats.ts
/videos
createVideo.ts
/schemas
index.ts
posts.ts
tasks.ts
...
index.ts
package.json
...
/db
/convex
/_generated
/functions
/dashboard
getStats.ts
/videos
createVideo.ts
/schemas
index.ts
posts.ts
tasks.ts
...
index.ts
package.json
...
Example of what my import look like in app using my @flimmer/db
// Somewhere far far away in distant lands...
import { createVideoFormSchema } from "@flimmer/db/schemas";
// Somewhere far far away in distant lands...
import { createVideoFormSchema } from "@flimmer/db/schemas";
Example of a schema in the db package
// db/schemas/videos.ts
import { v } from "convex/values";
import { defineTable } from "convex/server";
import { z } from "zod";

export const videosTableSchema = defineTable({
// Removed to save space \ (^_^) /
}).index("by_muxAssetId", ["muxAssetId"]);

export const createVideoFormSchema = z.object({
// Removed to save space \ (^_^) /
});
// db/schemas/videos.ts
import { v } from "convex/values";
import { defineTable } from "convex/server";
import { z } from "zod";

export const videosTableSchema = defineTable({
// Removed to save space \ (^_^) /
}).index("by_muxAssetId", ["muxAssetId"]);

export const createVideoFormSchema = z.object({
// Removed to save space \ (^_^) /
});
My plan is: To define the zod form schemas that i use within my react-hook-forms within these individual schema files, to keep it all in one place and allow for importing in apps using db pkg. I haven't come up with a better approach than defining the table schemas and form schemas separately, even though i'm on the looks for an improved way where i could maybe define a base type and then "piggy back" off those types and create my schemas. Another viable option i suppose would be to build a convex validation resolver, but that's not something i've delved into - and unsure whether i lack the skills to pull that off haha. I would love some advice / thoughts on my approach ⭐️
7 Replies
ballingt
ballingt17mo ago
@RJ wrote a Convex validation resolver/builder for converting Effect Schemas to Convex validators, I think writing one for Zod is possible — that'd be a real useful library for lots of folks. Zod is always going to be more descriptive than a Convex schema (Zod can check that a number is even, check that an email address looks valid, etc.) so you'll probably want to run your Zod validation on the server too. Your plan sounds good, defining Zod an Convex schemas in one place. You may be able to use type-level tests to check that the inferred type of the Zod validator is a subset of the inferred type of the Convex schema.
oscklm
oscklmOP17mo ago
That's cool! will def look into that, might be a fun project trying to build something like that for Zod. And worth it if some people could use it, i definitely could. I haven't thought about type-level tests at all, so thats interesting. Will look into that!
oscklm
oscklmOP17mo ago
Ahh i found this stack article Ian wrote, which is kinda cool. Testing this now https://stack.convex.dev/wrappers-as-middleware-zod-validation
Zod Validation: Wrappers as “Middleware”
Function validation is important for a production app because you can’t always control which clients are talking to your server. See how to use zod to...
RJ
RJ17mo ago
Yeah, I think the ideal scenario here is to be able to use your preferred schema library (sounds like Zod, in your case) to define your Convex DB schema, and also to use the same schema library to define Convex function validators. To make that happen, though, you need: 1. A function which "compiles" an e.g. Zod schema into an equivalent Convex validator, which probably fails in the case where the Zod schema contains some element which is unsupported by Convex validators, and 2. A lot of wrapping around Convex APIs (if you want to make use of these Zod schemas truly seamless) Here's the compiler I wrote for the Effect Schema library: https://github.com/rjdellecese/effect-convex/blob/985bfb3332e8bb35f7277f3ca0db2b723a925a62/src/schema-to-validator-compiler.ts. The args function converts the Schema to the shape that conforms to a Convex function's args parameter, but you could also imagine a compiler that produces a schema that conforms to a Convex table or whole (Convex) schema definition. But I agree with Tom, I think what you're doing here looks like a pretty good approach for now (co-locating the schemas, maybe with some type-level tests) I don't remember if Convex exposes a way to directly test a Validator<A>, but if so you could also write some value-level tests which runs some test data against the same Zod schema and Convex validator
oscklm
oscklmOP17mo ago
Awesome, thanks for your input, RJ - and for the link to the compiler u wrote Yeah, you're right, that's the dream scenario. I think I'm gonna put this on my to-do list and circle back to improve it later. It feels like deep waters for me at the moment, not really sure my skills suffice. But it should be a nice challenge and a good learning curve for later...
ian
ian15mo ago
Update on zod validation support: I made a new way to add zod validators for both input and output validation, installable from npm i convex-helpers: https://stack.convex.dev/typescript-zod-function-validation
Using Zod with TypeScript for Server-side Validation and End-to-End...
Use Zod with TypeScript for argument validation on your server functions allows you to both protect against invalid data, and define TypeScript types ...
ian
ian15mo ago
And @RJ I have an example doing a round trip to a Date type, which might be interesting to you (though not exactly what you've been doing): https://github.com/get-convex/convex-helpers/blob/main/convex/zodExample.ts#L71-L77
GitHub
convex-helpers/convex/zodExample.ts at main · get-convex/convex-hel...
A collection of useful code to complement the official packages. - get-convex/convex-helpers

Did you find this page helpful?