Node.js HTTP Static File Streaming
The `send` library provides a low-level utility for streaming static files directly from the file system in Node.js, specifically designed for HTTP responses. It expertly handles features like partial content responses via Range headers, conditional-GET negotiation using If-Match, If-None-Match, If-Modified-Since, and ETag generation. It offers granular control over aspects such as caching (Cache-Control, maxAge, immutable), dotfile handling, and automatic file extension resolution. While a `1.x.x` series exists (up to 1.2.1), the most recent stable release is currently `0.19.2`, indicating a somewhat unconventional release cadence or a focus shift. It is a foundational component often leveraged by higher-level static file serving middleware like `serve-static` within web frameworks.
Common errors
-
TypeError: send is not a function
cause Attempting to destructure `send` from a `require` or `import` statement, or using `require('send')` in an ESM context where only default imports are allowed implicitly.fixUse `const send = require('send')` for CommonJS or `import send from 'send'` for ESM. The `send` package provides a default export, not named exports. -
Error: ENOENT: no such file or directory, stat '/path/to/my/app/myFile.txt'
cause The file specified by the `path` argument combined with the `root` option does not exist, or the `root` option is misconfigured, or the `path` argument is not URL-encoded.fixDouble-check the `root` option to ensure it points to the correct base directory. Verify that the `path` argument accurately reflects the URL path (which `send` resolves relative to `root`). Ensure the file actually exists on the filesystem and has correct permissions. Remember `path` is URL-encoded, not a raw filesystem path. -
Node.js: `require()` of ES Module ... not supported. Instead change the require to a dynamic import()
cause Trying to `require()` the `send` package in a CommonJS module when `send` itself might be compiled as an ES Module in newer versions, or vice versa, `import`ing a CJS module in an ESM context without proper handling.fixIf your project is CommonJS, ensure `send` is installed as a CJS-compatible version or configure your build system to handle ESM. If your project is ESM (e.g., `"type": "module"` in `package.json`), use `import send from 'send'`. Node.js 18+ generally handles interoperability well, but conflicts can arise if `send` is configured as ESM-only without CJS fallback.
Warnings
- breaking Node.js 18 or newer is now required. Older Node.js versions (below 18) are no longer supported by `send@1.1.0` and above. Users on older Node versions must upgrade their environment or stick to `send@0.x.x` versions.
- breaking Security hardening in `send@1.1.0` explicitly prevents serving files when a requested path ends with a `/`. This changes previous behavior where such paths might have implicitly served an `index.html` or similar. This is a deliberate change to prevent potential path traversal or directory listing issues.
- gotcha The `dotfiles` option's default behavior ('ignore') is subtly different from explicitly setting `'deny'`. The default will ignore dotfiles (e.g., `.env`), but *not* ignore files within directories that begin with a dot (e.g., `/.git/HEAD`). For strict security, explicitly set `dotfiles: 'deny'` to prevent serving any dotfiles or files within dot directories.
- gotcha The `path` argument passed to `send(req, path, options)` must be a URL-encoded path, not a direct file-system path. Incorrectly providing a raw file-system path will likely result in 'File not found' errors or unexpected routing.
- gotcha The `send` package has shown an unusual release pattern, with version `0.19.2` being released *after* `1.2.1`. This can lead to confusion about the current stable or recommended version. Always verify the `npm` registry's `latest` tag and release dates.
Install
-
npm install send -
yarn add send -
pnpm add send
Imports
- send
import { send } from 'send'import send from 'send'
- send
const { send } = require('send')const send = require('send') - SendStream (type)
import type { SendStream } from 'send'
Quickstart
import http from 'http';
import path from 'path';
import send from 'send';
import { fileURLToPath } from 'url';
// In an ESM module, __dirname is not directly available. Recreate it.
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Create a simple HTTP server
const server = http.createServer((req, res) => {
const filePath = path.join(__dirname, 'public', req.url || 'index.html');
// Basic example of creating a public directory and a file
// For a real application, ensure 'public' directory exists with files
// or adjust `root` option accordingly.
// Example: echo '<h1>Hello from send!</h1>' > public/index.html
send(req, req.url || '/', { root: path.join(__dirname, 'public') })
.on('error', (err) => {
if (err.statusCode === 404) {
res.statusCode = 404;
res.end('File not found');
} else {
res.statusCode = 500;
res.end('Internal Server Error: ' + err.message);
}
})
.on('directory', () => {
// Redirect or serve index.html for directory requests if desired
res.statusCode = 301;
res.setHeader('Location', req.url + '/index.html');
res.end('Redirecting to index.html');
})
.on('end', () => {
console.log(`Served: ${req.url}`);
})
.pipe(res);
});
const PORT = 3000;
server.listen(PORT, () => {
console.log(`Server listening on http://localhost:${PORT}`);
console.log(`Try http://localhost:${PORT}/index.html (create 'public/index.html' first)`);
});