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
-
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}
-
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();
-
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
- OpenAPI json: http://localhost:3000/doc
- RapiDoc: http://localhost:3000/public/index.html
- Swagger UI: http://localhost:3000/documentation/
- Swagger statistics: http://localhost:3000/swagger-stats/
What's next
Consider to try Elements
Posts in this Series
- Node.js + MySQL (Connecton Pool + Prepared Statement + Transaction)
- Node + Fastify + OpenAPI + Statistics