-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #140 from sliit-foss/feature/express-http-context
Feat!: initialized express-http-context package
- Loading branch information
Showing
7 changed files
with
608 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
{ | ||
"name": "@sliit-foss/express-http-context", | ||
"version": "0.0.0", | ||
"description": "A rework of the express-http-context package to use the new async_hooks API", | ||
"main": "dist/index.js", | ||
"browser": "dist/browser.js", | ||
"types": "types/index.d.ts", | ||
"scripts": { | ||
"build": "node ../../scripts/esbuild.config.js", | ||
"build:watch": "bash ../../scripts/esbuild.watch.sh", | ||
"bump-version": "bash ../../scripts/bump-version.sh --name=@sliit-foss/express-http-context", | ||
"lint": "bash ../../scripts/lint.sh", | ||
"release": "bash ../../scripts/release.sh", | ||
"test": "bash ../../scripts/test/test.sh" | ||
}, | ||
"devDependencies": { | ||
"express": "^4.16.2" | ||
}, | ||
"author": "SLIIT FOSS", | ||
"license": "MIT", | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/sliit-foss/npm-catalogue.git" | ||
}, | ||
"homepage": "https://github.com/sliit-foss/npm-catalogue/blob/main/packages/express-http-context/readme.md", | ||
"keywords": [ | ||
"express", | ||
"context", | ||
"http-context", | ||
"bun", | ||
"bun.js" | ||
], | ||
"bugs": { | ||
"url": "https://github.com/sliit-foss/npm-catalogue/issues" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# @sliit-foss/express-http-context | ||
|
||
#### A rework of the [express-http-context](https://www.npmjs.com/package/express-http-context) package to use the new async_hooks API <br><br> | ||
|
||
**Built as the existing package does not work alongside the [Bun](https://bun.sh/) runtime** | ||
|
||
--- | ||
|
||
Get and set request-scoped context anywhere. This is just an unopinionated, idiomatic ExpressJS implementation of [AsyncLocalStorage](https://nodejs.org/api/async_context.html#new-asynclocalstorage). It's a great place to store user state, claims from a JWT, request/correlation IDs, and any other request-scoped data. Context is preserved even over async/await. | ||
|
||
## How to use it | ||
|
||
Install: `bun install --save express-http-context` | ||
|
||
Use the middleware immediately before the first middleware that needs to have access to the context. | ||
You won't have access to the context in any middleware "used" before this one. | ||
|
||
Note that some popular middlewares (such as body-parser, express-jwt) may cause context to get lost. | ||
To workaround such issues, you are advised to use any third party middleware that does NOT need the context | ||
BEFORE you use this middleware. | ||
|
||
```js | ||
const express = require("express"); | ||
const httpContext = require("express-http-context"); | ||
|
||
const app = express(); | ||
// Use any third party middleware that does not need access to the context here, e.g. | ||
// app.use(some3rdParty.middleware); | ||
app.use(httpContext.middleware); | ||
// all code from here on has access to the same context for each request | ||
``` | ||
|
||
Set values based on the incoming request: | ||
|
||
```js | ||
// Example authorization middleware | ||
app.use((req, res, next) => { | ||
userService.getUser(req.get("Authorization"), (err, result) => { | ||
if (err) { | ||
next(err); | ||
} else { | ||
httpContext.set("user", result.user); | ||
next(); | ||
} | ||
}); | ||
}); | ||
``` | ||
|
||
Get them from code that doesn't have access to the express `req` object: | ||
|
||
```js | ||
const httpContext = require("express-http-context"); | ||
|
||
// Somewhere deep in the Todo Service | ||
function createTodoItem(title, content, callback) { | ||
const user = httpContext.get("user"); | ||
db.insert({ title, content, userId: user.id }, callback); | ||
} | ||
``` | ||
|
||
You can access the store directly as follows | ||
|
||
```js | ||
const store = require("express-http-context").store; | ||
``` | ||
|
||
## Troubleshooting | ||
|
||
For users of Node below version 14 | ||
|
||
1. Unfortunatly, this package does not work with any version of Node below 14. This is due to the fact that the AsyncLocalStorage API was introduced in Node 14. If you are using Node below version 14, you can use the original [express-http-context](https://www.npmjs.com/package/express-http-context) package but as of writing this, it will not work with [Bun](https://bun.sh/). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/* eslint no-unused-vars: 0 */ | ||
|
||
export const middleware = (req, res, next) => { | ||
throw new Error("`middleware` cannot be called from the browser code."); | ||
}; | ||
|
||
export const get = () => null; | ||
|
||
export const set = (key, value) => {}; | ||
|
||
export const store = null; | ||
|
||
export const ns = null; | ||
|
||
export default { | ||
middleware, | ||
get, | ||
set, | ||
store: null, | ||
ns: null | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
"use strict"; | ||
|
||
import { AsyncLocalStorage } from "async_hooks"; | ||
|
||
export const store = new AsyncLocalStorage(); | ||
|
||
/** Express.js middleware that is responsible for initializing the context for each request. */ | ||
export const middleware = (req, res, next) => { | ||
store.run({}, () => { | ||
next(); | ||
}); | ||
}; | ||
|
||
/** | ||
* Gets a value from the context by key. Will return undefined if the context has not yet been initialized for this request or if a value is not found for the specified key. | ||
* @param {string} key | ||
*/ | ||
export const get = (key) => { | ||
const storeData = store.getStore() || {}; | ||
return storeData[key]; | ||
}; | ||
|
||
/** | ||
* Adds a value to the context by key. If the key already exists, its value will be overwritten. No value will persist if the context has not yet been initialized. | ||
* @param {string} key | ||
* @param {*} value | ||
*/ | ||
export const set = (key, value) => { | ||
const storeData = store.getStore() || {}; | ||
storeData[key] = value; | ||
store.enterWith(storeData); | ||
}; | ||
|
||
export const ns = null; | ||
|
||
export default { | ||
middleware, | ||
get: get, | ||
set: set, | ||
store: store, | ||
ns: null | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { middleware, set, get } from "../src"; | ||
import { middleware as browserMiddleware, set as browserSet, get as browserGet } from "../src/browser"; | ||
|
||
describe("test store", () => { | ||
beforeAll(() => { | ||
middleware({}, {}, () => {}); | ||
}); | ||
test("set and retrieve a value", () => { | ||
set("foo", "bar"); | ||
expect(get("foo")).toBe("bar"); | ||
}); | ||
}); | ||
|
||
describe("test browser store", () => { | ||
beforeAll(() => { | ||
expect(() => browserMiddleware({}, {}, () => {})).toThrowError(); | ||
}); | ||
test("set and retrieve a value", () => { | ||
browserSet("foo", "bar"); | ||
expect(browserGet("foo")).toBe(null); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { Request, Response, NextFunction } from "express"; | ||
import { AsyncLocalStorage } from "async_hooks"; | ||
|
||
/** Express.js middleware that is responsible for initializing the context for each request. */ | ||
export declare function middleware(req: Request, res: Response, next: NextFunction): void; | ||
|
||
/** | ||
* Gets a value from the context by key. Will return undefined if the context has not yet been initialized for this request or if a value is not found for the specified key. | ||
*/ | ||
export declare function get(key: string): any; | ||
|
||
/** | ||
* Adds a value to the context by key. If the key already exists, its value will be overwritten. No value will persist if the context has not yet been initialized. | ||
*/ | ||
export declare function set(key: string, value: any): void; | ||
|
||
/** | ||
* Gets the underlying store. | ||
*/ | ||
export declare const store: AsyncLocalStorage<any>; | ||
|
||
/** | ||
* @deprecated Since `async_hooks` uses a store instead of namespaces, this is no longer available. This property will be removed in a future release. | ||
*/ | ||
export declare const ns: null; |
Oops, something went wrong.