Home

Published

- 2 min read

Passing Objects to API Request Handlers with ExpressJS

img of Passing Objects to API Request Handlers with ExpressJS

Loggers, DALs, and more

Modular, maintainable, extensible pattern for accessing objects without import.

The Problem

You have a need to access an object in multiple API request handlers, but don’t want to create a singleton (especially in JS) and import it everywhere.

The Solution

The solution is a pattern I’m calling Request Injection Middleware.

   // <root>/index.ts
const localNeo4JDriver: Neo4JDriver = new Neo4JDriver(
    NEO4J_URI,
    NEO4J_UNAME,
    NEO4J_PW
);
localNeo4JDriver.initializeDriver();
...
const app: Express = express();
...
app.use((req, _, next) => {
    try {
        req.n4jDriver = localNeo4JDriver.getDriver()!;
    } catch (e) {
        log.error(e);
        process.exit(1);
    }
    next();
});

The Neo4JDriver class is used to instantiate a driver when the server starts for use during request handling. Since request handlers don’t seem to have proper access to app-level objects (they’re supposed to, but app.set() seems to only store strings properly and not objects), I decided to just inject my objects into the request object using a middleware lambda. They can then be accessed in the request handler.

   // Register/index.ts
// router object for the register endpoint
router
    .post("/", async (req: Request, res: Response) => {
        const neo4jDriver: Driver = req.n4jDriver;

This way I don’t have to mess with JS singletons. I’m going to want a bunch of Neo4JDrivers spun up dynamically for federation purposes, so creating a singleton is not ideal.

To alter the Request object to permit the new parameters, add this to the environment.d.ts:

   import { Driver } from 'neo4j-driver'
declare global {
	namespace Express {
		export interface Request {
			n4jDriver: Driver
		}
	}
}
export {}

Happy coding!

Neo4JDriver class

For reference, here is the Neo4JDriver class.

   export class Neo4JDriver {
	private driver: Driver | null = null

	constructor(
		readonly uri: string,
		readonly uname: string,
		private pw: string
	) {}

	async initializeDriver(): Promise<boolean> {
		try {
			this.driver = neo4j.driver(this.uri, neo4j.auth.basic(this.uname, this.pw))
			const serverInfo = await this.driver.getServerInfo()
			log.info('Neo4J connection established')
			log.info(serverInfo)
		} catch (err) {
			if (typeof err === 'string') {
				log.error(`Neo4J connection error: ${err}`)
			} else if (err instanceof Neo4jError) {
				log.error(`Neo4J connection error: ${err}\nCause:${err?.cause}`)
			} else {
				log.error(`Neo4J connection error: ${JSON.stringify(err)}`)
			}
			return false
		}
		return true
	}

	getDriver(): Driver | null {
		return this.driver
	}
}