Skip to content

v5.0.0

Choose a tag to compare

@aleclarson aleclarson released this 26 May 14:12
· 14 commits to main since this release

Breaking changes

Flattened client action arguments

Generated client methods now take semantic route input as the first argument and fetch options as the second argument.

Before:

await client.hello({
  path: { name: 'world' },
  query: { excited: true },
  headers: { 'x-request-id': 'abc' },
  signal,
})

After:

await client.hello(
  { name: 'world', excited: true },
  { headers: { 'x-request-id': 'abc' }, signal }
)

Path params, query params, and JSON object body fields are flattened into the first input object. RequestInit options and request headers belong in the second options object.

This also updates client response plugin request metadata: ClientResponsePluginRequest now exposes input and options directly instead of nesting them under args.

Mutation body schemas must be Zod objects

JSON mutation body schemas are now restricted to z.ZodObject. This is required so the client can pick body fields out of the flattened input object before validating and JSON encoding the request body.

If you need to send a non-object payload, use the new http.rawBody() marker described below.

New features

Raw request bodies

Added http.rawBody() for routes that need to pass a body through to fetch without JSON encoding, such as binary uploads, images, streams, FormData, or text payloads.

import * as http from 'rouzer/http'

export const uploadAvatar = http.post('avatars/:id', {
  body: http.rawBody(),
})

Client usage:

await client.uploadAvatar(
  { id: 'user-123' },
  { body: imageBytes }
)

Raw body routes:

  • read path params from the first input argument
  • read the raw fetch body from options.body
  • pass the body to fetch unchanged
  • skip client-side JSON stringification
  • skip server-side JSON body parsing, so handlers can read from request directly with APIs like arrayBuffer(), blob(), formData(), or text()

Migration guide

Client calls with path/query/body

Replace nested path, query, and body objects with a single flat input object.

// v4
await client.users.update({
  path: { id: '42' },
  body: { name: 'Grace' },
})

// v5
await client.users.update({ id: '42', name: 'Grace' })

Client calls with request options

Move request options to the second argument.

// v4
await client.search({
  query: { q: 'rouzer' },
  headers: { authorization: token },
  signal,
})

// v5
await client.search(
  { q: 'rouzer' },
  { headers: { authorization: token }, signal }
)

Request-level headers remain optional because headers can still be supplied as createClient({ headers }) defaults.

Non-object request bodies

Replace non-object Zod body schemas with http.rawBody() and pass the payload via options.body.

// v5
export const upload = http.post('upload', {
  body: http.rawBody(),
})

await client.upload(undefined, { body: file })