Speaking Report from "Cloudflare Workers Tech Talks in Fukuoka #1" ~ Decision-Making for Adopting Hono in Acsim, an AI Requirements Definition Platform
I had the opportunity to speak at the community event “Cloudflare Workers Tech Talks in Fukuoka #1” held in Fukuoka.
This article shares the content of the presentation along with the materials.
Presentation Content
Below are excerpts from the presentation slides “Why We Chose Hono for Acsim” from the event.
🎯 Presentation Overview
I introduced the reasons for adopting Hono + @hono/zod-openapi in the development of Acsim, an AI requirements definition platform, and the development experience it brought.
Through schema-driven development utilizing OpenAPI, we achieved the following 👇
- Sharing type information between Frontend and API, improving implementation speed
- Ensuring type safety at both compile-time and runtime
- Enhanced development experience through high compatibility with TanStack Query
Background: Organizing the Challenges
Challenges We Wanted to Solve
-
Improving implementation speed between Frontend and API
- Maintain implementation consistency
- Improve developer experience
-
Quality improvement through sharing type information
- Prevent bugs due to type mismatches
- Make refactoring safer
Decision: Schema-Driven Development Using OpenAPI
Architecture
API (Hono + @hono/zod-openapi)
↓
OpenAPI Schema Generation
↓
Frontend (orval)
↓
Type Information + TanStack Query Client Generation
Why Hono: Reasons for Selecting @hono/zod-openapi
1. TypeScript-Native Description
const route = createRoute({
method: 'post',
path: '/users',
request: { body: { content: { 'application/json': { schema: UserSchema } } } },
responses: { 200: { content: { 'application/json': { schema: UserSchema } } } }
});- Can be written in TypeScript instead of YAML/JSON
- Low learning curve
2. High Compatibility with Hono
- Library created by Hono’s developer
- Seamlessly integrates with Hono middleware
- Framework and schema definition are unified
3. Language-Agnostic Extensibility
- OpenAPI is a unified standard
- Can be utilized when migrating to another language in the future
Why orval: Code Generation on the Frontend Side
Compatibility with TanStack Query
// Auto-generated code
export const useGetUsers = () => {
return useQuery({
queryKey: ['users'],
queryFn: () => getUsersApi(),
});
};- Auto-generates hooks for TanStack Query
- Automates Query Key management
- Simplifies request handling
Comparison with Other Options
Options Considered
| Approach | Adopted | Reason |
|---|---|---|
| Generate both API/Frontend with orval | ❌ | Difficult to insert Middleware |
| openapi-generator | ❌ | Requires extensive configuration for TanStack Query integration |
| @hono/zod-openapi + orval | ✅ | Optimal learning cost and compatibility |
Benefits: Development Experience Gained
1. Ensuring Type Safety
- Prevents type mismatches between Frontend and API
- Detects errors at compile time
2. Improved Development Speed
- Auto-generation of API client code
- Reduced boilerplate
3. Improved Maintainability
- Automation of Query Key management
- Consistent codebase
Drawbacks: Trade-offs
Operational Costs
-
OpenAPI Schema needs to be updated when API changes
- Mitigated by auto-generation mechanisms
-
Cost of writing OpenAPI Schema
- Low learning cost since it can be written in TypeScript
-
orval configuration cost
- Initial setup is required, but once configured, it’s reusable
Practical Example: Response Schema Validation Middleware
Background and Challenges
While type safety between frontend and API is guaranteed by type definitions, there was a problem with backend responses:
- Limitations of type checking: TypeScript types only check for the existence of required properties and cannot detect extra properties
- Security risks: Even if sensitive information like passwords is mistakenly included in responses, it won’t result in a compile error
- Reviews alone are insufficient: Relying solely on human checks is difficult and has significant impact
Solution
By parsing responses with Zod schema at runtime:
- Automatically exclude properties not included in type definitions
- Prevent accidental transmission of sensitive information
- Guarantee response format
Usage
...createResponsesAndMiddleware({
response: {
schema: getAsIsPatternResponseSchema,
status: 200,
description: "asIsの業務パターンの詳細を取得します",
},
}),Implementation Points
export const createResponsesAndMiddleware = <T extends z.ZodSchema, U extends ContentfulStatusCode>({
response,
middleware: _middleware = [],
}: {
response: { schema: T; status: U; description: string };
middleware?: MiddlewareHandler[];
}) => {
const { schema, status, description } = response;
const responses: { [key in U]: { content: { "application/json": { schema: T } }; description: string } } = {
[status]: {
content: {
"application/json": {
schema,
},
},
description,
},
};
// Automatically apply response schema validation middleware
const middleware = [responseSchemaValidationMiddleware(schema, status), ..._middleware];
return { responses, middleware };
};Implementation Points
export const responseSchemaValidationMiddleware = <T extends ZodSchema, U extends ContentfulStatusCode>(
schema: T,
status: U
): MiddlewareHandler => {
return async (c, next) => {
await next();
// Validate only JSON responses with success status codes (200-299)
if (c.res.headers.get("Content-Type") !== "application/json") return;
if (!isSuccessStatusCode(c.res.status)) return;
const responseData = await c.res.json();
const result = schema.safeParse(responseData);
if (!result.success) {
// Return 500 error on parse failure to detect issues immediately
c.res = c.json({ message: "Response schema validation failed", errors: result.error.flatten().fieldErrors }, 500);
return;
}
// Safely exclude extra properties using Zod's safeParse
c.res = c.json(result.data, status);
};
};Practical Example: Response Schema Validation Middleware
Effects
Ensuring type safety at both compile-time and runtime:
- Security improvement: Prevents accidental transmission of sensitive information
- Data integrity: Guarantees response format
- Developer experience: Issues can be detected and fixed immediately
This allows us to maximize the benefits of schema-driven development.
Summary
Reasons for Choosing Hono
- TypeScript-native development experience
- Type safety through schema-driven development
- High compatibility with TanStack Query
- Low learning cost
Results
- Achieved type-safe development experience
- Improved development speed
- Highly maintainable codebase
References
Closing
Thank you so much to everyone who attended, the Cloudflare team, yusukebe-san (the creator of Hono), and seike460-san for giving me the opportunity to speak!
The first Cloudflare Workers Tech Talks held in Fukuoka was incredibly stimulating, with presentations from other speakers, and I could feel the enthusiasm of the Cloudflare and Hono communities firsthand.
That’s all from the Gemba, where we continue to practice schema-driven development with Hono.