/** * Configurable request context helpers for Fastify product backends. * * Factory function creates getRequestProductId() and getUserId() bound to * the provided product ID, eliminating hardcoded product IDs in each repo. */ import type { FastifyRequest } from 'fastify'; import { BadRequestError } from '@bytelyst/errors'; import type { RequestContextOptions } from './types.js'; export function createRequestContext(opts: RequestContextOptions) { const { productId } = opts; /** * Extract productId from request. Validates against this backend's product ID. * Falls back to the configured productId since this is a product-specific backend. */ function getRequestProductId(req: FastifyRequest): string { // 1. From JWT const jwtPid = req.jwtPayload?.productId; if (jwtPid && jwtPid !== productId) { throw new BadRequestError(`Invalid productId: expected ${productId}, got ${jwtPid}`); } // 2. From header const header = req.headers['x-product-id']; if (typeof header === 'string' && header.length > 0 && header !== productId) { throw new BadRequestError(`Invalid productId: expected ${productId}, got ${header}`); } return productId; } /** * Extract userId from the JWT payload on the request. * Throws BadRequestError if no authenticated user is found. */ function getUserId(req: FastifyRequest): string { const sub = req.jwtPayload?.sub; if (!sub) { throw new BadRequestError('Missing userId — request must be authenticated'); } return sub; } return { getRequestProductId, getUserId }; }