Skip to content

Instantly share code, notes, and snippets.

@hos
Created March 20, 2023 18:58
Show Gist options
  • Save hos/2b5db899ed66276174e58648b042d6b0 to your computer and use it in GitHub Desktop.
Save hos/2b5db899ed66276174e58648b042d6b0 to your computer and use it in GitHub Desktop.
import cors from "cors";
import express, { Express, NextFunction } from "express";
import { IncomingMessage, Server, ServerResponse } from "http";
import inspector from "inspector";
import { Duplex } from "stream";
import { isDev, isLocal } from "./config.js";
import * as middleware from "./middleware/index.js";
import { firebaseAuthMiddleware } from "./middleware/installFirebaseAuth.js";
import { makeShutdownActions, ShutdownAction } from "./shutdownActions.js";
// Server may not always be supplied, e.g. where mounting on a sub-route
export function getHttpServer(app: Express): Server | null {
return app.get("httpServer") ?? null;
}
export function getShutdownActions(app: Express): ShutdownAction[] {
return app.get("shutdownActions");
}
type UpgradeHandlers = Array<{
name: string;
check: (req: IncomingMessage, socket: Duplex, head: Buffer) => boolean | Promise<boolean>;
upgrade: (req: IncomingMessage, socket: Duplex, head: Buffer) => void;
}>;
export function getUpgradeHandlers(app: Express): UpgradeHandlers {
return app.get("upgradeHandlers");
}
export function getWebsocketMiddlewares(app: Express): Middleware<express.Request, express.Response>[] {
return app.get("websocketMiddlewares");
}
export interface Middleware<Request extends IncomingMessage, Response extends ServerResponse> {
(req: Request, res: Response, next: NextFunction): void;
}
export async function makeApp({
httpServer,
}: {
httpServer?: Server;
} = {}): Promise<Express> {
const shutdownActions = makeShutdownActions();
if (isDev || isLocal) {
shutdownActions.push(() => {
inspector.close();
});
}
/*
* Our Express server
*/
const app = express();
app.use(cors({ origin: true }));
/*
* Getting access to the HTTP server directly means that we can do things
* with websockets if we need to (e.g. GraphQL subscriptions).
*/
app.set("httpServer", httpServer);
/*
* For a clean nodemon shutdown, we need to close all our sockets otherwise
* we might not come up cleanly again (inside nodemon).
*/
app.set("shutdownActions", shutdownActions);
/*
* Since multiple things in our server might want to handle websockets and
* this is handled in node via the 'upgrade' event which should have one handler
* only, we need a centralised location to listen for upgrade events that then
* decides which handler to dispatch the event to. This array stores these
* handlers.
*/
const upgradeHandlers: UpgradeHandlers = [];
app.set("upgradeHandlers", upgradeHandlers);
/*
* When we're using websockets, we may want them to have access to
* sessions/etc for authentication.
*/
const websocketMiddlewares: Middleware<express.Request, express.Response>[] = [firebaseAuthMiddleware];
app.set("websocketMiddlewares", websocketMiddlewares);
/*
* Middleware is installed from the /server/middleware directory. These
* helpers may augment the express app with new settings and/or install
* express middleware. These helpers may be asynchronous, but they should
* operate very rapidly to enable quick as possible server startup.
*/
await middleware.installHealth(app);
await middleware.installDatabasePools(app);
await middleware.installWorkerUtils(app);
await middleware.installGraphQLClient(app);
await middleware.installSameOrigin(app);
await middleware.installFirebaseAuth(app);
await middleware.installPostGraphile(app);
/*
* Error handling middleware
*/
await middleware.installErrorHandler(app);
return app;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment