If you know Zod, you already know Schemic. The schema builder you reach for is
s, and it is a superset of z: the same constructors, the same chaining, the
same type inference. There is no new validation dialect to learn — you write the
Zod you already write.
That means s.string(), s.number(), s.enum([...]), .optional(),
.default(), .email() and the rest behave exactly as their z counterparts.
The inference helpers carry over too: s.infer, s.input and s.output give
you the same input/output split, and .coerce works the same way. So a field
you would write in Zod is a field you write in Schemic, unchanged.
import { s, defineTable } from "@schemic/core";
export const User = defineTable("user", {
// Plain Zod — same constructors, same chaining, same inference.
name: s.string().min(1),
email: s.string().email(),
role: s.enum(["admin", "member", "owner"]).default("member"),
// The only addition: $-prefixed methods that emit DDL.
id: s.string().$primaryKey(),
handle: s.string().$unique(),
});
type User = s.infer<typeof User>;So what does Schemic add? Exactly one thing: $-prefixed methods that emit DDL.
$primaryKey(), $unique() and friends are the database-facing layer — they
tell Schemic how a field maps to native schema, indexes and constraints. They
sit alongside the Zod API you already use, never in place of it.
The payoff is that adoption cost is essentially zero. You keep your validation
exactly as it is, add a few $ methods where the database needs them, and get
native DDL, migrations and end-to-end types out of the same typed source of
truth.