Skip to content

Commit c2b8958

Browse files
authored
Swagger (#32)
* Create swagger.json for documentation * Add swagger-ui-express to yarn.lock * Remove unnecessary Swagger YAML file and move JSON file to right location * Extract Swagger middleware to be used as a dependency of the container * Fix indentation and API name of swagger.json file * Move api docs to /api/docs endpoint * Refactor Swagger file * Set empty swagger.json file to be used after cleanup
1 parent 897ef27 commit c2b8958

File tree

7 files changed

+287
-4
lines changed

7 files changed

+287
-4
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@
4545
"pm2": "^2.4.2",
4646
"sequelize": "^3.30.4",
4747
"sequelize-cli": "^3.0.0",
48-
"structure": "^1.2.0"
48+
"structure": "^1.2.0",
49+
"swagger-ui-express": "^2.0.14"
4950
},
5051
"devDependencies": {
5152
"chai": "^4.1.2",

scripts/cleanup.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const path = require('path');
22
const replace = require('replace-in-file');
33
const remove = require('del');
44
const Listr = require('listr');
5+
const { writeFileSync } = require('fs');
56

67
const srcPath = path.join(__dirname, '..', 'src');
78
const testPath = path.join(__dirname, '..', 'test');
@@ -55,6 +56,27 @@ const tasks = new Listr([
5556
]);
5657
}
5758
},
59+
{
60+
title: 'Remove example data from swagger.json',
61+
task() {
62+
writeFileSync(
63+
path.join(srcPath, 'interfaces', 'http', 'swagger', 'swagger.json'),
64+
JSON.stringify({
65+
openapi: '3.0.0',
66+
info: {
67+
title: 'Node API boilerplate',
68+
version: 'v1'
69+
},
70+
servers: [
71+
{
72+
description: 'Local server',
73+
url: '/api'
74+
}
75+
]
76+
}, null, ' ')
77+
);
78+
}
79+
},
5880
{
5981
title: 'Remove cleanup script from package.json',
6082
task() {

src/container.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const router = require('./interfaces/http/router');
1818
const loggerMiddleware = require('./interfaces/http/logging/loggerMiddleware');
1919
const errorHandler = require('./interfaces/http/errors/errorHandler');
2020
const devErrorHandler = require('./interfaces/http/errors/devErrorHandler');
21+
const swaggerMiddleware = require('./interfaces/http/swagger/swaggerMiddleware');
2122

2223
const logger = require('./infra/logging/logger');
2324
const SequelizeUsersRepository = require('./infra/user/SequelizeUsersRepository');
@@ -44,7 +45,8 @@ container
4445
})
4546
.registerValue({
4647
containerMiddleware: scopePerRequest(container),
47-
errorHandler: config.production ? errorHandler : devErrorHandler
48+
errorHandler: config.production ? errorHandler : devErrorHandler,
49+
swaggerMiddleware: [swaggerMiddleware]
4850
});
4951

5052
// Repositories

src/interfaces/http/router.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const compression = require('compression');
66
const methodOverride = require('method-override');
77
const controller = require('./utils/createControllerRoutes');
88

