🔥
Hono RPC OpenAPI🔥
Generate RPC code from OpenAPI definition
OpenAPIの定義から、HonoのRPCで、使用できるコードを自動生成させてみる。
Demo
OpenAPI YAML
openapi.yaml
info:
title: Hono API
version: v1
openapi: 3.1.0
tags:
- name: Hono
description: Hono API
- name: Post
description: Post API
components:
schemas: {}
parameters: {}
paths:
/:
get:
tags:
- Hono
responses:
'200':
description: Hono🔥
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Hono🔥
required:
- message
/posts:
post:
tags:
- Post
description: create a new post
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
post:
type: string
minLength: 1
maxLength: 140
required:
- post
responses:
'201':
description: Created
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Created
required:
- message
'400':
description: Bad Request
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Bad Request
required:
- message
'500':
description: Internal Server Error
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Internal Server Error
required:
- message
get:
tags:
- Post
description: get PostList posts with optional pagination
parameters:
- schema:
type: string
required: true
name: page
in: query
- schema:
type: string
required: true
name: rows
in: query
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
type: object
properties:
id:
type: string
format: uuid
post:
type: string
minLength: 1
maxLength: 140
createdAt:
type: string
updatedAt:
type: string
required:
- id
- post
- createdAt
- updatedAt
'400':
description: Bad Request
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Bad Request
required:
- message
'500':
description: Internal Server Error
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Internal Server Error
required:
- message
/posts/{id}:
put:
tags:
- Post
description: update Post
parameters:
- schema:
type: string
format: uuid
required: true
name: id
in: path
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
post:
type: string
minLength: 1
maxLength: 140
required:
- post
responses:
'204':
description: No Content
'400':
description: Bad Request
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Bad Request
required:
- message
'500':
description: Internal Server Error
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Internal Server Error
required:
- message
delete:
tags:
- Post
description: delete post
parameters:
- schema:
type: string
format: uuid
required: true
name: id
in: path
responses:
'204':
description: No Content
'400':
description: Bad Request
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Bad Request
required:
- message
'500':
description: Internal Server Error
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Internal Server Error
required:
- message
Features
Sample
REST API
を作成する。
App
import { OpenAPIHono } from '@hono/zod-openapi'
import { swaggerUI } from '@hono/swagger-ui'
import { logger } from 'hono/logger'
import { serve } from '@hono/node-server'
import { getRoute, postPostsRoute, getPostsRoute, putPostsIdRoute, deletePostsIdRoute } from '@packages/hono-rpc'
import { getHandler } from '../handler/openapi_hono_handler'
import { postPostsHandler, getPostsHandler, putPostsIdHandler, deletePostsIdHandler } from '../handler/posts_handler'
export class App {
static init() {
const app = new OpenAPIHono()
const port = 3000
console.log(`Server is running on http://localhost:${port}`)
serve({
fetch: app.fetch,
port,
})
app.use('*', logger())
app.use('*', (c, next) => {
console.log(` ::: ${c.req.method} ${c.req.url}`)
return next()
})
app.use('*', async (c, next) => {
try {
await next()
} catch (e) {
return c.json({ error: (e as Error).message }, 500)
}
})
app
.doc('/doc', {
info: {
title: 'Hono API',
version: 'v1',
},
openapi: '3.1.0',
tags: [
{
name: 'Hono',
description: 'Hono API',
},
{
name: 'Post',
description: 'Post API',
},
],
})
.get('/ui', swaggerUI({ url: '/doc' }))
return this.applyRoutes(app)
}
static applyRoutes(app: OpenAPIHono) {
return app
.openapi(getRoute, getHandler)
.openapi(postPostsRoute, postPostsHandler)
.openapi(getPostsRoute, getPostsHandler)
.openapi(putPostsIdRoute, putPostsIdHandler)
.openapi(deletePostsIdRoute, deletePostsIdHandler)
}
}
Handler
import { type RouteHandler } from '@hono/zod-openapi'
import { postPostsRoute, getPostsRoute, putPostsIdRoute, deletePostsIdRoute } from '@packages/hono-rpc'
import { Post } from '@packages/prisma'
import { PostsService } from '@packages/service'
export const postPostsHandler: RouteHandler<typeof postPostsRoute> = async (c) => {
const { post } = c.req.valid('json')
await PostsService.postPosts(post)
return c.json({ message: 'Created' }, 201)
}
export const getPostsHandler: RouteHandler<typeof getPostsRoute> = async (c) => {
const { page, rows } = c.req.valid('query')
const pageNumber = parseInt(page)
const rowsPerPage = parseInt(rows)
if (isNaN(pageNumber) || isNaN(rowsPerPage) || pageNumber < 1 || rowsPerPage < 1) {
return c.json({ message: 'Bad Request' }, 400)
}
const limit = rowsPerPage
const offset = (pageNumber - 1) * rowsPerPage
const posts: Post[] = await PostsService.getPosts(limit, offset)
return c.json(posts, 200)
}
export const putPostsIdHandler: RouteHandler<typeof putPostsIdRoute> = async (c) => {
const { id } = c.req.valid('param')
const { post } = c.req.valid('json')
await PostsService.putPostsId(id, post)
return new Response(null, { status: 204 })
}
export const deletePostsIdHandler: RouteHandler<typeof deletePostsIdRoute> = async (c) => {
const { id } = c.req.valid('param')
await PostsService.deletePostsId(id)
return new Response(null, { status: 204 })
}
The end
OpenAPIの定義から、Zod OpenAPIを自動生成し、開発を楽にできるようにしたい。🔥
Discussion