Repository navigation
Strategy for sharing runtime-generated schemas from heavy dependencies (e.g., Drizzle) without bundle bloat? (Precompile?) #1630
Replies: 1 comment
|
The thing that makes this tractable is that a TypeBox schema is inert data. { type: 'object', properties: { x: { type: 'number' } }, required: ['x'] }So a Drizzle-derived schema contains nothing from That means you don't need anything TypeBox-specific. You need the generation to happen once at build time, with only its output crossing into the client graph. The build step// scripts/emit-schemas.ts (runs in Node, never bundled)
import { writeFileSync } from 'node:fs'
import { createSchemas } from '../src/server/drizzle-typebox'
const schemas = createSchemas() // your existing runtime generation
writeFileSync(
'src/shared/schemas.generated.json',
JSON.stringify(schemas, null, 2)
)Wire it into Keeping type safetyThis is the part worth thinking about, because there's a real trade-off.
Option A — emit JSON, recover types from Drizzle via type-only imports. Smallest possible bundle: // src/shared/user-schema.ts
import Schema from 'typebox/schema'
import schemas from './schemas.generated.json'
import type { users } from '../server/db/schema' // erased at compile time
export type User = typeof users.$inferSelect
export const UserValidator = Schema.Compile(schemas.User)
Option B — emit TypeBox constructor source instead of JSON. Keeps // src/shared/schemas.generated.ts (generated)
import Type from 'typebox'
export const User = Type.Object({
id: Type.String(),
name: Type.String(),
email: Type.String({ format: 'email' })
})type User = Type.Static<typeof User>Here the frontend bundles I'd default to B if you want a single idiomatic TypeBox story on both sides, and A if the frontend only ever validates and you want to ship literally nothing but data. Validation on the clientEither way, compile once at module scope rather than per call: import Schema from 'typebox/schema'
const UserValidator = Schema.Compile(User)
UserValidator.Check(value) // boolean
UserValidator.Parse(value) // returns value or throws
On driftThe one real risk with any codegen approach is the committed artifact going stale. Two cheap guards: emit into a |
Uh oh!
There was an error while loading. Please reload this page.
Hello,
I am currently generating TypeBox schemas at runtime from my drizzle-orm models. This works perfectly on the backend. However, when used on the frontend (e.g., for form/API validation), it pulls drizzle-orm and the generation logic into the frontend bundle, causing significant dependency bloat.
While my specific use case involves Drizzle, the broader question is: What is the recommended pattern in TypeBox v1 for sharing schemas that are generated at runtime from heavy backend dependencies, without inflating the frontend bundle whilst staying typesafe?
I assume the solution will involve some kind of precompilation or code-generation step to extract the static schemas for the client.
My goal is to keep my code DRY, so I'm hoping someone can advise on the best architectural approach or tooling to solve this generic problem.
Thank you! :)
All reactions