Русский
Русский
English
Статистика
Реклама

Создание самодокументирующегося сервера на Node.JS

Условия:

  • валидация через Joi

  • использование Typescript

  • Express сервер

  • SWAGGER на /api-docs

Задача: DRY

Решение:

Для начала необходимо решить что первично: схема Joi, Swagger или TypeScript интерфейс. Эмпирическим путём установлено что первичной стоит сделать Joi.

1. Установка всех модулей на Express

npm install --save swagger-ui-express

Добавить строки в app.ts (index.ts):

import swaggerUI = require('swagger-ui-express')import swDocument from './openapi'...app.use('/api-docs',swaggerUI.serve,swaggerUI.setup(swDocument))

2. Создать ./openapi.ts

В этом файле содержится основные сведения о сервере. Создать его (как и все схемы, приведённые ниже) можно с помощью SWAGGER-утилиты. Важно выбрать при этом протокол openapi v3.0.0

Пример содержимого:

import {swLoginRoute} from './routes/login'const swagger = {  openapi: '3.0.0',  info: {    title: 'Express API for Dangle',    version: '1.0.0',    description: 'The REST API for Dangle Panel service'  },  servers: [    {      url: 'http://localhost:3001',      description: 'Development server'    }  ],  paths: {    ...swLoginRoute  },}export default swagger

Пути забираются из роутеров через инклуды.

3. Написать спецификацию роутера

В каждом роутере добавить openapi-описание

Пример ./routes/login/index.ts:

import {swGetUserInfo} from './get-user-info'import {swUpdateInfo} from './update-info'export const swLoginRoute = {  "/login": {    "get": {      ...swGetUserInfo    },    "patch": {      ...swUpdateInfo    }  }}

Выше описан путь /login, поддеживающий два метода: get и patch. Спецификации методов берутся инлудами из файлов get-user-into.ts и update-info.ts. Эти же файлы у меня содержат сами роуты.

4. Написать спецификацию роута и валидацию данных

Спецификация роута будет создаваться автоматически, на основе Joi-схемы.

Для начала сделаем инклуд будущей схемы в нашем роуте.

Примечание: совершенно не важно как вы располагаете ваши файлы, если соответственно модифицируете инклуды.

Строки из файла update-info.ts, в котором расположен мой роут (код код его самого нам не важен):

import schema, {joiSchema} from './update-info.spec/schema'export const swUpdateInfo = {  "summary": "update the user info",  "tags": [    "login"  ],  "parameters": [    {      "name": "key",      "in": "header",      "schema": {        "type": "string"      },      "required": true    }  ],  "requestBody": {    "content": {      "application/json": {        "schema": {          ...schema        }      }    }  },  "responses": {    "200": {      "description": "Done"    },    "default": {      "description": "Error message"    }  }}// ...далее идёт код роута

Этот JSON-объект можно сгенерить той же Swagger-утилитой, чтобы не мучать себя. Обратите внимание на следующую строку:

"schema": {  ...schema}

Это обеспечивает динамическое подключение нашей схемы.

Теперь можно добавить Joi-валидацию в роуте:

await joiSchema.validateAsync(req.body)

4. Пишем Joi-схему

Установка Joi:

npm install --save joi joi-to-swagger

Пример содержимого файла:

const joi = require('joi')const j2s = require('joi-to-swagger')// Joiexport const joiSchema = joi.object().keys({  mode:    joi.string().required(),  email:   joi.string().email()})// end of Joiconst schema = j2s(joiSchema).swaggerexport default schema

Данный файл осуществляет экспорт Joi-объекта и его swagger-схемы.

Чтож, на данном этапе у нас уже есть самодокументирующийся SWAGGER-сервер и валидация данных. Осталось настроить автоматическую генерацию TypeScript-интерфейсов

5. Генерация интерфейсов TypeScript

npm install --save-dev gulp @babel/register @babel/plugin-proposal-class-properties @babel/preset-env @babel/preset-typescript

Задачи на себя возьмёт Gulp. Это самая чувствительная часть системы, которую нужно настроить вручную под структуры вашего проекта. Вот как выглядит gulpfile.ts у меня:

const gulp = require('gulp')const through = require('through2')import { compile } from 'json-schema-to-typescript'const fs = require('fs')const endName = "schema.ts"const routes = `./routes/**/*.spec/*${endName}`function path(str: string) : string{   let base = str   if(base.lastIndexOf(endName) != -1)     base = base.substring(0, base.lastIndexOf(endName))   return base}gulp.task('schema', () => {  return gulp.src(routes)    .pipe(through.obj((chunk, enc, cb) => {      const filename = chunk.path      import(filename).then(schema => { // dynamic import        console.log('Converting', filename)        compile(schema.default, `IDTO`)          .then(ts => {            //console.log(path(filename).concat('interface.ts'), ts)            fs.writeFileSync(path(filename).concat('interface.ts'), ts)          })        })      cb(null, chunk)    }))})// watch serviceconst { watch, series } = require('gulp')exports.default = function() {  watch(routes, series('schema'))}

Скрипт обходит все подкаталоги с названием *.spec внутри каталога с роутера. Там он ищет файлы с именами *schema.ts и создаёт рядом файлы c именами *interface.ts

Заключение

Разумеется, эти большие и сложные JSON-объекты с openAPI-спецификацией пугают, но надо понимать, что они не пишутся вручную, а геренятся SWAGGER-утилитой.

Из-за неопытности первичная настройка механизма может занять какое-то время, но это приведет к экономии сотен часов, которые могли бы быть потрачены на рутину!

Источник: habr.com
К списку статей
Опубликовано: 20.01.2021 14:20:49
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Javascript

Node.js

Api

Swagger

Openapi

Joi

Express

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru