Releases: alloc/rouzer
v4.0.0
What changed
Rouzer v4 removes the low-level client request descriptor API and makes generated, route-backed clients the only supported client shape.
Breaking changes
createClientnow requiresroutes.- Use
createClient({ baseURL, routes })and call generated action functions likeclient.users.get(...).
- Use
- HTTP actions no longer expose nested
.request(...)factories. - Generated clients no longer expose
client.request(...)orclient.json(...)helpers. - The client metadata property was renamed from
configtoclientConfig.- This allows route trees to define an action named
configwithout being overwritten, soclient.config(...)can be a route.
- This allows route trees to define an action named
Why
The removed request factory path was ambiguous for nested resources because action-local factories did not carry parent resource paths. Requiring createClient({ routes }) keeps URL construction, validation, response maps, and response plugins on the same generated action path.
Migration
Before:
const client = createClient({ baseURL })
await client.json(routes.getUser.request({ path: { id: '42' } }))After:
const client = createClient({ baseURL, routes })
await client.getUser({ path: { id: '42' } })If you read the client options from the returned client, use client.clientConfig instead of client.config.
Full Changelog: v3.2.0...v4.0.0
v3.2.0
Typed response maps.
import { $error, $type } from 'rouzer'
import * as http from 'rouzer/http'
export const getUser = http.get('users/:id', {
response: {
200: $type<User>(),
404: $error<{ code: 'NOT_FOUND'; message: string }>(),
},
})Handlers return declared errors with ctx.error:
createRouter().use({ getUser }, {
getUser(ctx) {
if (ctx.path.id === 'missing') {
return ctx.error(404, {
code: 'NOT_FOUND',
message: 'User not found',
})
}
return { id: ctx.path.id, name: 'Ada' }
},
})Clients get typed tuples instead of thrown errors for declared statuses:
const [error, user, status] = await client.getUser({
path: { id: 'missing' },
})Also:
- Add
$error<T>()for declared JSON error responses. - Add
ctx.success(status, body)for choosing explicit declared success statuses. - Support response plugins, including NDJSON, inside response maps.
- Forward extra
RequestInitfields such assignalandcredentialsfrom client calls. - Add runnable typed error response docs and tests.
Response markers are type contracts. Rouzer does not re-validate handler return values at the server boundary; validate data where it enters your server or client code.
Compare: v3.1.0...v3.2.0
v3.1.0
What's new
- Added NDJSON response stream support via the new
rouzer/ndjsonsubpath.- Define streamed responses with
ndjson.$type<T>(). - Register
ndjson.routerPluginwithcreateRouter(...)andndjson.clientPluginwithcreateClient(...). - Handlers can return
Iterable<T>orAsyncIterable<T>values, which Rouzer serializes asapplication/x-ndjson. - Generated client action functions resolve to an
AsyncIterable<T>parsed from the response body.
- Define streamed responses with
- Added response plugin support for non-JSON response codecs, with fast failure when plugin-backed routes are used without the matching router/client plugin.
- Added a runnable NDJSON streaming example:
examples/ndjson-stream.ts.
Notes
- Streamed NDJSON items are parsed as JSON but are not validated against a Zod schema.
- NDJSON support is for response streams; request bodies continue to use the existing JSON body schema path.
- Generated client action functions now follow the documented
client.json(...)non-2xx error behavior.
v3.0.0
Update every route map to rouzer/http.
-import { route } from 'rouzer'
+import * as http from 'rouzer/http'
-export const profileRoute = route('profiles/:id', {
- GET: { response: $type<Profile>() },
- PATCH: { body: updateProfileSchema, response: $type<Profile>() },
+export const profiles = http.resource('profiles/:id', {
+ get: http.get({ response: $type<Profile>() }),
+ update: http.patch({ body: updateProfileSchema, response: $type<Profile>() }),
}) createRouter().use(routes, {
- profileRoute: {
- GET(ctx) { ... },
- PATCH(ctx) { ... },
+ profiles: {
+ get(ctx) { ... },
+ update(ctx) { ... },
},
})-await client.profileRoute.GET({ path: { id: '42' } })
+await client.profiles.get({ path: { id: '42' } })Also:
createClient({ routes })andcreateRouter().use(...)now take HTTP action/resource trees.- Keep
route(...)only for low-levelclient.request(...)/client.json(...)calls. ALLfallback routes and route-levelOPTIONShandlers are removed.@remix-run/route-patternis upgraded to v0.21.
Docs: https://github.com/alloc/rouzer/blob/v3.0.0/docs/context.md
Compare: v2.0.1...v3.0.0
v2.0.0
- Breaking change: Switch from
zod/minito regularzod