Skip to content

The HTTP example contract validates nothing: type<>() is not a schema #74

Description

@btravers

Raised in review of #73.

The problem

examples/order-api-contract declares every input and output with oRPC's
type<T>(), which performs no runtime validation. oRPC's own doc for it:

Create a schema for things can be trust without validation.

Its runtime validate returns the value unchanged (@orpc/contract@2.0.0-beta.28,
dist/index.mjs). So every procedure in the HTTP example accepts whatever a
client sends: { id: null, quantity: "abc" } arrives at the handler typed
string / number and reaches the use case.

That is the one transport where input comes from a browser rather than from
inside the trust boundary, and it is the only example not validating.

It is the outlier, not a new proposal

Already on zod, with real object schemas:

  • examples/order-amqp-contractz.object({ tenantId: z.string(), kind: z.literal("order"), … })
  • examples/order-temporal-contracttenanted.extend({ orderId: z.string(), quantity: z.number() })
  • examples/order-domain

packages/amqp and packages/temporal carry zod too. Only the HTTP contract
uses type<>().

Why this does not change the framework's position

The root CLAUDE.md line stands: @btravstack/* brings no schema library, any
Standard Schema is accepted, and the fields exist so a starter and an ordinary
application need none. This is about what the examples teach, and two of
three already teach validation.

Scope

  • 10 type<>() call sites in examples/order-api-contract/src/contract.ts.
  • type<>() also appears in 9 documentation pages, which would otherwise keep
    teaching the unvalidated form: docs/index.md, docs/tutorial/getting-started.md,
    docs/tutorial/second-runtime.md, docs/how-to/serve-orpc-over-http.md,
    docs/how-to/split-a-router-into-controllers.md,
    docs/how-to/protect-a-procedure.md, docs/examples/order-api.md,
    docs/examples/order-application.md, docs/reference/contract.md.
  • zod is already in the catalog; examples/order-api-contract gains it as a
    dependency, matching its two siblings.

Worth deciding while doing it

  • What a validation failure answers. oRPC rejects a bad input before the
    handler; confirm what status and body that produces and whether it should be
    declared in the contract's error map the way NOT_FOUND and CONFLICT are,
    so a client sees it typed.
  • Whether type<>() keeps a legitimate home. It is honest for a value the
    server itself produced — an output already built from a domain entity — where
    re-validating buys nothing. If so, say where it is right rather than banning it.
  • Whether the tutorial pages move in the same change. They are a reader's
    first contact; leaving them on type<>() while the example validates would be
    a new drift.

Acceptance

  • examples/order-api-contract validates inputs with zod, matching its siblings.
  • A spec proves a malformed input is refused before the use case is reached —
    the property type<>() does not have today.
  • The documentation samples stop teaching the unvalidated form, or state plainly
    where it is deliberate.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions