Changelog
All notable changes to chanfana are documented here.
chanfana
3.2.1
Patch Changes
#325
c182e59Thanks @G4brym! - ExportOrderByDirectiontype alias ("asc" | "desc") so consumers can import it directly instead of inlining literal unions#325
c182e59Thanks @G4brym! - AddpassthroughErrorsoption to bypass chanfana's error handling and let errors propagate raw to the framework's error handlertypescriptimport { Hono } from "hono"; import { fromHono, ApiException } from "chanfana"; import { ZodError } from "zod"; const app = new Hono(); app.onError((err, c) => { // Errors arrive as raw exceptions — no HTTPException wrapping if (err instanceof ApiException) { return c.json( { ok: false, code: err.code, message: err.message }, err.status as any ); } if (err instanceof ZodError) { return c.json({ ok: false, validationErrors: err.issues }, 400); } return c.json({ ok: false, message: "Internal Server Error" }, 500); }); const openapi = fromHono(app, { passthroughErrors: true });
3.2.0
Minor Changes
#314
2408999Thanks @G4brym! - Addtagssupport to auto endpoint_metafor OpenAPI tag grouping#323
d9b7297Thanks @G4brym! - AddhandleErrorhook,defaultOrderByDirection, fix validation error format and D1 update with extra columnsAdd
handleError(error)protected method onOpenAPIRouteto transform errors before chanfana formats them. Enables custom error wrapping (e.g., bypassing chanfana's formatter to use Hono'sonError).Add
defaultOrderByDirectionproperty toListEndpoint(defaults to"asc"). Allows configuring the default sort direction whenorderByFieldsis used.Breaking: Validation errors from
validateRequest()now returnInputValidationExceptionformat instead of raw Zod issues. This makes the actual response match the OpenAPI schema that chanfana documents. If you parse validation error responses, update your code to use the new shape:Before:
json{ "errors": [ { "code": "invalid_type", "expected": "string", "received": "number", "message": "Invalid input: expected string, received number", "path": ["body", "name"] } ], "success": false, "result": {} }After:
json{ "errors": [ { "code": 7001, "message": "Invalid input: expected string, received number", "path": ["body", "name"] } ], "success": false, "result": {} }Key differences:
codeis now the numeric7001(was a string like"invalid_type"), and Zod-specific fields (expected,received) are no longer included.D1UpdateEndpoint.update()now automatically filtersupdatedDatato only include columns defined in the Zod schema. Previously, DB tables with extra columns not in the schema would causevalidateColumnName()to throw.
3.1.0
Minor Changes
#297
59df713Thanks @G4brym! - ### Breaking Changes- Parameter helper functions removed —
Str(),Num(),Int(),Bool(),DateTime(),DateOnly(),Email(),Uuid(),Hostname(),Ipv4(),Ipv6(),Ip(),Regex(),Enumeration(), andconvertParams()have been removed. Use native Zod schemas directly (e.g.,Str()→z.string(),Email()→z.email(),DateTime()→z.iso.datetime()) - Legacy type support removed —
legacyTypeIntoZod,Arr(), andObj()are no longer exported.contentJson()now requires a Zod schema instead of plain objects - D1 endpoint error messages sanitized — Database errors no longer expose internal details. Use the
constraintsMessagesproperty to map constraint violations to user-friendly errors - D1 delete/update restricted to primary key filters — Only filters matching
primaryKeysare used in WHERE clauses for security - D1
per_pagecapped at 100 — Configurable viamaxPerPageclass property raiseUnknownParametersnow enforced — Was previously accepted but not functional; now active
New Features
- 10 new exception classes —
UnauthorizedException(401),ForbiddenException(403),MethodNotAllowedException(405),ConflictException(409),UnprocessableEntityException(422),TooManyRequestsException(429),InternalServerErrorException(500),BadGatewayException(502),ServiceUnavailableException(503),GatewayTimeoutException(504) - D1 SQL injection prevention utilities — New
d1/base.tsmodule withvalidateSqlIdentifier(),validateTableName(),validateColumnName(),buildSafeFilters(),buildPrimaryKeyFilters(),getD1Binding(),handleDbError(), and query clause builders. All exported fromchanfana Retry-AfterHTTP header — Automatically set on responses forTooManyRequestsExceptionandServiceUnavailableExceptionwhenretryAfteris providedgetUnvalidatedData()— New method onOpenAPIRouteto access raw request data before Zod applies defaults/transformations, useful for partial updates with Zod 4- Hono
basePath()auto-detection — Chanfana now detects Hono'sbasePath()automatically; passing bothbasePath()andbaseoption now throws a descriptive error - Hono error flow — Errors now flow through Hono's
app.onErrorasHTTPExceptioninstances instead of being caught internally
Bug Fixes
- D1: Prevent unscoped DELETE/UPDATE —
buildPrimaryKeyFilters()throws when no primary key filters match - D1: Fix shared exception instances —
handleDbError()clones constraint exceptions instead of re-throwing the same object - D1: Fix empty update producing invalid SQL — Returns existing object when no fields to update
- D1: Read endpoint uses primary key filters — Consistent with delete/update behavior
- D1: Delete uses shared
handleDbError— Consistent error handling andconstraintsMessagessupport - D1: Escape LIKE wildcards —
%and_in search values no longer cause unintended pattern matching - D1: Column names validated against model schema — Only fields defined in the Zod schema are accepted in SQL queries
- Schema generation errors propagate — No longer silently swallowed
- CreateEndpoint response schema — Fixed reference from status 200 to 201
- Falsy default values —
0,false, and""are now correctly applied as defaults - BigInt coercion — Uses
BigInt()directly instead ofparseInt()to avoid precision loss - Boolean coercion null guard — Prevents errors when coercing null values to boolean
- HEAD requests — No longer attempt to parse request body
- YAML URL generation — Only replaces trailing
.jsonin URL - ApiException import — Changed from
import typeto value import for properinstanceofchecks - ReadEndpoint & ListEndpoint response schema — Added
InputValidationExceptionto documented 400 responses - Removed dead code —
handleValidationError()andD1EndpointConfiginterface removed
Improvements
- D1 parallel queries — List endpoint runs data and count queries concurrently with
Promise.all() - Configurable
maxPerPage—D1ListEndpoint.maxPerPageis a class property that can be overridden - Normalized ORDER BY direction — Returns lowercase
"asc"/"desc"for consistency sanitizeOperationId()— Ensures operationIds are valid by removing special characters- Router constructor validation —
OpenAPIHandlerthrows if router argument is missing - Comprehensive JSDoc — Added to all exception classes, D1 endpoint methods, and OpenAPI handler methods
- Error responses include
result: {}— Consistent shape with success responses
- Parameter helper functions removed —
#306
9470a04Thanks @G4brym! - ### HonobasePath()supportChanfana now properly handles Hono's
basePath()for route matching, OpenAPI schema generation, and documentation URLs.New features:
- Auto-detection of Hono's
basePath(): When a Hono instance is created withbasePath()(e.g.,new Hono().basePath("/api")), Chanfana automatically detects the base path and uses it for schema generation and doc routes. No need to passbaseseparately. baseoption appliesbasePath()for Hono: UsingfromHono(new Hono(), { base: "/api" })now calls Hono'sbasePath()internally, so routes actually match at the prefixed path — not just in the OpenAPI schema.optionsexposed via proxy: The router proxy now exposes theoptionsproperty for runtime access to the configuredRouterOptions.
New validations:
- Combining
basePath()andbasethrows an error: Using both Hono'sbasePath()and chanfana'sbaseoption (e.g.,fromHono(new Hono().basePath("/api"), { base: "/v1" })) now throws a descriptive error with migration guidance. - Base path format validation: The
baseoption must start with/and must not end with/. Invalid formats now throw a clear error.
Bug fixes:
- Fixed a stale reference in nested route handling where routes were registered on the original Hono instance instead of the based router.
No changes to itty-router behavior.
- Auto-detection of Hono's
b671b4dThanks @G4brym! - ### Hono error handler integrationChanfana errors (validation errors, API exceptions) now flow through Hono's
onErrorhandler automatically when usingfromHono(). Previously, these errors were caught internally and formatted into responses before Hono could see them.How it works:
The Hono adapter converts chanfana errors into Hono
HTTPExceptioninstances with the same JSON response body chanfana normally produces. This means:- Hono's
onErrorhandler receives the error for logging, monitoring, or custom formatting - If no
onErroris configured, Hono's default handler callsHTTPException.getResponse()and returns the formatted response as usual — no behavior change for users withoutonError - Unknown errors (not ZodError or ApiException) propagate as-is without wrapping
Usage:
typescriptimport { fromHono } from "chanfana"; import { Hono } from "hono"; import { HTTPException } from "hono/http-exception"; const app = new Hono(); const openapi = fromHono(app); app.onError((err, c) => { console.error("Caught:", err); if (err instanceof HTTPException) { return err.getResponse(); } return c.json({ error: "Internal Server Error" }, 500); });No changes to itty-router behavior.
Implementation details:
- Added
wrapHandler()hook toOpenAPIHandlerbase class for adapter-specific handler wrapping HonoOpenAPIHandleroverrideswrapHandler()to convert errors toHTTPExceptionvia dynamic import (no runtime cost for itty-router users)- Extracted
formatChanfanaError()utility for consistent error formatting across code paths
- Hono's