9-
module.exports = ({ config, containerMiddleware, loggerMiddleware, errorHandler }) => {
9+
module.exports = ({ config, containerMiddleware, loggerMiddleware, errorHandler, swaggerMiddleware }) => {
1010
const router = Router();
1111

1212
/* istanbul ignore if */
@@ -26,7 +26,8 @@ module.exports = ({ config, containerMiddleware, loggerMiddleware, errorHandler
2626
.use(cors())
2727
.use(bodyParser.json())
2828
.use(compression())
29-
.use(containerMiddleware);
29+
.use(containerMiddleware)
30+
.use('/docs', swaggerMiddleware);
3031

3132
/*
3233
* Add your API routes here
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
{
2+
"openapi": "3.0.0",
3+
"info": {
4+
"title": "Node API boilerplate",
5+
"version": "v1"
6+
},
7+
"servers": [
8+
{
9+
"description": "Local server",
10+
"url": "/api"
11+
}
12+
],
13+
"paths": {
14+
"/users": {
15+
"get": {
16+
"operationId": "listUsers",
17+
"tags": [ "Users" ],
18+
"responses": {
19+
"200": {
20+
"description": "List of all users",
21+
"content": {
22+
"application/json": {
23+
"schema": {
24+
"type": "array",
25+
"items": {
26+
"$ref": "#/components/schemas/User"
27+
}
28+
}
29+
}
30+
}
31+
}
32+
}
33+
},
34+
"post": {
35+
"operationId": "createUser",
36+
"tags": [ "Users" ],
37+
"requestBody": {
38+
"description": "User data",
39+
"required": true,
40+
"content": {
41+
"application/json": {
42+
"schema": {
43+
"$ref": "#/components/schemas/NewUser"
44+
}
45+
}
46+
}
47+
},
48+
"responses": {
49+
"201": {
50+
"description": "User created successfully",
51+
"content": {
52+
"application/json": {
53+
"schema": {
54+
"$ref": "#/components/schemas/User"
55+
}
56+
}
57+
}
58+
},
59+
"400": {
60+
"description": "User not created because of validation error",
61+
"content": {
62+
"application/json": {
63+
"schema": {
64+
"$ref": "#/components/schemas/ValidationError"
65+
}
66+
}
67+
}
68+
}
69+
}
70+
}
71+
},
72+
"/users/{id}": {
73+
"get": {
74+
"operationId": "showUser",
75+
"tags": [ "Users" ],
76+
"parameters": [
77+
{
78+
"name": "id",
79+
"in": "path",
80+
"description": "Id of user to show",
81+
"required": true,
82+
"type": "integer",
83+
"format": "int64"
84+
}
85+
],
86+
"responses": {
87+
"200": {
88+
"description": "Return user with given id",
89+
"content": {
90+
"application/json": {
91+
"schema": {
92+
"$ref": "#/components/schemas/User"
93+
}
94+
}
95+
}
96+
},
97+
"404": {
98+
"description": "User not found",
99+
"content": {
100+
"application/json": {
101+
"schema": {
102+
"$ref": "#/components/schemas/NotFoundError"
103+
}
104+
}
105+
}
106+
}
107+
}
108+
},
109+
"put": {
110+
"operationId": "updateUser",
111+
"tags": [ "Users" ],
112+
"parameters": [
113+
{
114+
"name": "id",
115+
"in": "path",
116+
"description": "Id of user to update",
117+
"required": true,
118+
"type": "integer",
119+
"format": "int64"
120+
}
121+
],
122+
"requestBody": {
123+
"description": "User new data",
124+
"required": true,
125+
"content": {
126+
"application/json": {
127+
"schema": {
128+
"$ref": "#/components/schemas/NewUser"
129+
}
130+
}
131+
}
132+
},
133+
"responses": {
134+
"202": {
135+
"description": "User updated successfully",
136+
"content": {
137+
"application/json": {
138+
"schema": {
139+
"$ref": "#/components/schemas/User"
140+
}
141+
}
142+
}
143+
},
144+
"404": {
145+
"description": "User not found",
146+
"content": {
147+
"application/json": {
148+
"schema": {
149+
"$ref": "#/components/schemas/NotFoundError"
150+
}
151+
}
152+
}
153+
}
154+
}
155+
},
156+
"delete": {
157+
"operationId": "deleteUser",
158+
"tags": [ "Users" ],
159+
"parameters": [
160+
{
161+
"name": "id",
162+
"in": "path",
163+
"description": "Id of user to delete",
164+
"required": true,
165+
"type": "integer",
166+
"format": "int64"
167+
}
168+
],
169+
"responses": {
170+
"202": {
171+
"description": "User deleted successfully"
172+
},
173+
"404": {
174+
"description": "User not found",
175+
"content": {
176+
"application/json": {
177+
"schema": {
178+
"$ref": "#/components/schemas/NotFoundError"
179+
}
180+
}
181+
}
182+
}
183+
}
184+
}
185+
}
186+
},
187+
"components": {
188+
"schemas": {
189+
"User": {
190+
"allOf": [
191+
{ "$ref": "#/components/schemas/NewUser" },
192+
{
193+
"required": [ "id" ],
194+
"properties": {
195+
"id": {
196+
"type": "integer",
197+
"format": "int64"
198+
}
199+
}
200+
}
201+
]
202+
},
203+
"NewUser": {
204+
"required": [ "name" ],
205+
"properties": {
206+
"name": {
207+
"type": "string"
208+
}
209+
}
210+
},
211+
"ValidationError": {
212+
"properties": {
213+
"type": {
214+
"type": "string",
215+
"enum": [ "ValidationError" ]
216+
},
217+
"details": {
218+
"type": "array",
219+
"items": {
220+
"$ref": "#/components/schemas/ValidationErrorDetail"
221+
}
222+
}
223+
}
224+
},
225+
"ValidationErrorDetail": {
226+
"properties": {
227+
"message": {
228+
"type": "string"
229+
},
230+
"path": {
231+
"type": "string"
232+
}
233+
}
234+
},
235+
"NotFoundError": {
236+
"properties": {
237+
"type": {
238+
"type": "string",
239+
"enum": [ "NotFoundError" ]
240+
},
241+
"details": {
242+
"type": "string",
243+
"enum": [ "User with id {id} not found" ]
244+
}
245+
}
246+
}
247+
}
248+
}
249+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const SwaggerUi = require('swagger-ui-express');
2+
const swaggerDocument = require('./swagger.json');
3+
4+
module.exports = [SwaggerUi.serve, SwaggerUi.setup(swaggerDocument)];

yarn.lock

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3806,6 +3806,10 @@ supports-color@^4.0.0:
38063806
dependencies:
38073807
has-flag "^2.0.0"
38083808

3809+
swagger-ui-express@^2.0.14:
3810+
version "2.0.15"
3811+
resolved "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-2.0.15.tgz#fba85a357b40c0d629e942537199987b8085587d"
3812+
38093813
symbol-observable@^1.0.1:
38103814
version "1.0.4"
38113815
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"

0 commit comments

Comments
 (0)