Error Handling
Balda.js provides intelligent error handling out of the box, following HTTP specifications (RFC 7231) for proper status codes and security best practices for error responses.
Error Response Format
All error responses follow a standardized format:
// Development (NODE_ENV !== 'production')
{
"code": "ROUTE_NOT_FOUND",
"message": "ROUTE_NOT_FOUND: Cannot GET /unknown",
"stack": "Error: ROUTE_NOT_FOUND...",
"cause": undefined
}
// Production (NODE_ENV === 'production')
{
"code": "ROUTE_NOT_FOUND",
"message": "ROUTE_NOT_FOUND: Cannot GET /unknown"
}
Why Stack Traces Are Hidden in Production
Stack traces can expose sensitive information:
- Internal file paths:
/home/app/src/server/server.ts - Code structure: Function names and middleware chains
- Framework internals: How your application processes requests
This information could help attackers understand your application structure and find vulnerabilities.
404 Not Found
When a request is made to a path that doesn't exist for any HTTP method, Balda.js returns a 404 Not Found response.
// No routes defined for /unknown
// Request: GET /unknown
// Response: 404 Not Found
{
"code": "RouteNotFoundError",
"message": "ROUTE_NOT_FOUND: Cannot GET /unknown"
}
Custom Not Found Handler
You can customize the 404 response using setNotFoundHandler:
import { Server } from 'balda-js';
const server = new Server({ port: 3000 });
server.setNotFoundHandler((req, res) => {
res.status(404).json({
error: 'Page not found',
path: new URL(req.url).pathname,
suggestion: 'Check the API documentation at /docs'
});
});
server.listen();
405 Method Not Allowed
When a request is made to a path that exists but with a different HTTP method, Balda.js returns a 405 Method Not Allowed response with an Allow header listing the valid methods.
import { Server } from 'balda-js';
const server = new Server({ port: 3000 });
// Only POST is allowed for /users
server.post('/users', (req, res) => {
res.created({ id: 1, ...req.body });
});
server.listen();
# Request: GET /users
# Response: 405 Method Not Allowed
# Headers: Allow: POST
{
"code": "MethodNotAllowedError",
"message": "METHOD_NOT_ALLOWED: Cannot GET /users"
}
Multiple Allowed Methods
If a path is registered with multiple methods, all valid methods are listed in the Allow header:
server.get('/users', (req, res) => {
res.json({ users: [] });
});
server.post('/users', (req, res) => {
res.created(req.body);
});
// Request: DELETE /users
// Response: 405 Method Not Allowed
// Headers: Allow: GET, POST
Route Matching Priority
Balda.js uses a radix trie router with the following matching priority:
| Priority | Type | Example | Description |
|---|---|---|---|
| 1st | Static | /users | Exact path match |
| 2nd | Parameterized | /users/:id | Dynamic segments |
| 3rd | Wildcard | * | Catch-all (404/405 handler) |
This ensures that specific routes always take precedence over catch-all handlers.
server.get('/users', handler); // Matches GET /users
server.get('/users/:id', handler); // Matches GET /users/123
// Wildcard catch-all handles everything else (404/405)
Global Error Handler
For handling errors thrown during request processing, use setErrorHandler:
import { Server } from 'balda-js';
const server = new Server({ port: 3000 });
server.setErrorHandler((req, res, next, error) => {
console.error('Request error:', error);
// Handle specific error types
if (error.name === 'ValidationError') {
return res.badRequest({
code: 'VALIDATION_ERROR',
message: error.message
});
}
if (error.name === 'UnauthorizedError') {
return res.unauthorized({
code: 'UNAUTHORIZED',
message: 'Authentication required'
});
}
// Default error response
res.internalServerError({
code: 'INTERNAL_ERROR',
message: 'An unexpected error occurred'
});
});
server.listen();
Built-in Error Types
Balda.js provides several built-in error types:
| Error | Status | Description |
|---|---|---|
RouteNotFoundError | 404 | Path doesn't exist for any method |
MethodNotAllowedError | 405 | Path exists but not for requested method |
JsonNotValidError | 400 | Invalid JSON in request body |
FileTooLargeError | 413 | Uploaded file exceeds size limit |
Best Practices
1. Always Set NODE_ENV in Production
NODE_ENV=production node server.js
This ensures stack traces are never exposed to clients.
2. Use Specific Error Handlers
server.setErrorHandler((req, res, next, error) => {
// Log the full error internally
console.error({
error: error.message,
stack: error.stack,
path: req.url,
method: req.method
});
// Return safe response to client
res.internalServerError({
code: 'INTERNAL_ERROR',
message: 'Something went wrong'
});
});
3. Provide Helpful 404 Responses
server.setNotFoundHandler((req, res) => {
const pathname = new URL(req.url).pathname;
res.status(404).json({
code: 'NOT_FOUND',
message: `The endpoint ${pathname} does not exist`,
docs: '/api/docs'
});
});
4. Log Errors for Debugging
server.setErrorHandler((req, res, next, error) => {
// Use your logging service
logger.error({
requestId: req.id,
error: error.message,
stack: error.stack,
url: req.url,
method: req.method
});
res.internalServerError({
code: 'INTERNAL_ERROR',
requestId: req.id // Include for support reference
});
});
HTTP Status Code Reference
| Status | Method | Description |
|---|---|---|
res.badRequest() | 400 | Invalid request syntax |
res.unauthorized() | 401 | Authentication required |
res.forbidden() | 403 | Access denied |
res.notFound() | 404 | Resource not found |
res.methodNotAllowed() | 405 | HTTP method not supported |
res.conflict() | 409 | Resource conflict |
res.internalServerError() | 500 | Server error |