Why Redact? β’ Features β’ Installation β’ Documentation β’ Contributing
Redact.js is a next-generation micro-framework for Node.js built on a simple premise: Web servers should be simple function calls, not complex state managers.
It abandons the traditional, imperative style of Express (req, res, next) in favor of a clean, declarative syntax. With Redact, you define your API as a structured object, and your logic is pure: You receive input, and you return output. The framework handles the HTTP complexity for you.
Most Node.js frameworks are Imperative: you have to tell the server how to send a response step-by-step. Redact is Declarative: you tell the server what the response is.
| Feature | π’ Express.js (The Old Way) | π Redact.js (The New Way) |
|---|---|---|
| Routing | Scattered app.get, app.post calls. Hard to visualize structure. |
Single Object Tree. See your entire API hierarchy at a glance. |
| Response | Manual res.status(200).json({...}). |
Just return {...}. The framework automates status & headers. |
| Async Logic | Easy to forget .catch(next). Crashes server on unhandled errors. |
Native Async/Await. Thrown errors are automatically caught and handled safely. |
| Real-Time | Requires external libraries (socket.io) and complex setup. | Built-in WebSockets. Handles HTTP and WS on the same port effortlessly. |
| Boilerplate | High. Middleware for everything (body-parser, cors, etc). | Zero. Automatic JSON parsing, query parsing, and security limits included. |
|
Define your API structure in a readable, nested object syntax. Return an Object/Array for JSON, or a String for text. No Real-time support out of the box with |
Native support for parameters like Built-in protection against DoS attacks (1MB body limit). Filter requests globally before they hit your logic. |
npm install @noturbob/redact wsNote:
wsis required for WebSocket features
const app = require('@noturbob/redact')();
// Define your API
app.routes({
path: "/",
GET: "Welcome to Redact!", // Returns text
POST: (body) => {
// Returns JSON automatically
return { status: "created", data: body };
}
});
app.listen(3000, () => {
console.log("π Server running at http://localhost:3000");
});Instead of writing imperative code, describe your API.
app.routes({
path: "/api/v1/status",
GET: { status: "online", uptime: process.uptime() }
});Your route handlers receive two arguments:
input: The parsed data (JSON body for POST/PUT, or empty object).req: The full request context (headers, params, query).
app.routes({
path: "/products",
POST: (body, req) => {
// 'body' is the JSON payload sent by the user
console.log("User Agent:", req.headers['user-agent']);
return { success: true, product: body };
}
});Use : to define dynamic parameters. Access them via req.params.
app.routes({
path: "/users/:id",
GET: (input, req) => {
// GET /users/500 -> { userId: "500" }
return { userId: req.params.id };
}
});Redact creates a unified server for both HTTP and WebSockets.
app.socket({
path: '/chat',
open: (ws) => {
console.log("β
Client connected");
ws.send("Welcome!");
},
message: (ws, data, clients) => {
// 'data' is auto-parsed JSON
// 'clients' is a Set of all connected users (for broadcasting)
clients.forEach(client => client.send(JSON.stringify(data)));
},
close: () => {
console.log("β Client disconnected");
}
});Middleware runs before every request. It follows the "Just Return" philosophy:
- Return
undefined: Request proceeds to the route handler. - Return a value: Request stops, and that value is sent as the response.
app.use((req) => {
// Log every request
console.log(`[${req.method}] ${req.url}`);
// Security Check
if (req.url.includes("/admin")) {
// Stop the request immediately with a 403-like error
return { error: "Unauthorized Access" };
}
});Query strings are automatically parsed into req.query.
Request: GET /search?q=javascript&sort=desc
app.routes({
path: "/search",
GET: (input, req) => {
return {
results: [],
meta: {
query: req.query.q, // "javascript"
sort: req.query.sort // "desc"
}
};
}
});const app = require('@noturbob/redact')();
let users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" }
];
app.routes({
path: "/api/users",
// Get all users
GET: () => users,
// Create new user
POST: (body) => {
const newUser = { id: users.length + 1, ...body };
users.push(newUser);
return newUser;
}
});
app.routes({
path: "/api/users/:id",
// Get specific user
GET: (input, req) => {
const user = users.find(u => u.id === parseInt(req.params.id));
return user || { error: "User not found" };
},
// Update user
PUT: (body, req) => {
const index = users.findIndex(u => u.id === parseInt(req.params.id));
if (index === -1) return { error: "User not found" };
users[index] = { ...users[index], ...body };
return users[index];
},
// Delete user
DELETE: (input, req) => {
const index = users.findIndex(u => u.id === parseInt(req.params.id));
if (index === -1) return { error: "User not found" };
users.splice(index, 1);
return { success: true };
}
});
app.listen(3000);We welcome contributions! Please fork the repository and submit a Pull Request.
- π΄ Fork the Project
- πΏ Create your Feature Branch (
git checkout -b feature/AmazingFeature) - πΎ Commit your Changes (
git commit -m 'Add some AmazingFeature') - π Push to the Branch (
git push origin feature/AmazingFeature) - π Open a Pull Request
Distributed under the MIT License. See LICENSE for more information.
If you find Redact.js helpful, please consider giving it a β on GitHub!