Ask
92
@visa_run_val ·

zod, typebox or hand-written types for 40 endpoints with 2 devs and no time Schema Types

Internal API, about 40 endpoints, two of us. I want runtime validation at the boundary and types that cannot drift from it. There is an OpenAPI document generated from the handlers today but nobody fully trusts it.

Constraints: some of these schemas get imported into the browser bundle, and our type-check time is already not great. Leaning Zod because everyone knows it, but I have read enough complaints about its compile cost to want a second opinion.

9 answers Share
Report

Answering anonymously — a moderator will review it first.

  • @orm_tamsin · 5mo ago · 3 replies

    Zod, and for two people it is not close. Not because it is technically superior on every axis but because you will spend your time on the product instead of on the schema library, and because when you hire a third person they already know it.

    The honest trade-offs:

    • Zod's inferred types are the heaviest of the three at type-check time. This is real, and it is exactly the kind of thing that shows up in --generateTrace. It also got dramatically better in v4 — if you last formed an opinion on v3, form a new one.
    • Typebox compiles to JSON Schema, so you get your OpenAPI document for free rather than through a bridge library, and validation at runtime is faster because it compiles validators. The developer experience is worse and the error messages are worse.
    • Hand-written types plus hand-written validation means you will eventually have a type and a validator that disagree, and finding out which one is lying is precisely the problem you are trying to eliminate.

    Put the schemas in their own package so editing one does not invalidate your entire type graph.

    66
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
    • @visa_run_val · 5mo ago

      The v3 versus v4 point is fair, most of what I had read was old. Going to prototype five endpoints and actually time the check before committing to all forty.

      22
      Share
      Reply

      Answering anonymously — a moderator will review it first.

      Report
    • @seedstart_sim · 6mo ago

      Timing five endpoints will underestimate it — the cost is superlinear in some of these chains, so measure at twenty if you can stomach it. Still the right instinct, just do not conclude from a small sample that it stays cheap.

      16
      Share
      Reply

      Answering anonymously — a moderator will review it first.

      Report
  • @wellhead_wanda · 6mo ago · 2 replies

    If you already have an OpenAPI document, the question is which artefact is the source of truth, and "nobody fully trusts it" means it currently is not one.

    Pick one direction and enforce it in CI. Either schemas generate the document, or the document generates client types and a check fails when they diverge. With 40 endpoints and two people, contract drift will hurt you long before validation performance does.

    41
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
    • @visa_run_val · 5mo ago

      Schemas generating the document is the direction I want. The current document is produced by a decorator scan that quietly skips anything it does not understand, which is why nobody trusts it.

      13
      Share
      Reply

      Answering anonymously — a moderator will review it first.

      Report
  • @first_repo_finn · 6mo ago

    Counterpoint for Typebox specifically: if JSON Schema is an output you genuinely need, going Zod means adding a converter, and every converter I have used is subtly wrong on unions, on refine, and on anything with a transform. Producing JSON Schema natively removes a whole class of "the docs say a string but the API wants a number" bugs.

    28
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
  • @slowmiles_kat · 5mo ago

    Whatever you pick, parse once at the boundary and pass typed objects inward. The thing that ruins teams is .parse() appearing in twelve layers because nobody trusts the layer above. One parse at the edge, one at any trust boundary like a queue consumer, none anywhere else.

    19
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
  • @pattern_tracer · 5mo ago · 2 replies

    On the bundle point: measure it before it influences the decision. If only a handful of schemas cross into the browser, split those into their own module and let the server keep whatever it likes. Server-side the byte count is irrelevant and people spend a startling amount of time optimising it anyway.

    12
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
    • @resole_ruth · 6mo ago

      Also check whether you actually need runtime validation in the browser or only the types. Plenty of teams ship a validator to the client to re-check data their own server just validated.

      8
      Share
      Reply

      Answering anonymously — a moderator will review it first.

      Report