diff --git a/README.md b/README.md index 2d894e32b..906a63b4c 100644 --- a/README.md +++ b/README.md @@ -202,6 +202,23 @@ json-server -s ./static json-server -s ./static -s ./node_modules ``` +## Middleware + +You can add your middlewares from the CLI using `--middleware` option: + +```js +// hello.js +module.exports = (req, res, next) => { + res.header('X-Hello', 'World') + next() +} +``` + +```bash +json-server db.json --middleware ./hello.js +json-server db.json --middleware ./first.js ./second.js +``` + ## Notable differences with v0.17 - `id` is always a string and will be generated for you if missing diff --git a/src/app.ts b/src/app.ts index b8c5e79e7..3fa601985 100644 --- a/src/app.ts +++ b/src/app.ts @@ -16,6 +16,7 @@ const isProduction = process.env['NODE_ENV'] === 'production' export type AppOptions = { logger?: boolean static?: string[] + middlewares?: ((req: unknown, res: unknown, next: unknown) => void)[] } const eta = new Eta({ @@ -36,6 +37,9 @@ export function createApp(db: Low, options: AppOptions = {}) { ?.map((path) => (isAbsolute(path) ? path : join(process.cwd(), path))) .forEach((dir) => app.use(sirv(dir, { dev: !isProduction }))) + // Use middleware if specified + options.middlewares?.forEach(m => app.use(m)); + // CORS app .use((req, res, next) => { diff --git a/src/bin.ts b/src/bin.ts index 4633e5e43..c7b81e313 100644 --- a/src/bin.ts +++ b/src/bin.ts @@ -1,6 +1,6 @@ #!/usr/bin/env node import { existsSync, readFileSync, writeFileSync } from 'node:fs' -import { extname } from 'node:path' +import { extname, resolve } from 'node:path' import { parseArgs } from 'node:util' import chalk from 'chalk' @@ -19,11 +19,12 @@ function help() { console.log(`Usage: json-server [options] Options: - -p, --port Port (default: 3000) - -h, --host Host (default: localhost) - -s, --static Static files directory (multiple allowed) - --help Show this message - --version Show version number + -p, --port Port (default: 3000) + -h, --host Host (default: localhost) + -s, --static Static files directory (multiple allowed) + --middleware, -m Paths to middleware files (multiple allowed) + --help Show this message + --version Show version number `) } @@ -33,6 +34,7 @@ function args(): { port: number host: string static: string[] + middleware: string } { try { const { values, positionals } = parseArgs({ @@ -53,6 +55,12 @@ function args(): { multiple: true, default: [], }, + middleware: { + type: 'string', + short: 'm', + multiple: true, + default: [] + }, help: { type: 'boolean', }, @@ -100,6 +108,7 @@ function args(): { port: parseInt(values.port as string), host: values.host as string, static: values.static as string[], + middleware: values.middleware as string[], } } catch (e) { if ((e as NodeJS.ErrnoException).code === 'ERR_PARSE_ARGS_UNKNOWN_OPTION') { @@ -112,13 +121,27 @@ function args(): { } } -const { file, port, host, static: staticArr } = args() +const { file, port, host, static: staticArr, middleware: middlewarePaths } = args() if (!existsSync(file)) { console.log(chalk.red(`File ${file} not found`)) process.exit(1) } +// Load middlewares if specified +const middlewareFunctions = await Promise.all( + middlewarePaths.map(async p => { + const resolvedPath = resolve(process.cwd(), p) + if (!existsSync(resolvedPath)){ + console.error(`Middleware file not found: ${resolvedPath}`) + return process.exit(1) + } + console.log(chalk.gray(`Loading middleware from ${resolvedPath}`)) + const middlewareModule = await import(resolvedPath) + return middlewareModule.default || middlewareModule + }) +); + // Handle empty string JSON file if (readFileSync(file, 'utf-8').trim() === '') { writeFileSync(file, '{}') @@ -140,7 +163,11 @@ const db = new Low(observer, {}) await db.read() // Create app -const app = createApp(db, { logger: false, static: staticArr }) +const app = createApp(db, { + logger: false, + static: staticArr, + middlewares: middlewareFunctions, +}) function logRoutes(data: Data) { console.log(chalk.bold('Endpoints:'))