Skip to content

Hono

Before

ts
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
import { PostService } from '@packages/service'
import { PostDomain } from '@packages/domain'
import { postSchema } from '@packages/schemas'
import { Post } from '@packages/prisma'

export class PostHandler {
  static apply(app: Hono) {
    return app
      .post(
        '/posts',
        zValidator(
          'json',
          postSchema.pick({
            post: true,
          }),
        ),
        async (c) => {
          try {
            const valid = c.req.valid('json')
            const req = valid.post
            await PostService.createPost(req)
            return c.json({ message: 'Created' }, 201)
          } catch (e) {
            console.log(e)
            return c.json({ message: 'Internal Server Error' }, 500)
          }
        },
      )
      .get(
        '/posts',
        zValidator(
          'query',
          z.object({
            page: z.string(),
            rows: z.string(),
          }),
        ),
        async (c) => {
          try {
            const valid = c.req.valid('query')
            const { page, rows } = PostDomain.convertNumberQueryParams(valid)
            if (isNaN(page) || isNaN(rows) || page < 1 || rows < 1) {
              return c.json({ message: 'Bad Request' }, 400)
            }
            const limit = rows
            const offset = (page - 1) * rows
            const res: Post[] = await PostService.getPosts(limit, offset)
            return c.json(res, 200)
          } catch (e) {
            console.log(e)
            return c.json({ message: 'Internal Server Error' }, 500)
          }
        },
      )
      .put(
        '/posts/:id',
        zValidator('param', z.object({ id: z.string().uuid() })),
        zValidator('json', postSchema.pick({ post: true })),
        async (c) => {
          try {
            const param_valid = c.req.valid('param')
            const id = param_valid.id
            const json_valid = c.req.valid('json')
            const { post } = json_valid
            await PostService.putPost(id, post)
            return new Response(null, { status: 204 })
          } catch (e) {
            console.log(e)
            return c.json({ message: 'Internal Server Error' }, 500)
          }
        },
      )
      .delete(
        '/posts/:id',
        zValidator(
          'param',
          z.object({
            id: z.string().uuid(),
          }),
        ),
        async (c) => {
          try {
            const valid = c.req.valid('param')
            const id = valid.id
            await PostService.deletePost(id)
            return new Response(null, { status: 204 })
          } catch (e) {
            console.log(e)
            return c.json({ message: 'Internal Server Error' }, 500)
          }
        },
      )
  }
}

Middleware

ts
import * as dotenv from 'dotenv'
import { logger } from 'hono/logger'
import { serve } from '@hono/node-server'
import { Hono } from 'hono'
import { HonoHandler } from '../handler/hono_handler'
import { PostHandler } from '../handler/post_handler'

dotenv.config()

export class App {
  static init() {
    const app = new Hono()
    const defaultPort = 3001
    const port = process.env.PORT !== undefined ? parseInt(process.env.PORT) : defaultPort
    console.log(`Server is running on http://localhost:${port}`)
    app.use('*', logger())
    app.use('*', async (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)
      }
    })
    serve({
      fetch: app.fetch,
      port,
    })
    return this.applyRoutes(app)
  }

  static applyRoutes(app: Hono) {
    return app.route('/', HonoHandler.apply(app)).route('/', PostHandler.apply(app))
  }
}

After

ts
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
import { PostService } from '@packages/service'
import { PostDomain } from '@packages/domain'
import { postSchema } from '@packages/schemas'
import { Post } from '@packages/prisma'

export class PostHandler {
  static apply(app: Hono) {
    return app
      .post(
        '/posts',
        zValidator(
          'json',
          postSchema.pick({
            post: true,
          }),
        ),
        async (c) => {
          const valid = c.req.valid('json')
          const req = valid.post
          await PostService.createPost(req)
          return c.json({ message: 'Created' }, 201)
        },
      )
      .get(
        '/posts',
        zValidator(
          'query',
          z.object({
            page: z.string(),
            rows: z.string(),
          }),
        ),
        async (c) => {
          const valid = c.req.valid('query')
          const { page, rows } = PostDomain.convertNumberQueryParams(valid)
          if (isNaN(page) || isNaN(rows) || page < 1 || rows < 1) {
            return c.json({ message: 'Bad Request' }, 400)
          }
          const limit = rows
          const offset = (page - 1) * rows
          const res: Post[] = await PostService.getPosts(limit, offset)
          return c.json(res, 200)
        },
      )
      .put(
        '/posts/:id',
        zValidator('param', z.object({ id: z.string().uuid() })),
        zValidator('json', postSchema.pick({ post: true })),
        async (c) => {
          const param_valid = c.req.valid('param')
          const id = param_valid.id
          const json_valid = c.req.valid('json')
          const { post } = json_valid
          await PostService.putPost(id, post)
          return new Response(null, { status: 204 })
        },
      )
      .delete(
        '/posts/:id',
        zValidator(
          'param',
          z.object({
            id: z.string().uuid(),
          }),
        ),
        async (c) => {
          const valid = c.req.valid('param')
          const id = valid.id
          await PostService.deletePost(id)
          return new Response(null, { status: 204 })
        },
      )
  }
}

