ESLint
Catch Kizuna mistakes in your editor with the official ESLint plugin.
@kizunajs/eslint-plugin catches Kizuna mistakes in your editor, as you type. These are things the type system can't express on its own.
Setup
Add the recommended config to your eslint.config.js, alongside whatever else you already run:
Turning a rule off
Override it after the recommended config:
Or silence a single line inline:
no-unsupported-schema
Flags anything no generated client can produce a type for: body, query, pathParams, headers, and response schemas in your routes, plus Kizuna.model schemas, inline or imported. It follows imports, so the error lands on the offending field when it's local, or on the reference when the schema comes from another file.
z.coerce
Kizuna already coerces query, path, and header params to their declared types (see Coercion), so z.coerce is redundant, and k.routes throws on it. Use the plain schema:
z.set and z.map
JSON has neither, so JSON.stringify(new Set(['a'])) is {} and the value never reaches the caller. The generated clients cannot name a type for something the wire does not carry:
A union of objects with no discriminator
Two object branches with overlapping fields cannot be told apart from the JSON, so a decoder picks whichever matches first, and which one that is depends on the order you declared them. z.discriminatedUnion puts the answer on the wire:
A union of different kinds is untouched, so the one-or-many query param that accepts a single value or a list still works:
A transform on a response body
A transform's output is whatever the function returns, which no generator can read, so the client would be typed from the input and be wrong about what it receives. Declare the response as the shape it sends:
A transform on the way in is fine, since the caller sends the input and the server parses it.
A nullable query, path, header or form field
A query string, a path, a header and a form field have no null, so every client leaves a null out, and the server rejects the missing key of a .nullable() field. Declare it .optional():
JSON bodies and responses can carry null, so .nullable() there is untouched. See Optional and null fields.
Across files
A schema defined elsewhere, such as a shared PaginationQuery in its own zod-only module, is still caught, and the error lands on the reference: