Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .changeset/quiet-snakes-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
'@hono/zod-openapi': minor
---

This PR adds two new utilities to improve route definition and registration in `@hono/zod-openapi`:

- `defineOpenAPIRoute`: Provides explicit type safety for route definitions
- `openapiRoutes`: Enables batch registration of multiple routes with full type safety

- Registering many routes individually was repetitive and verbose
- Type inference for complex route configurations was challenging
- Organizing routes across multiple files was difficult
- No built-in support for conditional route registration
- RPC type safety was hard to maintain across scattered route registrations

- `defineOpenAPIRoute`: Wraps route definitions with explicit types for better IDE support and type checking
- `openapiRoutes`: Accepts an array of route definitions and registers them all at once
- Supports `addRoute` flag for conditional registration
- Maintains full type safety and RPC support through recursive type merging
- Enables clean modular organization of routes

- ✅ Reduced boilerplate code
- ✅ Better type inference and IDE autocomplete
- ✅ Easier code organization and maintainability
- ✅ Declarative conditional routes
- ✅ Full backward compatibility

See the updated README for usage examples.

- All existing tests pass (102/102)
- Added tests for new functionality
- Verified type inference works correctly

- Updated package README with usage examples
77 changes: 77 additions & 0 deletions packages/zod-openapi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,83 @@ const appRoutes = app.openapi(route, (c) => {
const client = hc<typeof appRoutes>('http://localhost:8787/')
```

### Batch Route Registration

For better code organization and type safety, you can use `defineOpenAPIRoute` and `openapiRoutes` to register multiple routes at once.

#### Using `defineOpenAPIRoute`

`defineOpenAPIRoute` provides explicit type safety for route definitions:

```ts
import { OpenAPIHono, defineOpenAPIRoute, createRoute, z } from '@hono/zod-openapi'

const getUserRoute = defineOpenAPIRoute({
route: createRoute({
method: 'get',
path: '/users/{id}',
request: {
params: z.object({ id: z.string() }),
},
responses: {
200: {
content: {
'application/json': {
schema: z.object({ id: z.string(), name: z.string() }),
},
},
},
},
}),
handler: (c) => {
const { id } = c.req.valid('param')
return c.json({ id, name: 'John Doe' }, 200)
},
})
```

#### Using openapiRoutes for Batch Registration

Register multiple routes at once with full type safety:

```ts
const app = new OpenAPIHono()

// 'as const' is important for type inference
app.openapiRoutes([getUserRoute, createUserRoute, updateUserRoute] as const)
```

#### Conditional Routes

Use the addRoute flag to conditionally include routes:

```ts
const debugRoute = defineOpenAPIRoute({
route: createRoute({
/* ... */
}),
handler: (c) => {
/* ... */
},
addRoute: process.env.NODE_ENV === 'development', // Only in dev
})
```

#### Modular Organization

Organize routes across multiple files:

```ts
// routes/users.ts
export const userRoutes = [getUserRoute, createUserRoute, updateUserRoute] as const

// app.ts
import { userRoutes } from './routes/users'
import { postRoutes } from './routes/posts'

app.openapiRoutes([...userRoutes, ...postRoutes] as const)
```

## Tips

### Type utilities
Expand Down
10 changes: 8 additions & 2 deletions packages/zod-openapi/eslint-suppressions.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,22 @@
},
"src/index.test.ts": {
"@typescript-eslint/no-deprecated": {
"count": 3
"count": 4
},
"@typescript-eslint/no-unsafe-argument": {
"count": 1
},
"@typescript-eslint/no-unsafe-assignment": {
"count": 5
},
"@typescript-eslint/no-unsafe-call": {
"count": 1
},
"@typescript-eslint/no-unsafe-member-access": {
"count": 2
"count": 4
},
"@typescript-eslint/no-unsafe-return": {
"count": 1
},
"@typescript-eslint/require-await": {
"count": 3
Expand Down
Loading
Loading