diff --git a/package.json b/package.json index 0aba2d3..443ad26 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "@rondo.dev/test-utils": "file:packages/test-utils", "@rondo.dev/validator": "file:packages/validator", "@rondo.dev/db": "file:packages/db", - "@rondo.dev/db-typeorm": "file:packages/db-typeorm" + "@rondo.dev/db-typeorm": "file:packages/db-typeorm", + "@rondo.dev/middleware": "file:packages/middleware" }, "devDependencies": { "@types/bcrypt": "^3.0.0", @@ -104,4 +105,4 @@ "watchify": "^3.11.1" }, "name": "node" -} +} \ No newline at end of file diff --git a/packages/middleware/README.md b/packages/middleware/README.md new file mode 100644 index 0000000..e9cb5f3 --- /dev/null +++ b/packages/middleware/README.md @@ -0,0 +1,7 @@ +# @rondo.dev/middleware + +This package provides a framework-agnostic way to define middlewares for any +framework by using the Node's `http.IncomingMessage` and `http.ServerResponse` +types. + +See tests for more information. diff --git a/packages/middleware/jest.config.js b/packages/middleware/jest.config.js new file mode 100644 index 0000000..4c0cf86 --- /dev/null +++ b/packages/middleware/jest.config.js @@ -0,0 +1,16 @@ +module.exports = { + roots: [ + '/src', + ], + transform: { + '^.+\\.tsx?$': 'ts-jest', + }, + testRegex: '(/__tests__/.*|\\.(test|spec))\\.tsx?$', + moduleFileExtensions: [ + 'ts', + 'tsx', + 'js', + 'jsx', + ], + setupFiles: ['/jest.setup.js'], +} diff --git a/packages/middleware/jest.setup.js b/packages/middleware/jest.setup.js new file mode 100644 index 0000000..a952c9b --- /dev/null +++ b/packages/middleware/jest.setup.js @@ -0,0 +1,4 @@ +if (!process.env.LOG) { + process.env.LOG = 'sql:warn' +} +process.chdir(__dirname) diff --git a/packages/middleware/package-lock.json b/packages/middleware/package-lock.json new file mode 100644 index 0000000..ec23051 --- /dev/null +++ b/packages/middleware/package-lock.json @@ -0,0 +1,4 @@ +{ + "name": "@rondo.dev/middleware", + "lockfileVersion": 1 +} diff --git a/packages/middleware/package.json b/packages/middleware/package.json new file mode 100644 index 0000000..40bd941 --- /dev/null +++ b/packages/middleware/package.json @@ -0,0 +1,14 @@ +{ + "name": "@rondo.dev/middleware", + "private": true, + "scripts": { + "test": "jest", + "lint": "tslint --project .", + "compile": "tsc", + "clean": "rm -rf lib/" + }, + "dependencies": {}, + "main": "lib/index.js", + "module": "esm/index.js", + "types": "lib/index.d.ts" +} diff --git a/packages/middleware/src/Context.ts b/packages/middleware/src/Context.ts new file mode 100644 index 0000000..612f217 --- /dev/null +++ b/packages/middleware/src/Context.ts @@ -0,0 +1,6 @@ +import { IncomingMessage, ServerResponse } from 'http' + +export type Context = { + req: IncomingMessage + res: ServerResponse +} diff --git a/packages/middleware/src/Middleware.ts b/packages/middleware/src/Middleware.ts new file mode 100644 index 0000000..afe7b14 --- /dev/null +++ b/packages/middleware/src/Middleware.ts @@ -0,0 +1,3 @@ +import { Context } from './Context' + +export type Middleware = (ctx: C) => unknown diff --git a/packages/middleware/src/createMiddleware.ts b/packages/middleware/src/createMiddleware.ts new file mode 100644 index 0000000..55c8cef --- /dev/null +++ b/packages/middleware/src/createMiddleware.ts @@ -0,0 +1,7 @@ +import { Context } from './Context' + +export const createMiddleware = + (fn: (ctx: C) => unknown) => async (ctx: C) => { + await fn(ctx) + return undefined + } diff --git a/packages/middleware/src/expressify.test.ts b/packages/middleware/src/expressify.test.ts new file mode 100644 index 0000000..5a69e7e --- /dev/null +++ b/packages/middleware/src/expressify.test.ts @@ -0,0 +1,49 @@ +import express from 'express' +import { expressify } from './expressify' +import request from 'supertest' +import { createMiddleware } from './createMiddleware' + +describe('expressify', () => { + + describe('middleware', () => { + it('acts as a middleware when returned value is undefined', async () => { + const app = express() + app.use( + expressify( + createMiddleware(ctx => (ctx.req as any).id = 'test'))) + app.get('/test', (req, res) => res.json({ id: (req as any).id })) + await request(app) + .get('/test') + .expect(200) + .expect('{"id":"test"}') + }) + }) + + describe('response', () => { + it('sends a response', async () => { + const app = express() + app.use( + expressify( + createMiddleware(ctx => (ctx.req as any).id = 'test'))) + app.get('/test', expressify(ctx => (ctx.req as any).id)) + request(app) + .get('/test') + .expect(200) + .expect('"test"') + }) + + it('can send a response using a custom fn', () => { + const app = express() + app.use( + expressify( + createMiddleware(ctx => (ctx.req as any).id = 'test'))) + app.get('/test', + expressify(ctx => (ctx.req as any).id, (res, value) => res.send(value))) + request(app) + .get('/test') + .expect(200) + .expect('test') + }) + }) + +}) diff --git a/packages/middleware/src/expressify.ts b/packages/middleware/src/expressify.ts new file mode 100644 index 0000000..a710229 --- /dev/null +++ b/packages/middleware/src/expressify.ts @@ -0,0 +1,28 @@ +import { NextFunction, Request, Response } from 'express' +import { IncomingMessage, ServerResponse } from 'http' +import { Middleware } from './Middleware' + +export const expressify = ( + handleMiddleware: Middleware, + sendResponse: (res: Response, result: unknown) => void = + (res, result) => res.json(result), +) => async ( + req: Request, + res: Response, + next: NextFunction, +) => { + let result: unknown + try { + const r: IncomingMessage = req + const rr: ServerResponse = res + result = await handleMiddleware({req: r, res: rr}) + } catch (err) { + next(err) + return + } + if (result === undefined) { + next() + return + } + sendResponse(res, result) +} diff --git a/packages/middleware/src/index.ts b/packages/middleware/src/index.ts new file mode 100644 index 0000000..1a5f2b5 --- /dev/null +++ b/packages/middleware/src/index.ts @@ -0,0 +1,3 @@ +export * from './Context' +export * from './expressify' +export * from './Middleware' diff --git a/packages/middleware/tsconfig.esm.json b/packages/middleware/tsconfig.esm.json new file mode 100644 index 0000000..6e2aaa7 --- /dev/null +++ b/packages/middleware/tsconfig.esm.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "esm" + }, + "references": [ + ] +} diff --git a/packages/middleware/tsconfig.json b/packages/middleware/tsconfig.json new file mode 100644 index 0000000..94e864b --- /dev/null +++ b/packages/middleware/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../tsconfig.common.json", + "compilerOptions": { + "outDir": "lib", + "rootDir": "src" + }, + "references": [ + ] +}