Trough Middleware Pipeline
`trough` is a lightweight, promise-aware middleware utility designed for building flexible processing pipelines in JavaScript and TypeScript environments. Currently at version 2.2.0, it is actively maintained with a consistent release cadence for minor improvements and bug fixes, typically addressing specific use cases or compatibility enhancements. Unlike some traditional middleware solutions, `trough` allows each stage of the pipeline to modify the input for subsequent stages and seamlessly integrates both synchronous and asynchronous functions, including those that return promises or use Node.js-style callbacks. Its core differentiator lies in its minimal API and explicit control over data flow, making it suitable for scenarios like plugin systems or data transformation pipelines. It is an ESM-only package, targeting modern Node.js (v16+) and browser environments via `esm.sh`.
Common errors
-
ERR_REQUIRE_ESM
cause Attempting to use `require('trough')` in a CommonJS module or an environment that strictly enforces ESM.fixConvert your module to ESM or update your tooling configuration to handle ESM imports. Use `import { trough } from 'trough'` instead. -
TypeError: (0, trough_1.trough) is not a function
cause Incorrect import syntax, often seen when TypeScript compiles ESM to CommonJS, or when mixing default/named import styles with an ESM-only package.fixEnsure your import statement matches `import { trough } from 'trough'` and your TypeScript `tsconfig.json` `module` option is set to `ESNext` or `Node16` if targeting modern Node.js. -
ReferenceError: process is not defined
cause A middleware function uses Node.js-specific globals or modules (like `process`, `fs`, `path`) but the code is being executed in a browser environment.fixRefactor the problematic middleware to use browser-compatible APIs or ensure it's only run in a Node.js context. For shared code, abstract platform-specific logic. -
Error: Expected file
cause This is an example of a custom error thrown by a middleware function due to specific application logic (e.g., input validation, file type checks) that failed within the pipeline.fixReview the middleware that threw the error (in this case, the one checking `ctx.stats.isFile()`) and ensure the input to the pipeline, or the logic within the middleware, handles all expected cases. Implement robust error handling and input validation.
Warnings
- breaking `trough` transitioned to an ESM-only package in version 2.0.0. All CommonJS `require()` statements will fail, necessitating a migration to `import` syntax.
- breaking Version 2.0.1 fixed a regression from v2.0.0 where incorrect values could be passed between middleware functions in the pipeline.
- gotcha Asynchronous middleware functions must explicitly signal completion by either returning a Promise (or a thenable) or calling the `next` (or `done`) callback provided as the last argument. Failing to do so will cause the pipeline to proceed prematurely.
- gotcha When using `trough` in a browser environment, be mindful that any middleware functions relying on Node.js-specific APIs (e.g., `fs`, `path`, `process`) will fail.
- gotcha While v2.1.0 added support for the `this` context within middleware functions when using the `wrap` utility, developers should still be cautious about how `this` is bound, especially when passing methods as middleware. Arrow functions may capture `this` from their lexical scope.
Install
-
npm install trough -
yarn add trough -
pnpm add trough
Imports
- trough
const trough = require('trough')import { trough } from 'trough' - wrap
const { wrap } = require('trough')import { wrap } from 'trough' - Pipeline
import type { Pipeline } from 'trough' - Middleware
import type { Middleware } from 'trough'
Quickstart
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import {trough} from 'trough'
const pipeline = trough()
.use(function (fileName) {
console.log('Checking… ' + fileName)
})
.use(function (fileName) {
return path.join(process.cwd(), fileName)
})
.use(function (filePath, next) {
// Asynchronous middleware uses a callback
fs.stat(filePath, function (error, stats) {
next(error, {filePath, stats})
})
})
.use(async function (ctx) {
// Or use async/await for Promises
if (ctx.stats.isFile()) {
return new Promise((resolve, reject) => {
fs.readFile(ctx.filePath, (err, data) => {
if (err) reject(err); else resolve(data)
})
})
} else {
throw new Error('Expected file')
}
})
// Example usage with a valid file and an invalid path
pipeline.run('readme.md', console.log)
pipeline.run('node_modules', console.log)