nodejs-express-starter 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/.dockerignore +3 -0
  2. package/.editorconfig +9 -0
  3. package/.env.example +22 -0
  4. package/.eslintignore +2 -0
  5. package/.eslintrc.json +32 -0
  6. package/.gitignore +14 -0
  7. package/.prettierignore +3 -0
  8. package/.prettierrc.json +4 -0
  9. package/Dockerfile +15 -0
  10. package/LICENSE +21 -0
  11. package/README.md +440 -0
  12. package/bin/createNodejsApp.js +105 -0
  13. package/docker-compose.dev.yml +4 -0
  14. package/docker-compose.prod.yml +4 -0
  15. package/docker-compose.test.yml +4 -0
  16. package/docker-compose.yml +30 -0
  17. package/jest.config.js +9 -0
  18. package/package.json +117 -0
  19. package/src/app.js +82 -0
  20. package/src/config/config.js +64 -0
  21. package/src/config/logger.js +26 -0
  22. package/src/config/morgan.js +25 -0
  23. package/src/config/passport.js +30 -0
  24. package/src/config/roles.js +12 -0
  25. package/src/config/tokens.js +10 -0
  26. package/src/controllers/auth.controller.js +59 -0
  27. package/src/controllers/index.js +2 -0
  28. package/src/controllers/user.controller.js +43 -0
  29. package/src/docs/components.yml +92 -0
  30. package/src/docs/swaggerDef.js +21 -0
  31. package/src/index.js +57 -0
  32. package/src/middlewares/auth.js +33 -0
  33. package/src/middlewares/error.js +47 -0
  34. package/src/middlewares/rateLimiter.js +11 -0
  35. package/src/middlewares/requestId.js +14 -0
  36. package/src/middlewares/validate.js +21 -0
  37. package/src/models/index.js +2 -0
  38. package/src/models/plugins/index.js +2 -0
  39. package/src/models/plugins/paginate.plugin.js +70 -0
  40. package/src/models/plugins/toJSON.plugin.js +43 -0
  41. package/src/models/token.model.js +44 -0
  42. package/src/models/user.model.js +91 -0
  43. package/src/routes/v1/auth.route.js +291 -0
  44. package/src/routes/v1/docs.route.js +21 -0
  45. package/src/routes/v1/health.route.js +43 -0
  46. package/src/routes/v1/index.js +39 -0
  47. package/src/routes/v1/user.route.js +252 -0
  48. package/src/services/auth.service.js +99 -0
  49. package/src/services/email.service.js +63 -0
  50. package/src/services/index.js +4 -0
  51. package/src/services/token.service.js +123 -0
  52. package/src/services/user.service.js +89 -0
  53. package/src/utils/ApiError.js +14 -0
  54. package/src/utils/catchAsync.js +5 -0
  55. package/src/utils/pick.js +17 -0
  56. package/src/validations/auth.validation.js +60 -0
  57. package/src/validations/custom.validation.js +21 -0
  58. package/src/validations/index.js +2 -0
  59. package/src/validations/user.validation.js +54 -0
  60. package/tests/fixtures/token.fixture.js +14 -0
  61. package/tests/fixtures/user.fixture.js +46 -0
  62. package/tests/integration/auth.test.js +587 -0
  63. package/tests/integration/docs.test.js +14 -0
  64. package/tests/integration/health.test.js +32 -0
  65. package/tests/integration/user.test.js +625 -0
  66. package/tests/unit/middlewares/error.test.js +168 -0
  67. package/tests/unit/models/plugins/paginate.plugin.test.js +61 -0
  68. package/tests/unit/models/plugins/toJSON.plugin.test.js +89 -0
  69. package/tests/unit/models/user.model.test.js +57 -0
  70. package/tests/utils/setupTestDB.js +18 -0
