diff --git a/.nvmrc b/.nvmrc index 3c032078..209e3ef4 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18 +20 diff --git a/docs/docs/the-basics/coding-with-practica.md b/docs/docs/the-basics/coding-with-practica.md index 3ee4e804..b9beb3d3 100644 --- a/docs/docs/the-basics/coding-with-practica.md +++ b/docs/docs/the-basics/coding-with-practica.md @@ -135,18 +135,14 @@ This is a very typical express code, if you're familiar with express you'll be p ```javascript // A new route to edit order -router.put('/:id', async (req, res, next) => { - try { - logger.info(`Order API was called to edit order ${req.params.id}`); - // Later on we will call the main code in the domain layer - // Fow now let's put hard coded values - res.json({id:1, userId: 1, productId: 2, countryId: 1, - deliveryAddress: '123 Main St, New York', - paymentTermsInDays: 30}).status(200).end(); - } catch (err) { - next(err); - } - }); +router.put('/:id', wrapHandler(async (req, res, next) => { + logger.info(`Order API was called to edit order ${req.params.id}`); + // Later on we will call the main code in the domain layer + // Fow now let's put hard coded values + res.json({id:1, userId: 1, productId: 2, countryId: 1, + deliveryAddress: '123 Main St, New York', + paymentTermsInDays: 30}).status(200).end(); +})); ``` > **✅Best practice:** The API entry-point (controller) should stay thin and focus on forwarding the request to the domain layer. diff --git a/src/code-templates/.nvmrc b/src/code-templates/.nvmrc index 3c032078..209e3ef4 100644 --- a/src/code-templates/.nvmrc +++ b/src/code-templates/.nvmrc @@ -1 +1 @@ -18 +20 diff --git a/src/code-templates/libraries/logger/index.ts b/src/code-templates/libraries/logger/index.ts index 1d4a2e12..e0a90881 100644 --- a/src/code-templates/libraries/logger/index.ts +++ b/src/code-templates/libraries/logger/index.ts @@ -1,4 +1,4 @@ -import { context } from '@practica/request-context'; +import { context } from '@practica/middlewares'; import { Logger, LoggerConfiguration } from './definition'; import PinoLogger from './pino.logger'; diff --git a/src/code-templates/libraries/logger/package.json b/src/code-templates/libraries/logger/package.json index 13146d6f..de542bb7 100644 --- a/src/code-templates/libraries/logger/package.json +++ b/src/code-templates/libraries/logger/package.json @@ -15,7 +15,7 @@ "url": "git+https://github.com/practicajs/practica.git" }, "dependencies": { - "@practica/request-context": "^0.1.0", + "@practica/middlewares": "^0.1.0", "@practica/configuration-provider": "^0.0.1-alpha.1", "pino": "^8.5.0" }, diff --git a/src/code-templates/libraries/logger/test/index.test.ts b/src/code-templates/libraries/logger/test/index.test.ts index 4aa46a14..7ee73901 100644 --- a/src/code-templates/libraries/logger/test/index.test.ts +++ b/src/code-templates/libraries/logger/test/index.test.ts @@ -1,5 +1,5 @@ import sinon from 'sinon'; -import { context } from '@practica/request-context'; +import { context } from '@practica/middlewares'; import { logger } from '../index'; describe('logger', () => { diff --git a/src/code-templates/libraries/request-context/index.ts b/src/code-templates/libraries/middlewares/index.ts similarity index 80% rename from src/code-templates/libraries/request-context/index.ts rename to src/code-templates/libraries/middlewares/index.ts index 0299cd4c..fb44f7e1 100644 --- a/src/code-templates/libraries/request-context/index.ts +++ b/src/code-templates/libraries/middlewares/index.ts @@ -1,2 +1,3 @@ export { context } from './src/context'; export { addRequestId as addRequestIdExpressMiddleware } from './src/request-id/express/middleware'; +export * from './src/wrap-handler'; \ No newline at end of file diff --git a/src/code-templates/libraries/request-context/jest.config.js b/src/code-templates/libraries/middlewares/jest.config.js similarity index 100% rename from src/code-templates/libraries/request-context/jest.config.js rename to src/code-templates/libraries/middlewares/jest.config.js diff --git a/src/code-templates/libraries/request-context/package-lock.json b/src/code-templates/libraries/middlewares/package-lock.json similarity index 99% rename from src/code-templates/libraries/request-context/package-lock.json rename to src/code-templates/libraries/middlewares/package-lock.json index 15ad8f5f..1493a9f9 100644 --- a/src/code-templates/libraries/request-context/package-lock.json +++ b/src/code-templates/libraries/middlewares/package-lock.json @@ -1,5 +1,5 @@ { - "name": "@practica/request-context", + "name": "@practica/middlewares", "version": "0.1.0", "lockfileVersion": 2, "requires": true, diff --git a/src/code-templates/libraries/request-context/package.json b/src/code-templates/libraries/middlewares/package.json similarity index 95% rename from src/code-templates/libraries/request-context/package.json rename to src/code-templates/libraries/middlewares/package.json index f3a72689..e0952f76 100644 --- a/src/code-templates/libraries/request-context/package.json +++ b/src/code-templates/libraries/middlewares/package.json @@ -1,5 +1,5 @@ { - "name": "@practica/request-context", + "name": "@practica/middlewares", "version": "0.1.0", "description": "", "main": ".dist/index.js", diff --git a/src/code-templates/libraries/request-context/src/context.ts b/src/code-templates/libraries/middlewares/src/context.ts similarity index 100% rename from src/code-templates/libraries/request-context/src/context.ts rename to src/code-templates/libraries/middlewares/src/context.ts diff --git a/src/code-templates/libraries/request-context/src/request-id/constant.ts b/src/code-templates/libraries/middlewares/src/request-id/constant.ts similarity index 100% rename from src/code-templates/libraries/request-context/src/request-id/constant.ts rename to src/code-templates/libraries/middlewares/src/request-id/constant.ts diff --git a/src/code-templates/libraries/request-context/src/request-id/express/middleware.ts b/src/code-templates/libraries/middlewares/src/request-id/express/middleware.ts similarity index 100% rename from src/code-templates/libraries/request-context/src/request-id/express/middleware.ts rename to src/code-templates/libraries/middlewares/src/request-id/express/middleware.ts diff --git a/src/code-templates/libraries/middlewares/src/wrap-handler.ts b/src/code-templates/libraries/middlewares/src/wrap-handler.ts new file mode 100644 index 00000000..cbc16ea3 --- /dev/null +++ b/src/code-templates/libraries/middlewares/src/wrap-handler.ts @@ -0,0 +1,12 @@ + +import express from "express"; + +export function wrapHandler(handler: express.Handler) { + return async (req: express.Request, res: express.Response, next: express.NextFunction) => { + try { + await handler(req, res, next); + } catch (error) { + next(error); + } + }; +} \ No newline at end of file diff --git a/src/code-templates/libraries/request-context/test/request-context.test.ts b/src/code-templates/libraries/middlewares/test/request-context.test.ts similarity index 100% rename from src/code-templates/libraries/request-context/test/request-context.test.ts rename to src/code-templates/libraries/middlewares/test/request-context.test.ts diff --git a/src/code-templates/libraries/request-context/test/request-id-express-middleware.test.ts b/src/code-templates/libraries/middlewares/test/request-id-express-middleware.test.ts similarity index 100% rename from src/code-templates/libraries/request-context/test/request-id-express-middleware.test.ts rename to src/code-templates/libraries/middlewares/test/request-id-express-middleware.test.ts diff --git a/src/code-templates/libraries/request-context/tsconfig.json b/src/code-templates/libraries/middlewares/tsconfig.json similarity index 100% rename from src/code-templates/libraries/request-context/tsconfig.json rename to src/code-templates/libraries/middlewares/tsconfig.json diff --git a/src/code-templates/package-lock.json b/src/code-templates/package-lock.json index 8654282e..95f9ed57 100644 --- a/src/code-templates/package-lock.json +++ b/src/code-templates/package-lock.json @@ -137,7 +137,7 @@ "license": "ISC", "dependencies": { "@practica/configuration-provider": "^0.0.1-alpha.1", - "@practica/request-context": "^0.1.0", + "@practica/middlewares": "^0.1.0", "pino": "^8.5.0" }, "devDependencies": { @@ -271,8 +271,8 @@ } } }, - "libraries/request-context": { - "name": "@practica/request-context", + "libraries/middlewares": { + "name": "@practica/middlewares", "version": "0.1.0", "license": "ISC", "dependencies": { @@ -289,7 +289,7 @@ "typescript": "4.6.4" } }, - "libraries/request-context/node_modules/axios": { + "libraries/middlewares/node_modules/axios": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", @@ -299,6 +299,25 @@ "form-data": "^4.0.0" } }, + "libraries/request-context": { + "name": "@practica/middlewares", + "version": "0.1.0", + "extraneous": true, + "license": "ISC", + "dependencies": { + "jest": "^28.1.0", + "jest-sinon": "^1.0.4", + "sinon": "^14.0.0" + }, + "devDependencies": { + "@types/express": "^4.17.14", + "@types/node": "^17.0.33", + "axios": "^0.27.2", + "express": "^4.18.1", + "ts-jest": "^28.0.3", + "typescript": "4.6.4" + } + }, "libraries/validation": { "name": "@practica/validation", "version": "0.0.3", @@ -2245,8 +2264,8 @@ "resolved": "libraries/logger", "link": true }, - "node_modules/@practica/request-context": { - "resolved": "libraries/request-context", + "node_modules/@practica/middlewares": { + "resolved": "libraries/middlewares", "link": true }, "node_modules/@practica/validation": { @@ -4544,12 +4563,12 @@ } }, "node_modules/docker-compose": { - "version": "0.23.17", - "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.23.17.tgz", - "integrity": "sha512-YJV18YoYIcxOdJKeFcCFihE6F4M2NExWM/d4S1ITcS9samHKnNUihz9kjggr0dNtsrbpFNc7/Yzd19DWs+m1xg==", + "version": "0.24.7", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.24.7.tgz", + "integrity": "sha512-CdHl9n0S4+bl4i6MaxDQHNjqB1FdvuDirrDTzPKmdiMpheQqCjgsny0GZ2VhvN7qHTY0833lRlKWZgrkn1i6cg==", "dev": true, "dependencies": { - "yaml": "^1.10.2" + "yaml": "^2.2.2" }, "engines": { "node": ">= 6.0.0" @@ -13073,12 +13092,15 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", + "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", "dev": true, + "bin": { + "yaml": "bin.mjs" + }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/yargs": { @@ -13156,7 +13178,7 @@ "@practica/error-handling": "^0.0.3", "@practica/jwt-token-verifier": "^0.0.2", "@practica/logger": "^0.0.3", - "@practica/request-context": "^0.1.0", + "@practica/middlewares": "^0.1.0", "@practica/validation": "^0.0.3", "@prisma/client": "^4.6.1", "@sinclair/typebox": "^0.23.5", @@ -13180,7 +13202,7 @@ "@types/pg": "^8.6.5", "@types/sequelize": "^4.28.11", "@types/sinon": "^10.0.11", - "docker-compose": "^0.23.17", + "docker-compose": "^0.24.7", "is-ci": "^3.0.1", "is-port-reachable": "^3.0.0", "jest": "^27.5.1", @@ -14959,7 +14981,7 @@ "version": "file:libraries/logger", "requires": { "@practica/configuration-provider": "^0.0.1-alpha.1", - "@practica/request-context": "^0.1.0", + "@practica/middlewares": "^0.1.0", "@types/is-ci": "^3.0.0", "@types/jest": "^27.4.1", "jest": "^27.5.1", @@ -15043,8 +15065,8 @@ } } }, - "@practica/request-context": { - "version": "file:libraries/request-context", + "@practica/middlewares": { + "version": "file:libraries/middlewares", "requires": { "@types/express": "^4.17.14", "@types/node": "^17.0.33", @@ -16858,12 +16880,12 @@ } }, "docker-compose": { - "version": "0.23.17", - "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.23.17.tgz", - "integrity": "sha512-YJV18YoYIcxOdJKeFcCFihE6F4M2NExWM/d4S1ITcS9samHKnNUihz9kjggr0dNtsrbpFNc7/Yzd19DWs+m1xg==", + "version": "0.24.7", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.24.7.tgz", + "integrity": "sha512-CdHl9n0S4+bl4i6MaxDQHNjqB1FdvuDirrDTzPKmdiMpheQqCjgsny0GZ2VhvN7qHTY0833lRlKWZgrkn1i6cg==", "dev": true, "requires": { - "yaml": "^1.10.2" + "yaml": "^2.2.2" } }, "doctrine": { @@ -21435,7 +21457,7 @@ "@practica/error-handling": "^0.0.3", "@practica/jwt-token-verifier": "^0.0.2", "@practica/logger": "^0.0.3", - "@practica/request-context": "^0.1.0", + "@practica/middlewares": "^0.1.0", "@practica/validation": "^0.0.3", "@prisma/client": "^4.6.1", "@sinclair/typebox": "^0.23.5", @@ -21451,7 +21473,7 @@ "ajv": "^8.11.0", "amqplib": "^0.8.0", "axios": "^0.26.1", - "docker-compose": "^0.23.17", + "docker-compose": "^0.24.7", "express": "^4.17.3", "helmet": "^6.0.0", "is-ci": "^3.0.1", @@ -23445,9 +23467,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", + "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", "dev": true }, "yargs": { diff --git a/src/code-templates/services/order-service/entry-points/api/routes.ts b/src/code-templates/services/order-service/entry-points/api/routes.ts index c15f635b..4169fcd0 100644 --- a/src/code-templates/services/order-service/entry-points/api/routes.ts +++ b/src/code-templates/services/order-service/entry-points/api/routes.ts @@ -2,49 +2,41 @@ import util from 'util'; import express from 'express'; import { logger } from '@practica/logger'; import * as newOrderUseCase from '../../domain/new-order-use-case'; +import { wrapHandler } from '@practica/middlewares'; export default function defineRoutes(expressApp: express.Application) { const router = express.Router(); - router.post('/', async (req, res, next) => { - try { - logger.info( - `Order API was called to add new Order ${util.inspect(req.body)}` - ); - // ✅ Best Practice: Using the 3-tier architecture, routes/controller are kept thin, logic is encapsulated in a dedicated domain folder - const addOrderResponse = await newOrderUseCase.addOrder(req.body); - return res.json(addOrderResponse); - } catch (error) { - next(error); - return undefined; - } - }); + router.post('/', wrapHandler(async (req, res, next) => { + logger.info( + `Order API was called to add new Order ${util.inspect(req.body)}` + ); + // ✅ Best Practice: Using the 3-tier architecture, routes/controller are kept thin, logic is encapsulated in a dedicated domain folder + const addOrderResponse = await newOrderUseCase.addOrder(req.body); + res.json(addOrderResponse); + })); // get existing order by id - router.get('/:id', async (req, res, next) => { - try { - logger.info(`Order API was called to get user by id ${req.params.id}`); - const response = await newOrderUseCase.getOrder( - parseInt(req.params.id, 10) - ); - - if (!response) { - res.status(404).end(); - return; - } + router.get('/:id', wrapHandler(async (req, res, next) => { + logger.info(`Order API was called to get user by id ${req.params.id}`); + const response = await newOrderUseCase.getOrder( + parseInt(req.params.id, 10) + ); - res.json(response); - } catch (error) { - next(error); + if (!response) { + res.status(404).end(); + return; } - }); + + res.json(response); + })); // delete order by id - router.delete('/:id', async (req, res) => { + router.delete('/:id', wrapHandler(async (req, res) => { logger.info(`Order API was called to delete order ${req.params.id}`); await newOrderUseCase.deleteOrder(parseInt(req.params.id, 10)); res.status(204).end(); - }); + })); expressApp.use('/order', router); } diff --git a/src/code-templates/services/order-service/entry-points/api/server.ts b/src/code-templates/services/order-service/entry-points/api/server.ts index 124b1f01..4424514f 100644 --- a/src/code-templates/services/order-service/entry-points/api/server.ts +++ b/src/code-templates/services/order-service/entry-points/api/server.ts @@ -6,7 +6,7 @@ import helmet from 'helmet'; import { errorHandler } from '@practica/error-handling'; import * as configurationProvider from '@practica/configuration-provider'; import { jwtVerifierMiddleware } from '@practica/jwt-token-verifier'; -import { addRequestIdExpressMiddleware } from '@practica/request-context'; +import { addRequestIdExpressMiddleware } from '@practica/middlewares'; import configurationSchema from '../../config'; import defineRoutes from './routes'; diff --git a/src/code-templates/services/order-service/package.json b/src/code-templates/services/order-service/package.json index 42388e2d..fdfa5afa 100644 --- a/src/code-templates/services/order-service/package.json +++ b/src/code-templates/services/order-service/package.json @@ -24,7 +24,7 @@ "@practica/error-handling": "^0.0.3", "@practica/jwt-token-verifier": "^0.0.2", "@practica/logger": "^0.0.3", - "@practica/request-context": "^0.1.0", + "@practica/middlewares": "^0.1.0", "@practica/validation": "^0.0.3", "@prisma/client": "^4.6.1", "@sinclair/typebox": "^0.23.5", @@ -33,9 +33,9 @@ "axios": "^0.26.1", "express": "^4.17.3", "helmet": "^6.0.0", - "sequelize": "^6.17.0", "node-notifier": "^10.0.1", - "pg": "^8.7.3" + "pg": "^8.7.3", + "sequelize": "^6.17.0" }, "devDependencies": { "@jest-performance-reporter/core": "^2.1.2", @@ -48,7 +48,7 @@ "@types/pg": "^8.6.5", "@types/sequelize": "^4.28.11", "@types/sinon": "^10.0.11", - "docker-compose": "^0.23.17", + "docker-compose": "^0.24.7", "is-ci": "^3.0.1", "is-port-reachable": "^3.0.0", "jest": "^27.5.1", diff --git a/src/code-templates/services/order-service/test/global-setup.ts b/src/code-templates/services/order-service/test/global-setup.ts index cd196e1a..2d559cdf 100644 --- a/src/code-templates/services/order-service/test/global-setup.ts +++ b/src/code-templates/services/order-service/test/global-setup.ts @@ -1,6 +1,6 @@ import isPortReachable from 'is-port-reachable'; import path from 'path'; -import dockerCompose from 'docker-compose'; +import { v2 as dockerCompose } from 'docker-compose' import { execSync } from 'child_process'; export default async () => {