[JavaScript, Node.JS, API] Создание самодокументирующегося сервера на Node.JS
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Условия:
- валидация через 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')
// Joi
export const joiSchema = joi.object().keys({
mode: joi.string().required(),
email: joi.string().email()
})
// end of Joi
const schema = j2s(joiSchema).swagger
export 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 service
const { watch, series } = require('gulp')
exports.default = function() {
watch(routes, series('schema'))
}
Скрипт обходит все подкаталоги с названием *.spec внутри каталога с роутера. Там он ищет файлы с именами *schema.ts и создаёт рядом файлы c именами *interface.tsЗаключениеРазумеется, эти большие и сложные JSON-объекты с openAPI-спецификацией пугают, но надо понимать, что они не пишутся вручную, а геренятся SWAGGER-утилитой.Из-за неопытности первичная настройка механизма может занять какое-то время, но это приведет к экономии сотен часов, которые могли бы быть потрачены на рутину!
===========
Источник:
habr.com
===========
Похожие новости:
- [API, Видеоконференцсвязь] Top 10 Best Voice Chat APIs for Mobile & Web Apps
- [PHP, JavaScript, Программирование, Symfony] Новое в Symfony: инициатива UX — новая экосистема JavaScript для Symfony (перевод)
- [Open source, JavaScript, Программирование, Визуализация данных] Новый график на Moiva.io с данными от #StateOfJS
- [JavaScript, Node.JS] Авторизация и аутентификация на NodeJs и Socket.io и проблемы вокруг
- [Разработка веб-сайтов, JavaScript, VueJS] Впечатления о Vue.js после React
- [JavaScript, ReactJS] Первое погружение в исходники хуков (задел на будущие статьи)
- [SQL, API] Дизайн пагинации страниц в API (перевод)
- [JavaScript, Разработка игр, Usability, Тестирование игр] Управляемость транспортного средства в симуляторе: настраиваем коэффициенты модели
- [Python, API] Скрапинг Avito без headless-браузера
- [JavaScript, TypeScript] Ant Design Component Customization and Bundle Optimization
Теги для поиска: #_javascript, #_node.js, #_api, #_swagger, #_openapi, #_joi, #_express, #_node.js, #_api, #_javascript, #_node.js, #_api
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 13:54
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Условия:
npm install --save swagger-ui-express
import swaggerUI = require('swagger-ui-express')
import swDocument from './openapi' ... app.use('/api-docs',swaggerUI.serve,swaggerUI.setup(swDocument)) 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 import {swGetUserInfo} from './get-user-info'
import {swUpdateInfo} from './update-info' export const swLoginRoute = { "/login": { "get": { ...swGetUserInfo }, "patch": { ...swUpdateInfo } } } 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" } } } // ...далее идёт код роута "schema": {
...schema } await joiSchema.validateAsync(req.body)
npm install --save joi joi-to-swagger
const joi = require('joi')
const j2s = require('joi-to-swagger') // Joi export const joiSchema = joi.object().keys({ mode: joi.string().required(), email: joi.string().email() }) // end of Joi const schema = j2s(joiSchema).swagger export default schema npm install --save-dev gulp @babel/register @babel/plugin-proposal-class-properties @babel/preset-env @babel/preset-typescript
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 service const { watch, series } = require('gulp') exports.default = function() { watch(routes, series('schema')) } =========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 13:54
Часовой пояс: UTC + 5