Koa Hot Reload Middleware

raw JSON →
1.0.1 verified Thu Apr 23 auth: no javascript maintenance

koa-reload-middleware is a utility for Koa applications designed to facilitate development by automatically reloading specific middleware modules upon file changes, without requiring a full server restart. This package is currently at version 1.0.1 and, based on its GitHub activity, appears to be in a maintenance state, stable for its specific use case but not under active, rapid development. Its key differentiator is its focused approach to hot-reloading individual Koa middleware, leveraging Node.js's dynamic `import()` for ESM modules. This is particularly useful for avoiding the overhead of tools like `nodemon` for situations where only specific parts of the server-side logic need to be reloaded. It primarily benefits development workflows by significantly reducing feedback loop times when iterating on API routes or other modular Koa middleware components.

error SyntaxError: Cannot use import statement outside a module
cause Attempting to use `import` syntax in a CommonJS module or a Node.js environment not configured for ESM.
fix
Add "type": "module" to your package.json file. Ensure your Node.js version supports ESM without flags (Node.js 14+). If using TypeScript, configure tsconfig.json with "module": "NodeNext" or "ESNext".
error Error: Cannot find module 'path/to/your-route' or TypeError: Path must be a string. Received undefined
cause The path provided to the dynamic `import()` within `reload()` is incorrect, relative to the wrong base, or points to a non-existent file.
fix
Double-check the middlewarePath variable. Use path.resolve(__dirname, 'your', 'route', 'file.ts') for absolute paths and ensure the file exists. Remember that dynamic import() requires full file extensions in ESM.
error TypeError: reload is not a function
cause Incorrect import of the `reload` function, likely trying to `require()` it as a default export in a CJS context or destructuring a non-existent named export.
fix
Ensure you are using import reload from 'koa-reload-middleware'; for ESM. If strictly using CommonJS, const { default: reload } = require('koa-reload-middleware'); *might* work but isn't the intended pattern, as the internal dynamic import won't work correctly.
gotcha This middleware is designed for development environments only. Using it in production can lead to unexpected behavior, performance overhead, or security vulnerabilities due to dynamic module loading and file system watching.
fix Ensure `koa-reload-middleware` is only included in development dependencies and is not bundled or enabled in production builds. Use environment variables (e.g., `process.env.NODE_ENV !== 'production'`) to conditionally apply it.
breaking The primary usage pattern relies on Node.js's dynamic `import()` statement, which means your Koa application must be configured as an ESM module (`"type": "module"` in `package.json`) or run with `--experimental-modules` flag (less common now).
fix Configure your project as an ESM module by adding `"type": "module"` to your `package.json`. If using TypeScript, ensure your `tsconfig.json` has `"module": "NodeNext"` or `"ESNext"` and `"target": "ES2022"` or higher to support top-level `await` and dynamic `import()`.
gotcha Dynamic `import()` in Node.js caches modules. To ensure a fresh load on every change, it's often necessary to add a cache-busting query parameter (e.g., `?update=${Date.now()}`) to the import path, especially for `.js` files without specific handling by `koa-reload-middleware` itself. While `koa-reload-middleware` intends to clear the cache, explicit cache busting is a robust fallback.
fix Modify your `reload` factory function to include a unique query parameter with the dynamic import: `reload(() => import( './my-route.js?update=' + Date.now()))`. This ensures Node.js treats each import as a new module.
gotcha The middleware only reloads the specific module passed to `reload()`. If your reloaded middleware has its own nested dependencies that also change, those nested dependencies might not be reloaded unless `koa-reload-middleware` explicitly clears their cache or they are also dynamically imported within the reloaded module's factory function.
fix Structure your development code such that the dynamically imported module is a single entry point for its logic, minimizing external `import`s that are not themselves dynamically imported. For complex scenarios, consider more comprehensive HMR solutions if this middleware's scope is too limited.
npm install koa-reload-middleware
yarn add koa-reload-middleware
pnpm add koa-reload-middleware

This quickstart demonstrates setting up a basic Koa server that uses `koa-reload-middleware` to hot-reload a route file. It shows how to define a dynamic import for the middleware and provides a simple dummy route file that can be modified to observe the live reloading functionality without server restarts. The `?update=${Date.now()}` cache-busting query parameter is added to ensure Node.js's module cache is properly bypassed on reloads, which is often crucial for dynamic imports in development.

import Koa from 'koa';
import reload from 'koa-reload-middleware';
import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// Dummy route file to be reloaded
// Save this as `routes/my-route.ts`
// --------------------------------
// import type { Context, Next } from 'koa';
// export default async (ctx: Context, next: Next) => {
//   const message = `Hello from reloaded route at ${new Date().toLocaleTimeString()}`;
//   ctx.body = message;
//   console.log(`[ROUTE] Served: ${message}`);
//   await next();
// };
// --------------------------------

const app = new Koa();
const PORT = process.env.PORT ?? 3000;

// The path to the middleware file to be reloaded
const middlewarePath = path.resolve(__dirname, 'routes', 'my-route.ts');

// Use the reload middleware. It takes a function that dynamically imports the target middleware.
// The dynamic import path must be relative to the caller or an absolute file URL.
app.use(reload(() => import(middlewarePath.replace(/\\/g, '/') + `?update=${Date.now()}`)));

// Fallback/catch-all middleware (optional)
app.use(async (ctx, next) => {
  if (!ctx.body) {
    ctx.body = 'Welcome to Koa! Route not matched or reloaded.';
  }
  await next();
});

app.listen(PORT, () => {
  console.log(`Koa server listening on http://localhost:${PORT}`);
  console.log(`Edit '${middlewarePath}' and refresh your browser to see changes.`);
});