@@ -0,0 +1,4 @@
1
+ services:
2
+ node-app:
3
+ container_name: node-app-prod
4
+ command: npm start
@@ -0,0 +1,4 @@
1
+ services:
2
+ node-app:
3
+ container_name: node-app-test
4
+ command: npm test
@@ -0,0 +1,30 @@
1
+ services:
2
+ node-app:
3
+ build: .
4
+ image: node-app
5
+ environment:
6
+ - MONGODB_URL=mongodb://mongodb:27017/node-boilerplate
7
+ ports:
8
+ - '3000:3000'
9
+ depends_on:
10
+ - mongodb
11
+ volumes:
12
+ - .:/usr/src/node-app
13
+ networks:
14
+ - node-network
15
+
16
+ mongodb:
17
+ image: mongo:4.2.1-bionic
18
+ ports:
19
+ - '27017:27017'
20
+ volumes:
21
+ - dbdata:/data/db
22
+ networks:
23
+ - node-network
24
+
25
+ volumes:
26
+ dbdata:
27
+
28
+ networks:
29
+ node-network:
30
+ driver: bridge
package/jest.config.js ADDED
@@ -0,0 +1,9 @@
1
+ module.exports = {
2
+ testEnvironment: 'node',
3
+ testEnvironmentOptions: {
4
+ NODE_ENV: 'test',
5
+ },
6
+ restoreMocks: true,
7
+ coveragePathIgnorePatterns: ['node_modules', 'src/config', 'src/app.js', 'tests'],
8
+ coverageReporters: ['text', 'lcov', 'clover', 'html'],
9
+ };
package/package.json ADDED
@@ -0,0 +1,117 @@
1
+ {
2
+ "name": "nodejs-express-starter",
3
+ "version": "1.7.0",
4
+ "description": "Create a Node.js app for building production-ready RESTful APIs using Express, by running one command",
5
+ "bin": {
6
+ "nodejs-express-starter": "./bin/createNodejsApp.js"
7
+ },
8
+ "main": "src/index.js",
9
+ "repository": "https://github.com/Ahlyab/express-backend-starter-js.git",
10
+ "author": "Ahlyab <ahalyabasad@gmail.com>",
11
+ "license": "MIT",
12
+ "files": [
13
+ "bin",
14
+ "src",
15
+ "tests",
16
+ ".env.example",
17
+ ".gitignore",
18
+ ".eslintrc.json",
19
+ ".eslintignore",
20
+ ".prettierrc.json",
21
+ ".prettierignore",
22
+ ".editorconfig",
23
+ "docker-compose.yml",
24
+ "docker-compose.dev.yml",
25
+ "docker-compose.prod.yml",
26
+ "docker-compose.test.yml",
27
+ "Dockerfile",
28
+ ".dockerignore",
29
+ "jest.config.js",
30
+ "LICENSE",
31
+ "README.md"
32
+ ],
33
+ "engines": {
34
+ "node": ">=18.0.0"
35
+ },
36
+ "scripts": {
37
+ "start": "node src/index.js",
38
+ "dev": "cross-env NODE_ENV=development nodemon src/index.js",
39
+ "test": "cross-env NODE_ENV=test jest -i --colors --verbose --detectOpenHandles",
40
+ "test:watch": "cross-env NODE_ENV=test jest -i --watchAll",
41
+ "coverage": "cross-env NODE_ENV=test jest -i --coverage",
42
+ "lint": "eslint .",
43
+ "lint:fix": "eslint . --fix",
44
+ "prettier": "npx prettier --check \"**/*.js\"",
45
+ "prettier:fix": "npx prettier --write \"**/*.js\"",
46
+ "docker:prod": "docker compose -f docker-compose.yml -f docker-compose.prod.yml up --build",
47
+ "docker:prod:down": "docker compose -f docker-compose.yml -f docker-compose.prod.yml down",
48
+ "docker:dev": "docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build",
49
+ "docker:dev:down": "docker compose -f docker-compose.yml -f docker-compose.dev.yml down",
50
+ "docker:test": "docker compose -f docker-compose.yml -f docker-compose.test.yml up --build",
51
+ "docker:test:down": "docker compose -f docker-compose.yml -f docker-compose.test.yml down",
52
+ "prepare": "husky"
53
+ },
54
+ "keywords": [
55
+ "node",
56
+ "node.js",
57
+ "boilerplate",
58
+ "generator",
59
+ "express",
60
+ "rest",
61
+ "api",
62
+ "mongodb",
63
+ "mongoose",
64
+ "es6",
65
+ "es7",
66
+ "es8",
67
+ "es9",
68
+ "jest",
69
+ "travis",
70
+ "docker",
71
+ "passport",
72
+ "joi",
73
+ "eslint",
74
+ "prettier"
75
+ ],
76
+ "dependencies": {
77
+ "bcryptjs": "^2.4.3",
78
+ "compression": "^1.7.4",
79
+ "cors": "^2.8.5",
80
+ "cross-env": "^7.0.0",
81
+ "dotenv": "^16.4.5",
82
+ "express": "^4.21.0",
83
+ "express-mongo-sanitize": "^2.2.0",
84
+ "express-rate-limit": "^7.4.0",
85
+ "helmet": "^7.1.0",
86
+ "http-status": "^1.7.4",
87
+ "joi": "^17.13.3",
88
+ "jsonwebtoken": "^9.0.2",
89
+ "moment": "^2.30.1",
90
+ "mongoose": "^6.13.0",
91
+ "morgan": "^1.10.0",
92
+ "nodemailer": "^8.0.2",
93
+ "passport": "^0.7.0",
94
+ "passport-jwt": "^4.0.1",
95
+ "swagger-jsdoc": "^6.2.8",
96
+ "swagger-ui-express": "^5.0.0",
97
+ "validator": "^13.12.0",
98
+ "winston": "^3.14.2"
99
+ },
100
+ "devDependencies": {
101
+ "@faker-js/faker": "^9.2.0",
102
+ "eslint": "^8.57.0",
103
+ "eslint-config-airbnb-base": "^15.0.0",
104
+ "eslint-config-prettier": "^9.1.0",
105
+ "eslint-plugin-import": "^2.29.1",
106
+ "eslint-plugin-jest": "^27.9.0",
107
+ "eslint-plugin-prettier": "^5.2.0",
108
+ "eslint-plugin-security": "1.7.1",
109
+ "husky": "^9.0.11",
110
+ "jest": "^29.7.0",
111
+ "lint-staged": "^15.2.10",
112
+ "node-mocks-http": "^1.14.0",
113
+ "nodemon": "^3.1.4",
114
+ "prettier": "^3.3.3",
115
+ "supertest": "^7.0.0"
116
+ }
117
+ }
package/src/app.js ADDED
@@ -0,0 +1,82 @@
1
+ const express = require('express');
2
+ const helmet = require('helmet');
3
+ const mongoSanitize = require('express-mongo-sanitize');
4
+ const compression = require('compression');
5
+ const cors = require('cors');
6
+ const passport = require('passport');
7
+ const httpStatus = require('http-status');
8
+ const config = require('./config/config');
9
+ const morgan = require('./config/morgan');
10
+ const { jwtStrategy } = require('./config/passport');
11
+ const { authLimiter } = require('./middlewares/rateLimiter');
12
+ const routes = require('./routes/v1');
13
+ const healthRoute = require('./routes/v1/health.route');
14
+ const { errorConverter, errorHandler } = require('./middlewares/error');
15
+ const ApiError = require('./utils/ApiError');
16
+ const requestId = require('./middlewares/requestId');
17
+
18
+ const app = express();
19
+
20
+ // request ID for tracing (must be early)
21
+ app.use(requestId);
22
+
23
+ // health check endpoints (before auth, for load balancers/orchestration)
24
+ app.use('/health', healthRoute);
25
+
26
+ if (config.env !== 'test') {
27
+ app.use(morgan.successHandler);
28
+ app.use(morgan.errorHandler);
29
+ }
30
+
31
+ // set security HTTP headers
32
+ app.use(
33
+ helmet({
34
+ contentSecurityPolicy: config.env === 'production',
35
+ crossOriginEmbedderPolicy: false,
36
+ }),
37
+ );
38
+
39
+ // parse json request body
40
+ app.use(express.json({ limit: '10kb' }));
41
+
42
+ // parse urlencoded request body
43
+ app.use(express.urlencoded({ extended: true }));
44
+
45
+ // sanitize request data
46
+ app.use(mongoSanitize());
47
+
48
+ // gzip compression
49
+ app.use(compression());
50
+
51
+ // enable cors
52
+ const corsOptions = {
53
+ origin: config.corsOrigin === '*' ? true : config.corsOrigin,
54
+ credentials: true,
55
+ };
56
+ app.use(cors(corsOptions));
57
+ app.options('*', cors(corsOptions));
58
+
59
+ // jwt authentication
60
+ app.use(passport.initialize());
61
+ passport.use('jwt', jwtStrategy);
62
+
63
+ // limit repeated failed requests to auth endpoints
64
+ if (config.env === 'production') {
65
+ app.use('/v1/auth', authLimiter);
66
+ }
67
+
68
+ // v1 api routes
69
+ app.use('/v1', routes);
70
+
71
+ // send back a 404 error for any unknown api request
72
+ app.use((req, res, next) => {
73
+ next(new ApiError(httpStatus.NOT_FOUND, 'Not found'));
74
+ });
75
+
76
+ // convert error to ApiError, if needed
77
+ app.use(errorConverter);
78
+
79
+ // handle error
80
+ app.use(errorHandler);
81
+
82
+ module.exports = app;
@@ -0,0 +1,64 @@
1
+ const dotenv = require('dotenv');
2
+ const path = require('path');
3
+ const Joi = require('joi');
4
+
5
+ dotenv.config({ path: path.join(__dirname, '../../.env') });
6
+
7
+ const envVarsSchema = Joi.object()
8
+ .keys({
9
+ NODE_ENV: Joi.string().valid('production', 'development', 'test').required(),
10
+ PORT: Joi.number().default(3000),
11
+ LOG_LEVEL: Joi.string().valid('error', 'warn', 'info', 'http', 'verbose', 'debug').default('info'),
12
+ CORS_ORIGIN: Joi.string().allow('').default('*').description('CORS allowed origins, comma-separated'),
13
+ MONGODB_URL: Joi.string().required().description('Mongo DB url'),
14
+ JWT_SECRET: Joi.string().required().description('JWT secret key'),
15
+ JWT_ACCESS_EXPIRATION_MINUTES: Joi.number().default(30).description('minutes after which access tokens expire'),
16
+ JWT_REFRESH_EXPIRATION_DAYS: Joi.number().default(30).description('days after which refresh tokens expire'),
17
+ JWT_RESET_PASSWORD_EXPIRATION_MINUTES: Joi.number()
18
+ .default(10)
19
+ .description('minutes after which reset password token expires'),
20
+ JWT_VERIFY_EMAIL_EXPIRATION_MINUTES: Joi.number()
21
+ .default(10)
22
+ .description('minutes after which verify email token expires'),
23
+ SMTP_HOST: Joi.string().description('server that will send the emails'),
24
+ SMTP_PORT: Joi.number().description('port to connect to the email server'),
25
+ SMTP_USERNAME: Joi.string().description('username for email server'),
26
+ SMTP_PASSWORD: Joi.string().description('password for email server'),
27
+ EMAIL_FROM: Joi.string().description('the from field in the emails sent by the app'),
28
+ })
29
+ .unknown();
30
+
31
+ const { value: envVars, error } = envVarsSchema.prefs({ errors: { label: 'key' } }).validate(process.env);
32
+
33
+ if (error) {
34
+ throw new Error(`Config validation error: ${error.message}`);
35
+ }
36
+
37
+ module.exports = {
38
+ env: envVars.NODE_ENV,
39
+ port: envVars.PORT,
40
+ logLevel: envVars.LOG_LEVEL,
41
+ corsOrigin: envVars.CORS_ORIGIN && envVars.CORS_ORIGIN !== '*' ? envVars.CORS_ORIGIN.split(',').map((o) => o.trim()) : '*',
42
+ mongoose: {
43
+ url: envVars.MONGODB_URL + (envVars.NODE_ENV === 'test' ? '-test' : ''),
44
+ options: {},
45
+ },
46
+ jwt: {
47
+ secret: envVars.JWT_SECRET,
48
+ accessExpirationMinutes: envVars.JWT_ACCESS_EXPIRATION_MINUTES,
49
+ refreshExpirationDays: envVars.JWT_REFRESH_EXPIRATION_DAYS,
50
+ resetPasswordExpirationMinutes: envVars.JWT_RESET_PASSWORD_EXPIRATION_MINUTES,
51
+ verifyEmailExpirationMinutes: envVars.JWT_VERIFY_EMAIL_EXPIRATION_MINUTES,
52
+ },
53
+ email: {
54
+ smtp: {
55
+ host: envVars.SMTP_HOST,
56
+ port: envVars.SMTP_PORT,
57
+ auth: {
58
+ user: envVars.SMTP_USERNAME,
59
+ pass: envVars.SMTP_PASSWORD,
60
+ },
61
+ },
62
+ from: envVars.EMAIL_FROM,
63
+ },
64
+ };
@@ -0,0 +1,26 @@
1
+ const winston = require('winston');
2
+ const config = require('./config');
3
+
4
+ const enumerateErrorFormat = winston.format((info) => {
5
+ if (info instanceof Error) {
6
+ Object.assign(info, { message: info.stack });
7
+ }
8
+ return info;
9
+ });
10
+
11
+ const logger = winston.createLogger({
12
+ level: config.logLevel || (config.env === 'development' ? 'debug' : 'info'),
13
+ format: winston.format.combine(
14
+ enumerateErrorFormat(),
15
+ config.env === 'development' ? winston.format.colorize() : winston.format.uncolorize(),
16
+ winston.format.splat(),
17
+ winston.format.printf(({ level, message }) => `${level}: ${message}`),
18
+ ),
19
+ transports: [
20
+ new winston.transports.Console({
21
+ stderrLevels: ['error'],
22
+ }),
23
+ ],
24
+ });
25
+
26
+ module.exports = logger;
@@ -0,0 +1,25 @@
1
+ const morgan = require('morgan');
2
+ const config = require('./config');
3
+ const logger = require('./logger');
4
+
5
+ morgan.token('message', (req, res) => res.locals.errorMessage || '');
6
+ morgan.token('request-id', (req) => req.id || '-');
7
+
8
+ const getIpFormat = () => (config.env === 'production' ? ':remote-addr - ' : '');
9
+ const successResponseFormat = `${getIpFormat()}[:request-id] :method :url :status - :response-time ms`;
10
+ const errorResponseFormat = `${getIpFormat()}[:request-id] :method :url :status - :response-time ms - message: :message`;
11
+
12
+ const successHandler = morgan(successResponseFormat, {
13
+ skip: (req, res) => res.statusCode >= 400,
14
+ stream: { write: (message) => logger.info(message.trim()) },
15
+ });
16
+
17
+ const errorHandler = morgan(errorResponseFormat, {
18
+ skip: (req, res) => res.statusCode < 400,
19
+ stream: { write: (message) => logger.error(message.trim()) },
20
+ });
21
+
22
+ module.exports = {
23
+ successHandler,
24
+ errorHandler,
25
+ };
@@ -0,0 +1,30 @@
1
+ const { Strategy: JwtStrategy, ExtractJwt } = require('passport-jwt');
2
+ const config = require('./config');
3
+ const { tokenTypes } = require('./tokens');
4
+ const { User } = require('../models');
5
+
6
+ const jwtOptions = {
7
+ secretOrKey: config.jwt.secret,
8
+ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
9
+ };
10
+
11
+ const jwtVerify = async (payload, done) => {
12
+ try {
13
+ if (payload.type !== tokenTypes.ACCESS) {
14
+ throw new Error('Invalid token type');
15
+ }
16
+ const user = await User.findById(payload.sub);
17
+ if (!user) {
18
+ return done(null, false);
19
+ }
20
+ done(null, user);
21
+ } catch (error) {
22
+ done(error, false);
23
+ }
24
+ };
25
+
26
+ const jwtStrategy = new JwtStrategy(jwtOptions, jwtVerify);
27
+
28
+ module.exports = {
29
+ jwtStrategy,
30
+ };
@@ -0,0 +1,12 @@
1
+ const allRoles = {
2
+ user: [],
3
+ admin: ['getUsers', 'manageUsers'],
4
+ };
5
+
6
+ const roles = Object.keys(allRoles);
7
+ const roleRights = new Map(Object.entries(allRoles));
8
+
9
+ module.exports = {
10
+ roles,
11
+ roleRights,
12
+ };
@@ -0,0 +1,10 @@
1
+ const tokenTypes = {
2
+ ACCESS: 'access',
3
+ REFRESH: 'refresh',
4
+ RESET_PASSWORD: 'resetPassword',
5
+ VERIFY_EMAIL: 'verifyEmail',
6
+ };
7
+
8
+ module.exports = {
9
+ tokenTypes,
10
+ };
@@ -0,0 +1,59 @@
1
+ const httpStatus = require('http-status');
2
+ const catchAsync = require('../utils/catchAsync');
3
+ const { authService, userService, tokenService, emailService } = require('../services');
4
+
5
+ const register = catchAsync(async (req, res) => {
6
+ const user = await userService.createUser(req.body);
7
+ const tokens = await tokenService.generateAuthTokens(user);
8
+ res.status(httpStatus.CREATED).send({ user, tokens });
9
+ });
10
+
11
+ const login = catchAsync(async (req, res) => {
12
+ const { email, password } = req.body;
13
+ const user = await authService.loginUserWithEmailAndPassword(email, password);
14
+ const tokens = await tokenService.generateAuthTokens(user);
15
+ res.send({ user, tokens });
16
+ });
17
+
18
+ const logout = catchAsync(async (req, res) => {
19
+ await authService.logout(req.body.refreshToken);
20
+ res.status(httpStatus.NO_CONTENT).send();
21
+ });
22
+
23
+ const refreshTokens = catchAsync(async (req, res) => {
24
+ const tokens = await authService.refreshAuth(req.body.refreshToken);
25
+ res.send({ ...tokens });
26
+ });
27
+
28
+ const forgotPassword = catchAsync(async (req, res) => {
29
+ const resetPasswordToken = await tokenService.generateResetPasswordToken(req.body.email);
30
+ await emailService.sendResetPasswordEmail(req.body.email, resetPasswordToken);
31
+ res.status(httpStatus.NO_CONTENT).send();
32
+ });
33
+
34
+ const resetPassword = catchAsync(async (req, res) => {
35
+ await authService.resetPassword(req.query.token, req.body.password);
36
+ res.status(httpStatus.NO_CONTENT).send();
37
+ });
38
+
39
+ const sendVerificationEmail = catchAsync(async (req, res) => {
40
+ const verifyEmailToken = await tokenService.generateVerifyEmailToken(req.user);
41
+ await emailService.sendVerificationEmail(req.user.email, verifyEmailToken);
42
+ res.status(httpStatus.NO_CONTENT).send();
43
+ });
44
+
45
+ const verifyEmail = catchAsync(async (req, res) => {
46
+ await authService.verifyEmail(req.query.token);
47
+ res.status(httpStatus.NO_CONTENT).send();
48
+ });
49
+
50
+ module.exports = {
51
+ register,
52
+ login,
53
+ logout,
54
+ refreshTokens,
55
+ forgotPassword,
56
+ resetPassword,
57
+ sendVerificationEmail,
58
+ verifyEmail,
59
+ };
@@ -0,0 +1,2 @@
1
+ module.exports.authController = require('./auth.controller');
2
+ module.exports.userController = require('./user.controller');
@@ -0,0 +1,43 @@
1
+ const httpStatus = require('http-status');
2
+ const pick = require('../utils/pick');
3
+ const ApiError = require('../utils/ApiError');
4
+ const catchAsync = require('../utils/catchAsync');
5
+ const { userService } = require('../services');
6
+
7
+ const createUser = catchAsync(async (req, res) => {
8
+ const user = await userService.createUser(req.body);
9
+ res.status(httpStatus.CREATED).send(user);
10
+ });
11
+
12
+ const getUsers = catchAsync(async (req, res) => {
13
+ const filter = pick(req.query, ['name', 'role']);
14
+ const options = pick(req.query, ['sortBy', 'limit', 'page']);
15
+ const result = await userService.queryUsers(filter, options);
16
+ res.send(result);
17
+ });
18
+
19
+ const getUser = catchAsync(async (req, res) => {
20
+ const user = await userService.getUserById(req.params.userId);
21
+ if (!user) {
22
+ throw new ApiError(httpStatus.NOT_FOUND, 'User not found');
23
+ }
24
+ res.send(user);
25
+ });
26
+
27
+ const updateUser = catchAsync(async (req, res) => {
28
+ const user = await userService.updateUserById(req.params.userId, req.body);
29
+ res.send(user);
30
+ });
31
+
32
+ const deleteUser = catchAsync(async (req, res) => {
33
+ await userService.deleteUserById(req.params.userId);
34
+ res.status(httpStatus.NO_CONTENT).send();
35
+ });
36
+
37
+ module.exports = {
38
+ createUser,
39
+ getUsers,
40
+ getUser,
41
+ updateUser,
42
+ deleteUser,
43
+ };
@@ -0,0 +1,92 @@
1
+ components:
2
+ schemas:
3
+ User:
4
+ type: object
5
+ properties:
6
+ id:
7
+ type: string
8
+ email:
9
+ type: string
10
+ format: email
11
+ name:
12
+ type: string
13
+ role:
14
+ type: string
15
+ enum: [user, admin]
16
+ example:
17
+ id: 5ebac534954b54139806c112
18
+ email: fake@example.com
19
+ name: fake name
20
+ role: user
21
+
22
+ Token:
23
+ type: object
24
+ properties:
25
+ token:
26
+ type: string
27
+ expires:
28
+ type: string
29
+ format: date-time
30
+ example:
31
+ token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1ZWJhYzUzNDk1NGI1NDEzOTgwNmMxMTIiLCJpYXQiOjE1ODkyOTg0ODQsImV4cCI6MTU4OTMwMDI4NH0.m1U63blB0MLej_WfB7yC2FTMnCziif9X8yzwDEfJXAg
32
+ expires: 2020-05-12T16:18:04.793Z
33
+
34
+ AuthTokens:
35
+ type: object
36
+ properties:
37
+ access:
38
+ $ref: '#/components/schemas/Token'
39
+ refresh:
40
+ $ref: '#/components/schemas/Token'
41
+
42
+ Error:
43
+ type: object
44
+ properties:
45
+ code:
46
+ type: number
47
+ message:
48
+ type: string
49
+
50
+ responses:
51
+ DuplicateEmail:
52
+ description: Email already taken
53
+ content:
54
+ application/json:
55
+ schema:
56
+ $ref: '#/components/schemas/Error'
57
+ example:
58
+ code: 400
59
+ message: Email already taken
60
+ Unauthorized:
61
+ description: Unauthorized
62
+ content:
63
+ application/json:
64
+ schema:
65
+ $ref: '#/components/schemas/Error'
66
+ example:
67
+ code: 401
68
+ message: Please authenticate
69
+ Forbidden:
70
+ description: Forbidden
71
+ content:
72
+ application/json:
73
+ schema:
74
+ $ref: '#/components/schemas/Error'
75
+ example:
76
+ code: 403
77
+ message: Forbidden
78
+ NotFound:
79
+ description: Not found
80
+ content:
81
+ application/json:
82
+ schema:
83
+ $ref: '#/components/schemas/Error'
84
+ example:
85
+ code: 404
86
+ message: Not found
87
+
88
+ securitySchemes:
89
+ bearerAuth:
90
+ type: http
91
+ scheme: bearer
92
+ bearerFormat: JWT
@@ -0,0 +1,21 @@
1
+ const { version } = require('../../package.json');
2
+ const config = require('../config/config');
3
+
4
+ const swaggerDef = {
5
+ openapi: '3.0.0',
6
+ info: {
7
+ title: 'Express Backend Starter JS',
8
+ version,
9
+ license: {
10
+ name: 'MIT',
11
+ url: 'https://github.com/Ahlyab/express-backend-starter-js/blob/master/LICENSE',
12
+ },
13
+ },
14
+ servers: [
15
+ {
16
+ url: `http://localhost:${config.port}/v1`,
17
+ },
18
+ ],
19
+ };
20
+
21
+ module.exports = swaggerDef;