OpenAPIHono

Before

ts
import { OpenAPIHono } from '@hono/zod-openapi'
import { Post } from '@packages/prisma'
import { PostDomain } from '@packages/domain'
import { PostService } from '@packages/service'
import { postRoutes } from '../openapi'

export class PostHandler {
  static apply(app: OpenAPIHono) {
    return app
      .openapi(postRoutes['createPost'], async (c) => {
        try {
          const valid = c.req.valid('json')
          const req = valid.post
          await PostService.createPost(req)
          return c.json({ message: 'Created' }, 201)
        } catch (e) {
          console.log(e)
          return c.json({ message: 'Internal Server Error' }, 500)
        }
      })
      .openapi(postRoutes['getPostList'], async (c) => {
        try {
          const valid = c.req.valid('query')
          const { page, rows } = PostDomain.convertNumberQueryParams(valid)
          if (isNaN(page) || isNaN(rows) || page < 1 || rows < 1) {
            return c.json({ message: 'Bad Request' }, 400)
          }
          const limit = rows
          const offset = (page - 1) * rows
          const res: Post[] = await PostService.getPosts(limit, offset)
          return c.json(res, 200)
        } catch (e) {
          console.log(e)
          return c.json({ message: 'Internal Server Error' }, 500)
        }
      })
      .openapi(postRoutes['updatePost'], async (c) => {
        try {
          const param_valid = c.req.valid('param')
          const id = param_valid.id
          const json_valid = c.req.valid('json')
          const { post } = json_valid
          await PostService.putPost(id, post)
          return new Response(null, { status: 204 })
        } catch (e) {
          console.log(e)
          return c.json({ message: 'Internal Server Error' }, 500)
        }
      })
      .openapi(postRoutes['deletePost'], async (c) => {
        try {
          const valid = c.req.valid('param')
          const id = valid.id
          await PostService.deletePost(id)
          return new Response(null, { status: 204 })
        } catch (e) {
          console.log(e)
          return c.json({ message: 'Internal Server Error' }, 500)
        }
      })
  }
}

Middleware

ts
import * as dotenv from 'dotenv'
import { OpenAPIHono } from '@hono/zod-openapi'
import { logger } from 'hono/logger'
import { OpenAPIHonoHandler } from '../handler/openapi_hono_handler'
import { PostHandler } from '../handler/post_handler'
import { SwaggerHandler } from '../handler/swagger_handler'
import { serve } from '@hono/node-server'

dotenv.config()

export class App {
  static init() {
    const app = new OpenAPIHono()
    app.use('*', logger())
    app.use('*', (c, next) => {
      console.log(`  ::: ${c.req.method} ${c.req.url}`)
      return next()
    })
    const defaultPort = 3002
    const port = process.env.PORT !== undefined ? parseInt(process.env.PORT) : defaultPort
    console.log(`Server is running on http://localhost:${port}`)
    serve({
      fetch: app.fetch,
      port,
    })
    app.use('*', async (c, next) => {
      try {
        await next()
      } catch (e) {
        return c.json({ error: (e as Error).message }, 500)
      }
    })

    return this.applyRoutes(app)
  }

  static applyRoutes(app: OpenAPIHono) {
    return app
      .route('/', OpenAPIHonoHandler.apply(app))
      .route('/', PostHandler.apply(app))
      .route('/', SwaggerHandler.apply(app))
  }
}

After

ts
import { OpenAPIHono } from '@hono/zod-openapi'
import { Post } from '@packages/prisma'
import { PostDomain } from '@packages/domain'
import { PostService } from '@packages/service'
import { postRoutes } from '../openapi'

export class PostHandler {
  static apply(app: OpenAPIHono) {
    return app
      .openapi(postRoutes['createPost'], async (c) => {
        const valid = c.req.valid('json')
        const req = valid.post
        await PostService.createPost(req)
        return c.json({ message: 'Created' }, 201)
      })
      .openapi(postRoutes['getPostList'], async (c) => {
        const valid = c.req.valid('query')
        const { page, rows } = PostDomain.convertNumberQueryParams(valid)
        if (isNaN(page) || isNaN(rows) || page < 1 || rows < 1) {
          return c.json({ message: 'Bad Request' }, 400)
        }
        const limit = rows
        const offset = (page - 1) * rows
        const res: Post[] = await PostService.getPosts(limit, offset)
        return c.json(res, 200)
      })
      .openapi(postRoutes['updatePost'], async (c) => {
        const param_valid = c.req.valid('param')
        const id = param_valid.id
        const json_valid = c.req.valid('json')
        const { post } = json_valid
        await PostService.putPost(id, post)
        return new Response(null, { status: 204 })
      })
      .openapi(postRoutes['deletePost'], async (c) => {
        const valid = c.req.valid('param')
        const id = valid.id
        await PostService.deletePost(id)
        return new Response(null, { status: 204 })
      })
  }
}