Schotsl
Schotsl

Reputation: 227

Pass object to Express export function

I'm working (and learning) on my TypeScript skills, although I ran into a problem: I have a class named Manager which contains and manages multiple 'sub' managers. In the index file, I load the Manager by creating an instance and calling the load function. When loading all 'sub' managers get a reference to the main/only Manager instance, this way they can call/use the other 'sub' managers.

But I would like to be able to get some info from the 'sub' managers at a REST API endpoint. These endpoints are loaded through routes:

index.ts

import "reflect-metadata";

import { createConnection } from "typeorm";
import { Request, Response } from "express";

import * as express from "express";
import * as bodyParser from "body-parser";

import { AppRoutes } from "./routes";
import { Manager } from "./manager";

createConnection().then(async (typeORMConnection) => {
    const manager = new Manager();

    manager.load().then(() => {
        console.log("Manager has loaded all managers");

        const expressApp = express();

        expressApp.use(bodyParser.json());
        expressApp.use(function(req, res, next) {
        res.header("Access-Control-Allow-Origin", "*");
        res.header("Access-Control-Allow-Methods", "*");
        res.header("Access-Control-Allow-Headers", "*");
        next();
        });

        // Loop over every route
        AppRoutes.forEach((singleRoute) => {

        // Generate Express route
        expressApp[singleRoute.method](singleRoute.path, (request: Request, response: Response, next: Function) => {
            singleRoute.action(request, response)
                .then(() => next())
                .catch((error) => next(error));
            });
        });

        // Start Express app
        expressApp.listen(3000);

        console.log("Express application is up and running on port 3000");
    });
}).catch((error) => console.log(`TypeORM connection error: ${error}`));

A route file looks like this:

routes.ts

import { getSpeakerById, getSpeakerAll } from "./controller/get";
import { enableSpeakerById, disableSpeakerById } from "./controller/put";

export const AppRoutes = [
    {
        path: "/speaker",
        method: "get",
        action: getSpeakerAll
    },
    {
        path: "/speaker/:id",
        method: "get",
        action: getSpeakerById
    },
    {
        path: "/speaker/:id/disable",
        method: "put",
        action: disableSpeakerById
    },
    {
        path: "/speaker/:id/enable",
        method: "put",
        action: enableSpeakerById
    },
];

And last but not least this is an Express endpoint file containing the actual logic:

controller/get.ts

import { Request, Response } from "express";
import { getManager } from "typeorm";

import { Speaker } from "../entity/Speaker";

const ping = require("ping");

export async function getSpeakerById(request: Request, response: Response) {
    const speakerRepository = getManager().getRepository(Speaker);
    const speakerObject = await speakerRepository.findOne(request.params.id);

    // If no post is found return 404
    if (!speakerObject) {
        response.status(404);
        response.send("Speaker doesn't exist");
        response.end();
        return;
    }

    // Ping speaker and bind the time once its been resolved
    speakerObject.time = await ping.promise.probe(speakerObject.host);

    response.send(speakerObject);
}

export async function getSpeakerAll(request: Request, response: Response) {
    const speakerRepository = getManager().getRepository(Speaker);
    const speakerObjects = await speakerRepository.find();
    const speakerPromise = [];

    // Create a promise array of pings to all speakers
    speakerObjects.forEach((speakerObject) => speakerPromise.push(ping.promise.probe(speakerObject.host)));

    const speakerResults = await Promise.all(speakerPromise);

    // Since the promise array is based on order we can rebind the property by looping over it in the same order
    speakerResults.forEach((speakerResult, speakerIndex) => speakerObjects[speakerIndex].time = speakerResult.time);

    response.send(speakerObjects);
}

Now I need to access the main Manager instance in the controller/get.ts, but I can't pass it along as parameter (for as far as I know) since it's an export. I would just import the Manager class and create a new instance but I only want to start the Manager once since it contains logic such as intervals and speaker instances from the Sonos package. I hope I was able to explain the problem but if anyone needs clarification on something I'll update the post.

Upvotes: 0

Views: 361

Answers (1)

rh16
rh16

Reputation: 1073

You actually can pass it along as a parameter. There's nothing stopping you doing something like this in your index.ts:

// Generate Express route
expressApp[singleRoute.method](singleRoute.path, (request: Request, response: Response, next: Function) => {
    singleRoute.action(request, response, manager)
        .then(() => next())
        .catch((error) => next(error));
    });
});

And then updating the signature on you exported controller methods to be like

import { Manager } from '../manager';

export async function getSpeakerById(request: Request, response: Response, manager: Manager) {
    ...
}

Upvotes: 1

Related Questions