Mount Express applications within your MooseStack project using the WebApp class. Express is the most popular Node.js web framework with a rich ecosystem of middleware.
WebApp for unified deployment and access to MooseStack utilities.import express from "express";import { WebApp, expressMiddleware, getMooseUtils } from "@514labs/moose-lib";import { MyTable } from "../tables/MyTable"; const app = express(); app.use(express.json());app.use(expressMiddleware()); // Required for Express app.get("/health", (req, res) => { res.json({ status: "ok" });}); app.get("/data", async (req, res) => { const moose = getMooseUtils(req); if (!moose) { return res.status(500).json({ error: "Moose utilities not available" }); } const { client, sql } = moose; const limit = parseInt(req.query.limit as string) || 10; try { const query = sql` SELECT ${MyTable.columns.id}, ${MyTable.columns.name}, ${MyTable.columns.createdAt} FROM ${MyTable} ORDER BY ${MyTable.columns.createdAt} DESC LIMIT ${limit} `; const result = await client.query.execute(query); const data = await result.json(); res.json(data); } catch (error) { res.status(500).json({ error: String(error) }); }}); export const expressApp = new WebApp("expressApp", app, { mountPath: "/express", metadata: { description: "Express API with custom middleware" }});Access your API:
GET http://localhost:4000/express/healthGET http://localhost:4000/express/data?limit=20Express applications must use expressMiddleware() to access Moose utilities:
import { expressMiddleware } from "@514labs/moose-lib";app.use(expressMiddleware());This middleware injects MooseStack utilities into the request object.
import express, { Request, Response, NextFunction } from "express";import { WebApp, expressMiddleware, getMooseUtils } from "@514labs/moose-lib";import { UserEvents } from "../tables/UserEvents";import { UserProfile } from "../tables/UserProfile"; const app = express(); // Middleware setupapp.use(express.json());app.use(expressMiddleware()); // Required! // Custom logging middlewareapp.use((req: Request, res: Response, next: NextFunction) => { console.log(`${req.method} ${req.path}`); next();}); // Error handling middlewareconst asyncHandler = (fn: Function) => (req: Request, res: Response, next: NextFunction) => { Promise.resolve(fn(req, res, next)).catch(next);}; // Health check endpointapp.get("/health", (req, res) => { res.json({ status: "ok", timestamp: new Date().toISOString() });}); // GET endpoint with query parametersapp.get("/users/:userId/events", asyncHandler(async (req: Request, res: Response) => { const moose = getMooseUtils(req); if (!moose) { return res.status(500).json({ error: "Moose utilities not available" }); } const { client, sql } = moose; const { userId } = req.params; const limit = parseInt(req.query.limit as string) || 10; const eventType = req.query.eventType as string; const cols = UserEvents.columns; const query = sql` SELECT ${cols.id}, ${cols.event_type}, ${cols.timestamp} FROM ${UserEvents} WHERE ${cols.user_id} = ${userId} ${eventType ? sql`AND ${cols.event_type} = ${eventType}` : sql``} ORDER BY ${cols.timestamp} DESC LIMIT ${limit} `; const result = await client.query.execute(query); const events = await result.json(); res.json({ userId, count: events.length, events });})); // POST endpointapp.post("/users/:userId/profile", asyncHandler(async (req: Request, res: Response) => { const moose = getMooseUtils(req); if (!moose) { return res.status(500).json({ error: "Moose utilities not available" }); } const { userId } = req.params; const { name, email } = req.body; // Validation if (!name || !email) { return res.status(400).json({ error: "Name and email are required" }); } // Handle POST logic here res.json({ success: true, userId, profile: { name, email } });})); // Protected endpoint with JWTapp.get("/protected", asyncHandler(async (req: Request, res: Response) => { const moose = getMooseUtils(req); if (!moose?.jwt) { return res.status(401).json({ error: "Unauthorized" }); } const userId = moose.jwt.sub; res.json({ message: "Authenticated", userId, claims: moose.jwt });})); // Error handlingapp.use((err: Error, req: Request, res: Response, next: NextFunction) => { console.error(err); res.status(500).json({ error: "Internal Server Error", message: err.message });}); // Register as WebAppexport const advancedExpressApp = new WebApp("advancedExpress", app, { mountPath: "/api/v1", metadata: { description: "Advanced Express API with routing and middleware" }});new WebApp(name, app, config)Parameters:
name (string): Unique identifier for your WebAppapp: Your Express application instanceconfig (WebAppConfig): Configuration objectWebAppConfig:
interface WebAppConfig { mountPath: string; // Required: URL path (e.g., "/api/v1") metadata?: { description?: string }; // Optional: Documentation metadata injectMooseUtils?: boolean; // Optional: Inject utilities (default: true)}import { getMooseUtils } from "@514labs/moose-lib"; app.get("/data", async (req, res) => { const moose = getMooseUtils(req); if (!moose) { return res.status(500).json({ error: "Utilities not available" }); } const { client, sql, jwt } = moose; // Use client and sql for database queries});Available utilities:
client: MooseClient for database queriessql: Template tag for safe SQL queriesjwt: Parsed JWT payload (when authentication is configured)Express middleware works seamlessly with MooseStack:
import helmet from "helmet";import compression from "compression";import rateLimit from "express-rate-limit"; const app = express(); // Securityapp.use(helmet()); // Compressionapp.use(compression()); // Rate limitingconst limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100 // limit each IP to 100 requests per windowMs});app.use(limiter); // Body parsingapp.use(express.json()); // Moose utilities (must be after body parsing)app.use(expressMiddleware());Organize routes using Express Router:
import { Router } from "express";import { getMooseUtils } from "@514labs/moose-lib";import { UserProfile } from "../../tables/UserProfile"; export const usersRouter = Router(); usersRouter.get("/:userId", async (req, res) => { const moose = getMooseUtils(req); if (!moose) { return res.status(500).json({ error: "Utilities not available" }); } const { client, sql } = moose; const { userId } = req.params; const query = sql` SELECT ${UserProfile.columns.id}, ${UserProfile.columns.name}, ${UserProfile.columns.email}, ${UserProfile.columns.createdAt} FROM ${UserProfile} WHERE ${UserProfile.columns.id} = ${userId} `; const result = await client.query.execute(query); const users = await result.json(); if (users.length === 0) { return res.status(404).json({ error: "User not found" }); } res.json(users[0]);});import express from "express";import { WebApp, expressMiddleware } from "@514labs/moose-lib";import { usersRouter } from "./routers/usersRouter"; const app = express(); app.use(express.json());app.use(expressMiddleware()); // Mount routersapp.use("/users", usersRouter); export const mainApp = new WebApp("mainApp", app, { mountPath: "/api", metadata: { description: "Main API with routers" }});app.get("/protected", async (req, res) => { const moose = getMooseUtils(req); if (!moose?.jwt) { return res.status(401).json({ error: "Unauthorized" }); } const userId = moose.jwt.sub; const userRole = moose.jwt.role; // Check permissions if (userRole !== "admin") { return res.status(403).json({ error: "Forbidden" }); } res.json({ message: "Authenticated", userId });});See Authentication documentation for JWT configuration.
getMooseUtils(req) returns a valueSolution: Ensure expressMiddleware() is added after body parsing:
app.use(express.json());app.use(expressMiddleware()); // Must come after body parsersSolution: The utilities may be undefined, always check:
const moose = getMooseUtils(req);if (!moose) { return res.status(500).json({ error: "Utilities not available" });}// Now moose.client, moose.sql are safely accessibleSolution: Ensure your mount path doesn't conflict with reserved paths:
/api, /admin, /consumption, /health, /ingest, /mcp/myapi, /v1, /custom