Node + Fastify + OpenAPI + Statistics

Overview

Requirement

I aim to develop a [Node].js(https://nodejs.org/)-based FastAPI that is OpenAPI compatible (formerly known as Swagger). To accomplish this, I will employ RepiDoc for generating OpenAPI documents and keep track of the API's execution status using swagger-stats.

Code

  1. This is package.json

     1{
     2    "name": "fastify-example",
     3    "version": "1.0.0",
     4    "description": "A simple Fastify example with OpenAPI",
     5    "type": "commonjs",
     6    "scripts": {
     7        "start": "node app.js"
     8    },
     9    "dependencies": {
    10        "@fastify/express": "^2.3.0",
    11        "@fastify/static": "^6.11.2",
    12        "@fastify/swagger": "^8.10.1",
    13        "@fastify/swagger-ui": "^1.9.3",
    14        "fastify": "latest",
    15        "swagger-stats": "^0.99.7"
    16    }
    17}
    
  2. This is app.js

  • If you want to use Swagger UI, you can remark line 40-53 and line 55-69

      1'use strict'
      2
      3const fastify = require('fastify')({
      4logger: true
      5});
      6
      7// Static plugin to render HTML pages
      8const path = require('node:path')
      9fastify.register(require('@fastify/static'), {
     10root: path.join(__dirname, '/public'),
     11prefix: '/public/', // optional: default '/'
     12// http://localhost:3000/public/index.html
     13// constraints: { host: 'example.com' } // optional: default {}
     14})
     15
     16// Swagger Stats
     17const swStats  = require('swagger-stats');
     18const apiSpec = require('./swagger.json');
     19
     20// Register Swagger
     21const registerSwagger = async () => {
     22try {
     23    await fastify.register(require('@fastify/swagger'), {
     24    // openapi options
     25    openapi: {
     26        openapi: "3.1.0",
     27        info: {
     28        title: 'test openapi',
     29        description: 'this is a test',
     30        version: '1.0.1',
     31        },
     32        // externalDocs: Object,
     33        // servers: [ Object ],
     34        // components: Object,
     35        // security: [ Object ],
     36        // tags: [ Object ]
     37    },
     38
     39    // Swagger options
     40    swagger: {
     41        info: {
     42        title: 'Test swagger',
     43        description: 'Testing the Fastify swagger API',
     44        version: '0.1.0'
     45        },
     46        externalDocs: {
     47        url: 'https://swagger.io',
     48        description: 'Find more info here'
     49        },
     50        host: 'localhost',
     51        schemes: ['http'],
     52    } 
     53    })
     54
     55    // await fastify.register(require('@fastify/swagger-ui'), {
     56    //   routePrefix: '/documentation',
     57    //   uiConfig: {
     58    //     docExpansion: 'full',
     59    //     deepLinking: false
     60    //   },
     61    //   uiHooks: {
     62    //     onRequest: function (request, reply, next) { next() },
     63    //     preHandler: function (request, reply, next) { next() }
     64    //   },
     65    //   staticCSP: true,
     66    //   transformStaticCSP: (header) => header,
     67    //   transformSpecification: (swaggerObject, request, reply) => { return swaggerObject },
     68    //   transformSpecificationClone: true
     69    // })
     70} catch (err) {
     71    console.log(err);
     72    process.exit(1);
     73}    
     74}
     75
     76// Register Fastify Route
     77const registerRoute = async () => {
     78try {
     79    // Define a sample route
     80    fastify.get('/route1', async (request, reply) => {
     81    return { hello: 'world' };
     82    });
     83
     84    // Define a sample route
     85    fastify.get('/route2', async (request, reply) => {
     86    return { hello: 'world' };
     87    });
     88
     89    // Define a swagger route
     90    fastify.get('/doc', async (request, reply) => {
     91    return fastify.swagger();
     92    // reply.send(fastify.swagger()) ==> work, but bad, why?
     93    });
     94
     95    // Enable swagger-stats
     96    // fastify.register(require('fastify-express')).then(()=>{
     97    fastify.register(require('@fastify/express')).then(()=>{
     98    fastify.register(swStats.getFastifyPlugin, {swaggerSpec:apiSpec});
     99    });
    100
    101    await fastify.ready()
    102    await fastify.listen({ port: 3000 });
    103
    104    console.log(`Server listening on ${fastify.server.address().port}`);
    105} catch (err) {
    106    console.log(err);
    107    process.exit(1);
    108}
    109};
    110
    111const main = async () => {
    112console.log("Start to register Swagger")
    113await registerSwagger();
    114console.log("Start to register Route")
    115await registerRoute();
    116console.log("Done")
    117}
    118
    119main();
    
  1. If you want to use RapiDoc, you need to add a HTML file

     1<!doctype html> <!-- Important: must specify -->
     2<html>
     3<head>
     4    <meta charset="utf-8"> <!-- Important: rapi-doc uses utf8 characters -->
     5    <script type="module" src="rapidoc-min.js"></script>
     6</head>
     7<body>
     8    <rapi-doc spec-url = "http://localhost:3000/doc"> </rapi-doc>
     9</body>
    10</html>
    

Result

What's next

Consider to try Elements

Posts in this Series