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-contract — z.object({ tenantId: z.string(), kind: z.literal("order"), … })
examples/order-temporal-contract — tenanted.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.
Raised in review of #73.
The problem
examples/order-api-contractdeclares every input and output with oRPC'stype<T>(), which performs no runtime validation. oRPC's own doc for it:Its runtime
validatereturns the value unchanged (@orpc/contract@2.0.0-beta.28,dist/index.mjs). So every procedure in the HTTP example accepts whatever aclient sends:
{ id: null, quantity: "abc" }arrives at the handler typedstring/numberand 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-contract—z.object({ tenantId: z.string(), kind: z.literal("order"), … })examples/order-temporal-contract—tenanted.extend({ orderId: z.string(), quantity: z.number() })examples/order-domainpackages/amqpandpackages/temporalcarryzodtoo. Only the HTTP contractuses
type<>().Why this does not change the framework's position
The root
CLAUDE.mdline stands:@btravstack/*brings no schema library, anyStandard 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
type<>()call sites inexamples/order-api-contract/src/contract.ts.type<>()also appears in 9 documentation pages, which would otherwise keepteaching 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.zodis already in the catalog;examples/order-api-contractgains it as adependency, matching its two siblings.
Worth deciding while doing it
handler; confirm what status and body that produces and whether it should be
declared in the contract's error map the way
NOT_FOUNDandCONFLICTare,so a client sees it typed.
type<>()keeps a legitimate home. It is honest for a value theserver 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.
first contact; leaving them on
type<>()while the example validates would bea new drift.
Acceptance
examples/order-api-contractvalidates inputs with zod, matching its siblings.the property
type<>()does not have today.where it is deliberate.