the statusphere demo reworked into a vite/react app in a monorepo

more tidy

dholms 19149b18 d6ebb4ef

+39 -134
-45
src/common/__tests__/errorHandler.test.ts
··· 1 - import express, { type Express } from "express"; 2 - import { StatusCodes } from "http-status-codes"; 3 - import request from "supertest"; 4 - 5 - import errorHandler from "#/common/middleware/errorHandler"; 6 - 7 - describe("Error Handler Middleware", () => { 8 - let app: Express; 9 - 10 - beforeAll(() => { 11 - app = express(); 12 - 13 - app.get("/error", () => { 14 - throw new Error("Test error"); 15 - }); 16 - app.get("/next-error", (_req, _res, next) => { 17 - const error = new Error("Error passed to next()"); 18 - next(error); 19 - }); 20 - 21 - app.use(errorHandler()); 22 - app.use("*", (req, res) => res.status(StatusCodes.NOT_FOUND).send("Not Found")); 23 - }); 24 - 25 - describe("Handling unknown routes", () => { 26 - it("returns 404 for unknown routes", async () => { 27 - const response = await request(app).get("/this-route-does-not-exist"); 28 - expect(response.status).toBe(StatusCodes.NOT_FOUND); 29 - }); 30 - }); 31 - 32 - describe("Handling thrown errors", () => { 33 - it("handles thrown errors with a 500 status code", async () => { 34 - const response = await request(app).get("/error"); 35 - expect(response.status).toBe(StatusCodes.INTERNAL_SERVER_ERROR); 36 - }); 37 - }); 38 - 39 - describe("Handling errors passed to next()", () => { 40 - it("handles errors passed to next() with a 500 status code", async () => { 41 - const response = await request(app).get("/next-error"); 42 - expect(response.status).toBe(StatusCodes.INTERNAL_SERVER_ERROR); 43 - }); 44 - }); 45 - });
-52
src/common/__tests__/requestLogger.test.ts
··· 1 - import express from "express"; 2 - import { StatusCodes } from "http-status-codes"; 3 - import request from "supertest"; 4 - 5 - import errorHandler from "#/common/middleware/errorHandler"; 6 - import requestLogger from "#/common/middleware/requestLogger"; 7 - 8 - describe("Request Logger Middleware", () => { 9 - const app = express(); 10 - 11 - beforeAll(() => { 12 - app.use(requestLogger); 13 - app.get("/success", (req, res) => res.status(StatusCodes.OK).send("Success")); 14 - app.get("/redirect", (req, res) => res.redirect("/success")); 15 - app.get("/error", () => { 16 - throw new Error("Test error"); 17 - }); 18 - app.use(errorHandler()); 19 - }); 20 - 21 - describe("Successful requests", () => { 22 - it("logs successful requests", async () => { 23 - const response = await request(app).get("/success"); 24 - expect(response.status).toBe(StatusCodes.OK); 25 - }); 26 - 27 - it("checks existing request id", async () => { 28 - const requestId = "test-request-id"; 29 - const response = await request(app).get("/success").set("X-Request-Id", requestId); 30 - expect(response.status).toBe(StatusCodes.OK); 31 - }); 32 - }); 33 - 34 - describe("Re-directions", () => { 35 - it("logs re-directions correctly", async () => { 36 - const response = await request(app).get("/redirect"); 37 - expect(response.status).toBe(StatusCodes.MOVED_TEMPORARILY); 38 - }); 39 - }); 40 - 41 - describe("Error handling", () => { 42 - it("logs thrown errors with a 500 status code", async () => { 43 - const response = await request(app).get("/error"); 44 - expect(response.status).toBe(StatusCodes.INTERNAL_SERVER_ERROR); 45 - }); 46 - 47 - it("logs 404 for unknown routes", async () => { 48 - const response = await request(app).get("/unknown"); 49 - expect(response.status).toBe(StatusCodes.NOT_FOUND); 50 - }); 51 - }); 52 - });
-15
src/common/middleware/rateLimiter.ts
··· 1 - import type { Request } from "express"; 2 - import { rateLimit } from "express-rate-limit"; 3 - 4 - import { env } from "#/common/utils/envConfig"; 5 - 6 - const rateLimiter = rateLimit({ 7 - legacyHeaders: true, 8 - limit: env.COMMON_RATE_LIMIT_MAX_REQUESTS, 9 - message: "Too many requests, please try again later.", 10 - standardHeaders: true, 11 - windowMs: 15 * 60 * env.COMMON_RATE_LIMIT_WINDOW_MS, 12 - keyGenerator: (req: Request) => req.ip as string, 13 - }); 14 - 15 - export default rateLimiter;
-19
src/router.ts
··· 1 - import express from "express"; 2 - import type { AppContext } from "#/config"; 3 - 4 - export const createRouter = (ctx: AppContext) => { 5 - const router = express.Router(); 6 - 7 - router.get("/", async (req, res) => { 8 - const posts = await ctx.db 9 - .selectFrom("post") 10 - .selectAll() 11 - .orderBy("indexedAt", "desc") 12 - .limit(10) 13 - .execute(); 14 - const postTexts = posts.map((row) => row.text); 15 - res.json(postTexts); 16 - }); 17 - 18 - return router; 19 - };
+23
src/routes/index.ts
··· 1 + import express from "express"; 2 + import type { AppContext } from "#/config"; 3 + import { handler } from "./util"; 4 + 5 + export const createRouter = (ctx: AppContext) => { 6 + const router = express.Router(); 7 + 8 + router.get( 9 + "/", 10 + handler(async (req, res) => { 11 + const posts = await ctx.db 12 + .selectFrom("post") 13 + .selectAll() 14 + .orderBy("indexedAt", "desc") 15 + .limit(10) 16 + .execute(); 17 + const postTexts = posts.map((row) => row.text); 18 + res.json(postTexts); 19 + }) 20 + ); 21 + 22 + return router; 23 + };
+15
src/routes/util.ts
··· 1 + import type express from "express"; 2 + 3 + export const handler = 4 + (fn: express.Handler) => 5 + async ( 6 + req: express.Request, 7 + res: express.Response, 8 + next: express.NextFunction 9 + ) => { 10 + try { 11 + await fn(req, res, next); 12 + } catch (err) { 13 + next(err); 14 + } 15 + };
+1 -3
src/server.ts
··· 6 6 import { pino } from "pino"; 7 7 8 8 import errorHandler from "#/common/middleware/errorHandler"; 9 - import rateLimiter from "#/common/middleware/rateLimiter"; 10 9 import requestLogger from "#/common/middleware/requestLogger"; 11 10 import { env } from "#/common/utils/envConfig"; 12 11 import { createDb, migrateToLatest } from "#/db"; 13 12 import { Firehose } from "#/firehose"; 14 - import { createRouter } from "#/router"; 13 + import { createRouter } from "#/routes"; 15 14 import type { AppContext } from "./config"; 16 15 17 16 export class Server { ··· 48 47 app.use(express.urlencoded({ extended: true })); 49 48 app.use(cors({ origin: env.CORS_ORIGIN, credentials: true })); 50 49 app.use(helmet()); 51 - app.use(rateLimiter); 52 50 53 51 // Request logging 54 52 app.use(requestLogger);