High performance middleware framework for Node.js HTTP servers
npx @tessl/cli install tessl/npm-connect@3.7.0Connect is an extensible HTTP server framework for Node.js using middleware to handle requests. It provides a minimalist foundation for building web applications, APIs, and HTTP services through a composable middleware stack architecture.
npm install connectconst connect = require("connect");For ES modules:
import connect from "connect";const connect = require("connect");
const http = require("http");
// Create app
const app = connect();
// Add middleware
app.use(function(req, res, next) {
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("Hello from Connect!");
});
// Start server
app.listen(3000);
// or: http.createServer(app).listen(3000);Connect implements a middleware stack pattern where:
setImmediate (or process.nextTick fallback) for non-blocking flowfinalhandler module for unhandled requests/errorsCreates a new Connect application instance that can handle HTTP requests.
/**
* Create a new Connect server application
* @returns {Function} Connect app function with middleware capabilities
*/
function connect();The returned app is both a function (req, res, next) => void and an object with methods.
Adds middleware functions to the application stack for processing requests.
/**
* Add middleware function to the app stack
* @param {string} [route] - Optional path to mount middleware on (defaults to '/')
* @param {Function|Server} fn - Middleware function, HTTP server, or Connect app
* @returns {Function} The app instance for chaining
*/
app.use([route], fn);
/**
* Add middleware function to the app stack (no route)
* @param {Function|Server} fn - Middleware function, HTTP server, or Connect app
* @returns {Function} The app instance for chaining
*/
app.use(fn);Usage Examples:
// Basic middleware
app.use(function(req, res, next) {
console.log("Request:", req.method, req.url);
next();
});
// Path-mounted middleware
app.use("/api", function(req, res, next) {
// Only handles requests starting with /api
// req.url is modified: "/api/users" becomes "/users"
// req.originalUrl preserves original: "/api/users"
next();
});
// Mounting other Connect apps
const adminApp = connect();
adminApp.use(function(req, res, next) {
res.end("Admin section");
});
app.use("/admin", adminApp);
// Mounting HTTP servers
const http = require("http");
const staticServer = http.createServer(/* static handler */);
app.use("/static", staticServer);
// Error-handling middleware (4 parameters)
app.use(function(err, req, res, next) {
console.error(err.stack);
res.statusCode = 500;
res.end("Internal Server Error");
});
// Chaining
app.use(middleware1).use("/admin", middleware2);Processes HTTP requests through the middleware stack. Connect automatically sets req.originalUrl to preserve the original URL and manipulates req.url for mounted middleware.
/**
* Handle HTTP requests through middleware stack
* @param {IncomingMessage} req - Node.js HTTP request object
* @param {ServerResponse} res - Node.js HTTP response object
* @param {Function} [out] - Optional callback for unhandled requests or errors
*/
app.handle(req, res, [out]);
/**
* Alias for app.handle - app itself is callable
* @param {IncomingMessage} req - Node.js HTTP request object
* @param {ServerResponse} res - Node.js HTTP response object
* @param {Function} [next] - Optional callback for unhandled requests
*/
app(req, res, [next]);URL Manipulation Behavior:
When middleware is mounted on a route, Connect modifies the request URL:
req.originalUrl is set to the full original URL (only set once)req.url is modified to remove the mounted path/) or dot (.) boundariesapp.use("/api/v1", function(req, res, next) {
// Request to "/api/v1/users/123"
console.log(req.originalUrl); // "/api/v1/users/123"
console.log(req.url); // "/users/123"
next();
});
// Route boundary examples:
app.use("/foo", handler); // Matches: /foo, /foo/, /foo/bar, /foo.bar
// Does NOT match: /foobarCreates and starts an HTTP server using the Connect app as the request handler.
/**
* Start HTTP server listening for requests
* @param {...*} args - Same arguments as Node.js http.Server.listen()
* @returns {Server} Node.js HTTP server instance
*/
app.listen(...args);Usage Examples:
// Listen on port 3000
const server = app.listen(3000);
// Listen with hostname and callback
app.listen(3000, "localhost", function() {
console.log("Server running on http://localhost:3000");
});
// Using http.createServer directly
const http = require("http");
const server = http.createServer(app);
server.listen(3000);Connect apps inherit from EventEmitter and can emit/listen for events.
/**
* Add event listener (inherited from EventEmitter)
* @param {string} event - Event name
* @param {Function} listener - Event listener function
* @returns {Function} The app instance
*/
app.on(event, listener);
/**
* Emit event (inherited from EventEmitter)
* @param {string} event - Event name
* @param {...*} args - Arguments to pass to listeners
* @returns {boolean} Whether event had listeners
*/
app.emit(event, ...args);Usage Examples:
// Listen for custom events
app.on("user:login", function(user) {
console.log("User logged in:", user.name);
});
// Emit events from middleware
app.use(function(req, res, next) {
if (req.url === "/login") {
app.emit("user:login", { name: "John" });
}
next();
});/**
* Standard middleware function
* @param {IncomingMessage} req - HTTP request object
* @param {ServerResponse} res - HTTP response object
* @param {Function} next - Call to continue to next middleware or pass error
*/
function middleware(req, res, next);
/**
* Error-handling middleware function (4 parameters)
* @param {Error} err - Error object from previous middleware
* @param {IncomingMessage} req - HTTP request object
* @param {ServerResponse} res - HTTP response object
* @param {Function} next - Call to continue to next error handler or pass error
*/
function errorMiddleware(err, req, res, next);/**
* Connect app properties
*/
interface ConnectApp {
/** Middleware stack array (internal, but publicly accessible) */
stack: Array<{route: string, handle: Function}>;
/** Mount route path (defaults to '/') */
route: string;
/** Alias for app.handle */
(req: IncomingMessage, res: ServerResponse, next?: Function): void;
/** Add middleware to the stack */
use(fn: Function): ConnectApp;
use(route: string, fn: Function): ConnectApp;
/** Handle requests through middleware stack */
handle(req: IncomingMessage, res: ServerResponse, out?: Function): void;
/** Start HTTP server */
listen(...args: any[]): http.Server;
/** Event emitter methods (inherited) */
on(event: string, listener: Function): ConnectApp;
emit(event: string, ...args: any[]): boolean;
}
/**
* Enhanced request object (Node.js IncomingMessage with Connect additions)
*/
interface ConnectRequest extends IncomingMessage {
/** Original URL before any modifications (set by Connect) */
originalUrl?: string;
}Connect uses a special error-handling pattern:
next(error) to pass errors to error handlers(err, req, res, next) handle errorsfinalhandler module if no error middleware handles the error// Middleware that might error
app.use(function(req, res, next) {
if (req.url === "/error") {
return next(new Error("Something went wrong"));
}
next();
});
// Error handler
app.use(function(err, req, res, next) {
res.statusCode = 500;
res.end("Error: " + err.message);
});Complex Error Handling Examples:
// Multiple error handlers for different error types
app.use(function(req, res, next) {
if (req.url === "/auth-error") {
const err = new Error("Unauthorized");
err.status = 401;
return next(err);
}
next();
});
// Handle auth errors specifically
app.use(function(err, req, res, next) {
if (err.status === 401) {
res.statusCode = 401;
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify({ error: "Unauthorized access" }));
return;
}
next(err); // Pass to next error handler
});
// General error handler (should be last)
app.use(function(err, req, res, next) {
console.error(err.stack);
res.statusCode = err.status || 500;
res.end("Internal Server Error");
});Connect apps can be mounted as middleware in other Connect apps, creating modular application structures.
const main = connect();
const admin = connect();
const api = connect();
// Build admin sub-app
admin.use(function(req, res, next) {
// Admin authentication middleware
if (!req.headers.authorization) {
res.statusCode = 401;
res.end("Unauthorized");
return;
}
next();
});
admin.use(function(req, res, next) {
res.end("Admin Dashboard: " + req.url);
});
// Build API sub-app
api.use(function(req, res, next) {
res.setHeader("Content-Type", "application/json");
next();
});
api.use("/users", function(req, res, next) {
res.end(JSON.stringify({ users: [] }));
});
// Mount sub-apps in main app
main.use("/admin", admin); // All /admin/* requests go to admin app
main.use("/api", api); // All /api/* requests go to api app
// Main app middleware
main.use(function(req, res, next) {
res.end("Main app: " + req.url);
});Connect apps work with any Node.js HTTP server and can be combined with other server technologies.
const https = require("https");
const http2 = require("http2");
const fs = require("fs");
// HTTPS server
const httpsOptions = {
key: fs.readFileSync("private-key.pem"),
cert: fs.readFileSync("certificate.pem")
};
https.createServer(httpsOptions, app).listen(443);
// HTTP/2 server
const http2Server = http2.createSecureServer(httpsOptions, app);
http2Server.listen(8443);
// Multiple servers with same app
const httpServer = http.createServer(app);
const httpsServer = https.createServer(httpsOptions, app);
httpServer.listen(80);
httpsServer.listen(443);// Conditional routing based on request properties
app.use(function(req, res, next) {
if (req.method === "POST" && req.url.startsWith("/api/")) {
// Handle API POST requests differently
res.setHeader("Content-Type", "application/json");
}
next();
});
// Route parameter extraction (manual)
app.use("/users", function(req, res, next) {
// req.url is "/123" for request to "/users/123"
const userId = req.url.slice(1); // Remove leading slash
if (userId && /^\d+$/.test(userId)) {
req.userId = userId;
}
next();
});
// Multiple route patterns
app.use("/api/v1", apiV1Handler);
app.use("/api/v2", apiV2Handler);
app.use("/api", function(req, res, next) {
// Catch-all for other API routes
res.statusCode = 404;
res.end("API version not found");
});Connect processes requests through several internal steps:
req.originalUrl is set to preserve the original request URLstack arrayreq.url is temporarily modifiedfinalhandler// Route matching follows specific rules:
app.use("/foo", handler);
// Matches these URLs:
// "/foo" - exact match
// "/foo/" - with trailing slash
// "/foo/bar" - with additional path
// "/foo.html" - with dot separator
// Does NOT match:
// "/foobar" - no separator after route
// "/FOO" - case-insensitive (actually matches in Connect)Note: Route matching is case-insensitive in Connect.
Connect respects the NODE_ENV environment variable: