Hono Takibi 0.50
Hono Takibi 0.5.0
npm
GitHub
Hono Takibi 0.5.0の変更点
0.4.*では、以下のように設定できましたが、誤ってapp
オプションを指定して、生成されるコードに影響を及ぼすことを考え、0.5.0以降ではapp
オプションを削除しました。
{
"schema": {
"name": "PascalCase",
"export": false
},
"type": {
"name": "PascalCase",
"export": false
},
"app": {
"output": true,
"test": true,
"basePath": "api",
"env": "process.env.NODE_ENV"
}
}
0.5.0以降では、app
オプションを削除したため、以下のようにオプションを指定して、コマンドで実行をお願いします。
Options
Option | Description |
---|---|
-template | アプリケーションの雛形の生成を有効にする。 |
-test | テストファイルを生成する。 |
--base-path | APIのエンドポイントを指定する。 |
--env | 開発モードを決定するために使用される環境変数を指定する。(デフォルト process.env.NODE_ENV ) |
⚠️
-template
オプションを使用する場合、有効なディレクトリパスを指定する必要があります。コマンドを実行する前に、そのディレクトリが存在することを確認してください。
Example
npx hono-takibi openapi.yaml -o project/routes.ts -template -test --basePath 'api' --env 'process.env.NODE_ENV'
使用例
openapi.yaml
を用意します。
.
└── openapi.yaml
openapi.yaml
openapi: 3.1.0
info:
title: Hono API
version: v1
components:
schemas:
Error:
type: object
properties:
message:
type: string
required:
- message
Post:
type: object
properties:
id:
type: string
format: uuid
description: Unique identifier of the post
post:
type: string
description: Content of the post
minLength: 1
maxLength: 140
createdAt:
type: string
format: date-time
description: Timestamp when the post was created
updatedAt:
type: string
format: date-time
description: Timestamp when the post was last updated
required:
- id
- post
- createdAt
- updatedAt
tags:
- name: Hono
description: Endpoints related to general Hono operations
- name: Post
description: Endpoints for creating, retrieving, updating, and deleting posts
paths:
/:
get:
tags:
- Hono
summary: Welcome message
description: Retrieve a simple welcome message from the Hono API.
responses:
'200':
description: Successful response with a welcome message.
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Hono🔥
required:
- message
/posts:
post:
tags:
- Post
summary: Create a new post
description: Submit a new post with a maximum length of 140 characters.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
post:
type: string
description: Content of the post
minLength: 1
maxLength: 140
required:
- post
example:
post: "This is my first post!"
responses:
'201':
description: Post successfully created.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
message: Created
'400':
description: Invalid request due to bad input.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
message: Post content is required and must be between 1 and 140 characters.
'500':
description: Internal server error.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
message: An unexpected error occurred. Please try again later.
get:
tags:
- Post
summary: Retrieve a list of posts
description: Retrieve a paginated list of posts. Specify the page number and the number of posts per page.
parameters:
- in: query
name: page
required: true
schema:
type: integer
minimum: 0
default: 1
example: 1
description: The page number to retrieve. Must be a positive integer. Defaults to 1.
- in: query
name: rows
required: true
schema:
type: integer
minimum: 0
default: 10
example: 10
description: The number of posts per page. Must be a positive integer. Defaults to 10.
responses:
'200':
description: Successfully retrieved a list of posts.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Post'
example:
- id: "123e4567-e89b-12d3-a456-426614174000"
post: "Hello world!"
createdAt: "2024-12-01T12:34:56Z"
updatedAt: "2024-12-02T14:20:00Z"
'400':
description: Invalid request due to bad input.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
message: Invalid page or rows parameter. Both must be positive integers.
'500':
description: Internal server error.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
message: An unexpected error occurred. Please try again later.
/posts/{id}:
put:
tags:
- Post
summary: Update an existing post
description: Update the content of an existing post identified by its unique ID.
parameters:
- in: path
name: id
required: true
schema:
type: string
format: uuid
description: Unique identifier of the post.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
post:
type: string
description: Updated content for the post
minLength: 1
maxLength: 140
required:
- post
example:
post: "Updated post content."
responses:
'204':
description: Post successfully updated.
'400':
description: Invalid input.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
message: Post content is required and must be between 1 and 140 characters.
'500':
description: Internal server error.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
message: An unexpected error occurred. Please try again later.
delete:
tags:
- Post
summary: Delete a post
description: Delete an existing post identified by its unique ID.
parameters:
- in: path
name: id
required: true
schema:
type: string
format: uuid
example: 123e4567-e89b-12d3-a456-426614174000
description: Unique identifier of the post.
responses:
'204':
description: Post successfully deleted.
'400':
description: Invalid input.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
message: Invalid post ID.
'500':
description: Internal server error.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
message: An unexpected error occurred. Please try again later.
オプションを指定して、コマンドを実行します。
pnpm hono-takibi openapi.yaml -o src/routes.ts -template --base-path = "api"
コマンドを実行すると、以下のようにファイルが生成されます。
.
├── src
│ ├── handler
│ │ ├── index_handler.ts
│ │ └── posts_handler.ts
│ ├── index.ts
│ └── routes.ts
├── openapi.yaml
├── package.json
└── pnpm-lock.yaml
生成されたファイルを確認します。
ルート定義が生成されます。詳細は省略します。
src/routes.ts
import { createRoute, z } from '@hono/zod-openapi'
const ErrorSchema = z.object({ message: z.string() }).openapi('Error')
const PostSchema = z
.object({
id: z.string().uuid(),
post: z.string().min(1).max(140),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
})
.openapi('Post')
export const getRoute = createRoute({
tags: ['Hono'],
method: 'get',
path: '/',
summary: 'Welcome message',
description: 'Retrieve a simple welcome message from the Hono API.',
responses: {
200: {
description: 'Successful response with a welcome message.',
content: {
'application/json': {
schema: z.object({ message: z.string().openapi({ example: 'Hono🔥' }) }),
},
},
},
},
})
export const postPostsRoute = createRoute({
tags: ['Post'],
method: 'post',
path: '/posts',
summary: 'Create a new post',
description: 'Submit a new post with a maximum length of 140 characters.',
request: {
body: {
required: true,
content: { 'application/json': { schema: z.object({ post: z.string().min(1).max(140) }) } },
},
},
responses: {
201: {
description: 'Post successfully created.',
content: { 'application/json': { schema: ErrorSchema } },
},
400: {
description: 'Invalid request due to bad input.',
content: { 'application/json': { schema: ErrorSchema } },
},
500: {
description: 'Internal server error.',
content: { 'application/json': { schema: ErrorSchema } },
},
},
})
export const getPostsRoute = createRoute({
tags: ['Post'],
method: 'get',
path: '/posts',
summary: 'Retrieve a list of posts',
description:
'Retrieve a paginated list of posts. Specify the page number and the number of posts per page.',
request: {
query: z.object({
page: z.string().pipe(z.coerce.number().int().min(0).default(1).openapi({ example: 1 })),
rows: z.string().pipe(z.coerce.number().int().min(0).default(10).openapi({ example: 10 })),
}),
},
responses: {
200: {
description: 'Successfully retrieved a list of posts.',
content: { 'application/json': { schema: z.array(PostSchema) } },
},
400: {
description: 'Invalid request due to bad input.',
content: { 'application/json': { schema: ErrorSchema } },
},
500: {
description: 'Internal server error.',
content: { 'application/json': { schema: ErrorSchema } },
},
},
})
export const putPostsIdRoute = createRoute({
tags: ['Post'],
method: 'put',
path: '/posts/{id}',
summary: 'Update an existing post',
description: 'Update the content of an existing post identified by its unique ID.',
request: {
body: {
required: true,
content: { 'application/json': { schema: z.object({ post: z.string().min(1).max(140) }) } },
},
params: z.object({ id: z.string().uuid() }),
},
responses: {
204: { description: 'Post successfully updated.' },
400: {
description: 'Invalid input.',
content: { 'application/json': { schema: ErrorSchema } },
},
500: {
description: 'Internal server error.',
content: { 'application/json': { schema: ErrorSchema } },
},
},
})
export const deletePostsIdRoute = createRoute({
tags: ['Post'],
method: 'delete',
path: '/posts/{id}',
summary: 'Delete a post',
description: 'Delete an existing post identified by its unique ID.',
request: {
params: z.object({
id: z
.string()
.uuid()
.openapi({
param: { name: 'id', in: 'path' },
example: '123e4567-e89b-12d3-a456-426614174000',
}),
}),
},
responses: {
204: { description: 'Post successfully deleted.' },
400: {
description: 'Invalid input.',
content: { 'application/json': { schema: ErrorSchema } },
},
500: {
description: 'Internal server error.',
content: { 'application/json': { schema: ErrorSchema } },
},
},
})
src/index.ts
を確認します。自動生成されたものです。ミドルウェアなどは、必要に応じて設定する必要があります。
import { OpenAPIHono } from '@hono/zod-openapi'
import { swaggerUI } from '@hono/swagger-ui'
import {
getRoute,
postPostsRoute,
getPostsRoute,
putPostsIdRoute,
deletePostsIdRoute,
} from './route.ts'
import { getRouteHandler } from './handler/index_handler.ts'
import {
postPostsRouteHandler,
getPostsRouteHandler,
putPostsIdRouteHandler,
deletePostsIdRouteHandler,
} from './handler/posts_handler.ts'
const app = new OpenAPIHono().basePath('api')
export const api = app
.openapi(getRoute, getRouteHandler)
.openapi(postPostsRoute, postPostsRouteHandler)
.openapi(getPostsRoute, getPostsRouteHandler)
.openapi(putPostsIdRoute, putPostsIdRouteHandler)
.openapi(deletePostsIdRoute, deletePostsIdRouteHandler)
const isDev = process.env.NODE_ENV === 'development'
if (isDev) {
app
.doc('/doc', {
openapi: '3.1.0',
info: { title: 'Hono API', version: 'v1' },
tags: [
{ name: 'Hono', description: 'Endpoints related to general Hono operations' },
{
name: 'Post',
description: 'Endpoints for creating, retrieving, updating, and deleting posts',
},
],
})
.get('/ui', swaggerUI({ url: '/api/doc' }))
}
export type AddType = typeof api
export default app
src/handler
ディレクトリに、各ルートのハンドラーが生成されます。ビジネスロジックは、開発者が実装する必要があります。
src/handler/index_handler.ts
import type { RouteHandler } from '@hono/zod-openapi'
import type { getRoute } from '../route.ts'
export const getRouteHandler: RouteHandler<typeof getRoute> = async (c) => {}
src/handler/posts_handler.ts
import type { RouteHandler } from '@hono/zod-openapi'
import type {
postPostsRoute,
getPostsRoute,
putPostsIdRoute,
deletePostsIdRoute,
} from '../route.ts'
export const postPostsRouteHandler: RouteHandler<typeof postPostsRoute> = async (c) => {}
export const getPostsRouteHandler: RouteHandler<typeof getPostsRoute> = async (c) => {}
export const putPostsIdRouteHandler: RouteHandler<typeof putPostsIdRoute> = async (c) => {}
export const deletePostsIdRouteHandler: RouteHandler<typeof deletePostsIdRoute> = async (c) => {}
オプションを指定して、雛形が生成されました。次にロジックを書いていきます。
ミドルウェアやポート番号を設定していきます。
src/index.ts
import { OpenAPIHono } from '@hono/zod-openapi'
import { swaggerUI } from '@hono/swagger-ui'
import {
getRoute,
postPostsRoute,
getPostsRoute,
putPostsIdRoute,
deletePostsIdRoute,
} from './route.ts'
import { getRouteHandler } from './handler/index_handler.ts'
import {
postPostsRouteHandler,
getPostsRouteHandler,
putPostsIdRouteHandler,
deletePostsIdRouteHandler,
} from './handler/posts_handler.ts'
import { logger } from 'hono/logger'
import { serve } from '@hono/node-server'
const app = new OpenAPIHono().basePath('api')
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)
}
})
export const api = app
.openapi(getRoute, getRouteHandler)
.openapi(postPostsRoute, postPostsRouteHandler)
.openapi(getPostsRoute, getPostsRouteHandler)
.openapi(putPostsIdRoute, putPostsIdRouteHandler)
.openapi(deletePostsIdRoute, deletePostsIdRouteHandler)
const isDev = process.env.NODE_ENV === 'development'
if (isDev) {
app
.doc('/doc', {
openapi: '3.1.0',
info: { title: 'Hono API', version: 'v1' },
tags: [
{ name: 'Hono', description: 'Endpoints related to general Hono operations' },
{
name: 'Post',
description: 'Endpoints for creating, retrieving, updating, and deleting posts',
},
],
})
.get('/ui', swaggerUI({ url: '/api/doc' }))
}
export type AddType = typeof api
const port = 3000
console.log(`Server is running on http://localhost:${port}`)
serve({
fetch: app.fetch,
port,
})
続いて、handlerを実装していきます。
src/handler/index_handler.ts
import type { RouteHandler } from '@hono/zod-openapi'
import type { getRoute } from '../route.ts'
export const getRouteHandler: RouteHandler<typeof getRoute> = async (c) => {
return c.json({ message: 'Hono🔥 Drizzle' })
}
src/handler/posts_handler.ts
import type { RouteHandler } from '@hono/zod-openapi'
import type {
postPostsRoute,
getPostsRoute,
putPostsIdRoute,
deletePostsIdRoute,
} from '../route.ts'
import db from '../../db'
import { Post } from '../../db/schema'
import { eq, desc } from 'drizzle-orm'
export const postPostsRouteHandler: RouteHandler<typeof postPostsRoute> = async (c) => {
const { post } = c.req.valid('json')
await db.insert(Post).values({ post })
return c.json({ message: 'Created' }, 201)
}
export const getPostsRouteHandler: RouteHandler<typeof getPostsRoute> = async (c) => {
const { page, rows } = c.req.valid('query')
const limit = rows
const offset = (page - 1) * rows
const posts = await db
.select()
.from(Post)
.orderBy(desc(Post.createdAt))
.limit(limit)
.offset(offset)
return c.json(posts, 200)
}
export const putPostsIdRouteHandler: RouteHandler<typeof putPostsIdRoute> = async (c) => {
const { id } = c.req.valid('param')
const { post } = c.req.valid('json')
await db.update(Post).set({ post }).where(eq(Post.id, id))
return new Response(null, { status: 204 })
}
export const deletePostsIdRouteHandler: RouteHandler<typeof deletePostsIdRoute> = async (c) => {
const { id } = c.req.valid('param')
await db.delete(Post).where(eq(Post.id, id))
return new Response(null, { status: 204 })
}
Swagger UIを確認します。
OpenAPI
定義から、雛形を作成させ、ビジネスロジックの実装に集中できるようになったと思います。
OpenAPI
定義を生成させ、雛形を生成する
OpenAIから、プロンプト
あなたは「REST APIのOpenAPI仕様」を生成する専門家です。以下の要件をすべて満たすOpenAPIドキュメントを YAML形式 で作成してください。なお、OpenAPIのバージョンは3.0以上 とし、誤りや不足がないよう注意深く出力してください。追加条件で、複雑なスキーマを定義してください。
OpenAIから、OpenAPI
定義を生成させ、雛形を生成します。
openapi.yaml
openapi: "3.0.3"
info:
title: "Sample Complex REST API"
version: "1.0.0"
description: >
このAPIは、複雑なスキーマ定義(入れ子になったオブジェクト、配列、条件付き型など)を含むサンプルのREST APIです。
servers:
- url: "https://api.example.com/v1"
paths:
/items:
get:
summary: "アイテムの一覧を取得"
operationId: "getItems"
responses:
'200':
description: "アイテムの配列を返却"
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Item"
post:
summary: "新しいアイテムを作成"
operationId: "createItem"
requestBody:
description: "作成するアイテムの情報"
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/Item"
responses:
'201':
description: "アイテム作成成功"
content:
application/json:
schema:
$ref: "#/components/schemas/Item"
/items/{itemId}:
parameters:
- $ref: "#/components/parameters/itemId"
get:
summary: "指定したIDのアイテムを取得"
operationId: "getItemById"
responses:
'200':
description: "アイテムの詳細情報を返却"
content:
application/json:
schema:
$ref: "#/components/schemas/Item"
'404':
description: "アイテムが存在しない"
put:
summary: "指定したIDのアイテムを更新"
operationId: "updateItem"
requestBody:
description: "更新するアイテムの情報"
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/ItemUpdate"
responses:
'200':
description: "アイテム更新成功"
content:
application/json:
schema:
$ref: "#/components/schemas/Item"
'404':
description: "アイテムが存在しない"
delete:
summary: "指定したIDのアイテムを削除"
operationId: "deleteItem"
responses:
'204':
description: "アイテム削除成功(内容なし)"
'404':
description: "アイテムが存在しない"
components:
schemas:
Item:
type: object
required:
- id
- name
- details
properties:
id:
type: string
description: "ユニークなアイテムID(UUID形式など)"
example: "123e4567-e89b-12d3-a456-426614174000"
name:
type: string
description: "アイテム名"
example: "サンプルアイテム"
description:
type: string
description: "アイテムの詳細な説明"
example: "このアイテムはサンプルとして使用されます。"
details:
type: object
description: "アイテムに関する詳細情報"
required:
- createdAt
- updatedAt
properties:
createdAt:
type: string
format: date-time
description: "作成日時"
example: "2021-07-21T17:32:28Z"
updatedAt:
type: string
format: date-time
description: "更新日時"
example: "2021-07-22T11:15:45Z"
attributes:
type: array
description: "各種属性情報の配列"
minItems: 1
maxItems: 10
items:
$ref: "#/components/schemas/Attribute"
tags:
type: array
description: "アイテムに紐付くタグの一覧"
minItems: 1
maxItems: 5
items:
type: string
example: ["new", "sale"]
metadata:
type: object
description: "追加のメタデータ情報"
properties:
attributeGroups:
type: array
description: "グループごとに分類された属性情報"
minItems: 1
maxItems: 3
items:
type: array
description: "属性のグループ"
minItems: 1
maxItems: 5
items:
$ref: "#/components/schemas/Attribute"
history:
type: array
description: "アイテムの更新履歴"
items:
type: object
required:
- updatedAt
- changes
properties:
updatedAt:
type: string
format: date-time
description: "更新日時"
changes:
type: array
description: "変更されたフィールドの一覧"
items:
type: string
ItemUpdate:
type: object
description: "アイテム更新時に使用するスキーマ。部分更新可能なフィールドを定義。"
properties:
name:
type: string
description: "更新後のアイテム名"
example: "更新されたアイテム名"
description:
type: string
description: "更新後の詳細説明"
example: "更新された説明文。"
details:
type: object
description: "更新対象の詳細情報"
properties:
updatedAt:
type: string
format: date-time
description: "更新日時"
example: "2021-07-22T11:15:45Z"
attributes:
type: array
description: "更新対象の属性情報配列"
minItems: 1
maxItems: 10
items:
$ref: "#/components/schemas/Attribute"
additionalProperties: false
tags:
type: array
description: "更新後のタグ配列"
minItems: 1
maxItems: 5
items:
type: string
metadata:
type: object
description: "更新対象の追加メタデータ情報"
properties:
attributeGroups:
type: array
description: "グループごとに分類された属性情報"
minItems: 1
maxItems: 3
items:
type: array
description: "属性のグループ"
minItems: 1
maxItems: 5
items:
$ref: "#/components/schemas/Attribute"
history:
type: array
description: "更新履歴の追加情報"
items:
type: object
required:
- updatedAt
- changes
properties:
updatedAt:
type: string
format: date-time
description: "更新日時"
changes:
type: array
description: "変更されたフィールドの一覧"
items:
type: string
additionalProperties: false
Attribute:
type: object
description: "アイテムの属性情報。値は文字列、数値、または真偽値を取る可能性がある。"
required:
- key
- value
properties:
key:
type: string
description: "属性のキー"
example: "color"
value:
description: "属性の値。型は文字列、数値、または真偽値のいずれか。"
oneOf:
- type: string
example: "red"
- type: number
example: 10
- type: boolean
example: true
additionalProperties: false
parameters:
itemId:
name: itemId
in: path
description: "対象のアイテムID"
required: true
schema:
type: string
route.ts
import { createRoute, z } from '@hono/zod-openapi'
const AttributeSchema = z
.object({
key: z.string().openapi({ example: 'color' }),
value: z.union([
z.string().openapi({ example: 'red' }),
z.number().openapi({ example: 10 }),
z.boolean(),
]),
})
.openapi('Attribute')
const ItemSchema = z
.object({
id: z.string().openapi({ example: '123e4567-e89b-12d3-a456-426614174000' }),
name: z.string().openapi({ example: 'サンプルアイテム' }),
description: z
.string()
.openapi({ example: 'このアイテムはサンプルとして使用されます。' })
.optional(),
details: z.object({
createdAt: z.string().datetime().openapi({ example: '2021-07-21T17:32:28Z' }),
updatedAt: z.string().datetime().openapi({ example: '2021-07-22T11:15:45Z' }),
attributes: z.array(AttributeSchema).optional(),
}),
tags: z.array(z.string()).min(1).max(5).optional(),
metadata: z
.object({
attributeGroups: z.array(z.array(AttributeSchema).min(1).max(5)).min(1).max(3),
history: z.array(
z.object({ updatedAt: z.string().datetime(), changes: z.array(z.string()) }),
),
})
.partial()
.optional(),
})
.openapi('Item')
const ItemUpdateSchema = z
.object({
name: z.string().openapi({ example: '更新されたアイテム名' }),
description: z.string().openapi({ example: '更新された説明文。' }),
details: z
.object({
updatedAt: z.string().datetime().openapi({ example: '2021-07-22T11:15:45Z' }),
attributes: z.array(AttributeSchema),
})
.partial(),
tags: z.array(z.string()).min(1).max(5),
metadata: z
.object({
attributeGroups: z.array(z.array(AttributeSchema).min(1).max(5)).min(1).max(3),
history: z.array(
z.object({ updatedAt: z.string().datetime(), changes: z.array(z.string()) }),
),
})
.partial(),
})
.partial()
.openapi('ItemUpdate')
export const getItemsRoute = createRoute({
tags: [],
method: 'get',
path: '/items',
summary: 'アイテムの一覧を取得',
responses: {
200: {
description: 'アイテムの配列を返却',
content: { 'application/json': { schema: z.array(ItemSchema) } },
},
},
})
export const postItemsRoute = createRoute({
tags: [],
method: 'post',
path: '/items',
summary: '新しいアイテムを作成',
request: { body: { required: true, content: { 'application/json': { schema: ItemSchema } } } },
responses: {
201: {
description: 'アイテム作成成功',
content: { 'application/json': { schema: ItemSchema } },
},
},
})
export const getItemsItemIdRoute = createRoute({
tags: [],
method: 'get',
path: '/items/{itemId}',
summary: '指定したIDのアイテムを取得',
responses: {
200: {
description: 'アイテムの詳細情報を返却',
content: { 'application/json': { schema: ItemSchema } },
},
404: { description: 'アイテムが存在しない' },
},
})
export const putItemsItemIdRoute = createRoute({
tags: [],
method: 'put',
path: '/items/{itemId}',
summary: '指定したIDのアイテムを更新',
request: {
body: { required: true, content: { 'application/json': { schema: ItemUpdateSchema } } },
},
responses: {
200: {
description: 'アイテム更新成功',
content: { 'application/json': { schema: ItemSchema } },
},
404: { description: 'アイテムが存在しない' },
},
})
export const deleteItemsItemIdRoute = createRoute({
tags: [],
method: 'delete',
path: '/items/{itemId}',
summary: '指定したIDのアイテムを削除',
responses: {
204: { description: 'アイテム削除成功(内容なし)' },
404: { description: 'アイテムが存在しない' },
},
})
src/index.ts
import { OpenAPIHono } from '@hono/zod-openapi'
import { swaggerUI } from '@hono/swagger-ui'
import {
getItemsRoute,
postItemsRoute,
parametersItemsItemIdRoute,
getItemsItemIdRoute,
putItemsItemIdRoute,
deleteItemsItemIdRoute,
} from './routes.ts'
import {
getItemsRouteHandler,
postItemsRouteHandler,
parametersItemsItemIdRouteHandler,
getItemsItemIdRouteHandler,
putItemsItemIdRouteHandler,
deleteItemsItemIdRouteHandler,
} from './handler/items_handler.ts'
const app = new OpenAPIHono().basePath('api')
export const api = app
.openapi(getItemsRoute, getItemsRouteHandler)
.openapi(postItemsRoute, postItemsRouteHandler)
.openapi(parametersItemsItemIdRoute, parametersItemsItemIdRouteHandler)
.openapi(getItemsItemIdRoute, getItemsItemIdRouteHandler)
.openapi(putItemsItemIdRoute, putItemsItemIdRouteHandler)
.openapi(deleteItemsItemIdRoute, deleteItemsItemIdRouteHandler)
const isDev = process.env.NODE_ENV === 'development'
if (isDev) {
app
.doc('/doc', {
openapi: '3.0.3',
info: {
title: 'Sample Complex REST API',
version: '1.0.0',
description:
'このAPIは、複雑なスキーマ定義(入れ子になったオブジェクト、配列、条件付き型など)を含むサンプルのREST APIです。\n',
},
servers: [{ url: 'https://api.example.com/v1' }],
})
.get('/ui', swaggerUI({ url: '/api/doc' }))
}
export type AddType = typeof api
export default app
その他、handlerなども生成されますが、省略します。
その他
-
今後、これ以上機能追加の予定はありません。
-
hono-takibi.json
の設定の仕様を、頻繁に変えてきましたが、0.5.0以降変更するつもりはありません。 -
オプションはより使いやすくなるように、変更する可能性はあります。
おわりに
0.5.0
以降では、オプションでOpenAPI
定義から、雛形を生成できるようにしました。また、zod
スキーマの生成も、0.4.0
から、対応箇所が増えました。(.length
...など)
今後も、メンテナンスを継続し、精度が高いライブラリを目指します。
また、Hono Takibi
を試みた記事やブログをお待ちしております。
Discussion