Node.js + fastify + TypeScript + SwaggerでRESTサーバを構築する


概要

Node.js、fastify、TypeScript、Swaggerで簡単なサンプルを作ってみたので共有します。
サンプルコード: https://github.com/swmokyun/sample-fastify-typescript-swagger

目的

  • とにかくラクにRESTサーバを作りたい
  • pythonでもいいけどフロントエンドでNext.jsとかつかうからNode.jsで言語を統一したい
  • TypeScriptで型の恩恵を受けたい
  • SwaggerでRESTスキーマまわりのあれこれをラクしたい

expressよりfastifyがいいらしい

Node.jsでサーバといえばexpressと思ってたけど、最近ではKoa.jsとかfastifyが有力候補らしい。fastifyの方がtypescriptまわりがいい感じにサポートされているっぽいのでこっちをチョイス。

fastify-swaggerがいい感じ

調べた感じではfastify-swaggerを使うとtypescriptで型補完しながらスキーマ定義が記述できるっぽい。これは素晴らしい。
https://github.com/fastify/fastify-swagger

typeboxでさらにいい感じ

typeboxを使うとfastifyでスキーマ定義と関数の型を共通化できる。これはさらに素晴らしい。
https://www.fastify.io/docs/latest/TypeScript/#json-schema

サンプル

公式を参考にGETやqueryのサンプルを追加したのが以下になります。
また、各モジュールのバージョンは以下になっています。fastifyはバージョンがあがると結構APIが変わるっぽいので注意。

"@sinclair/typebox": "^0.23.1",
"fastify": "^3.24.1",
"fastify-swagger": "^4.12.6"
import { Static, Type } from "@sinclair/typebox"
import fastify, { FastifyInstance } from "fastify"
import fastifySwagger from "fastify-swagger"

const PORT = 9000

const server: FastifyInstance = fastify({ logger: true })

/**
 * swagger
 */
server.register(fastifySwagger, {
  routePrefix: "/docs",
  swagger: {
    info: {
      title: "Test swagger",
      description: "Testing the Fastify swagger API",
      version: "0.1.0",
    },
  },
  exposeRoute: true,
})

/**
 * test_post
 * https://www.fastify.io/docs/latest/TypeScript/#typebox
 */
const User = Type.Object({
  name: Type.String(),
  mail: Type.Optional(Type.String({ format: "email" })),
})
type UserType = Static<typeof User>

server.post<{ Body: UserType; Reply: UserType }>(
  "/test_post",
  {
    schema: {
      body: User,
      response: {
        200: User,
      },
    },
  },
  (req, rep) => {
    const { body: user } = req
    rep.status(200).send(user)
  }
)

/**
 * test get, querystring
 */
const ErrorResponse = Type.Object({
  msg: Type.String(),
})
type ErrorResponseType = Static<typeof ErrorResponse>

server.get<{ Querystring: UserType; Reply: UserType | ErrorResponseType }>(
  "/test_get",
  {
    schema: {
      querystring: User,
      response: {
        200: User,
        400: ErrorResponse,
      },
    },
  },
  (req, rep) => {
    const { query: user } = req
    if (user.name.length < 3) {
      rep.status(400).send({ msg: "name is too short" })
    } else {
      rep.status(200).send(user)
    }
  }
)

server.listen(PORT)

http://localhost:9000/docs 
でSwaggerからAPIの確認やJSONスキーマの出力が可能。とっても便利で開発が捗ります。