Request & Response
Balda.js provides a clean and intuitive API for handling HTTP requests and responses. The framework extends the standard Node.js request and response objects with additional convenience methods and properties.
Request Object
The request object contains information about the incoming HTTP request.
Basic Properties
interface Request {
method: string; // HTTP method (GET, POST, etc.)
url: string; // Request URL
path: string; // Request path
headers: Record<string, string>; // Request headers
query: Record<string, string>; // Query parameters
params: Record<string, string>; // Route parameters
body: any; // Request body (parsed)
ip: string; // Client IP address
cookies: Record<string, string>; // Request cookies
}
Accessing Request Data
@get('/users/:id')
async getUser(req: Request, res: Response) {
// Route parameters
const userId = req.params.id;
// Query parameters
const { page = 1, limit = 10 } = req.query;
// Headers
const userAgent = req.headers['user-agent'];
const authToken = req.headers.authorization;
// Request body (for POST/PUT/PATCH)
const userData = req.body;
// Client IP
const clientIP = req.ip;
res.json({ userId, page, limit, userAgent, clientIP });
}
File Uploads
import { Request, Response, fileParser } from 'balda-js';
@post('/upload')
@middleware(fileParser())
async uploadFile(req: Request, res: Response) {
const file = req.file('file');
if (!file) {
return res.badRequest({ error: 'No file uploaded' });
}
res.json({
filename: file.originalName,
size: file.size,
mimetype: file.mimeType
});
}
Response Object
The response object provides methods for sending HTTP responses.
Status Methods
import { Request, Response } from 'balda-js';
@get('/users')
async getUsers(req: Request, res: Response) {
// Success responses
res.ok({ users: [] }); // 200 OK
res.created({ id: 1 }); // 201 Created
res.noContent(); // 204 No Content
// Client error responses
res.badRequest({ error: 'Invalid data' }); // 400 Bad Request
res.unauthorized({ error: 'Auth required' }); // 401 Unauthorized
res.forbidden({ error: 'Access denied' }); // 403 Forbidden
res.notFound({ error: 'User not found' }); // 404 Not Found
res.conflict({ error: 'User exists' }); // 409 Conflict
res.tooManyRequests({ error: 'Rate limited' }); // 429 Too Many Requests
// Server error responses
res.internalServerError({ error: 'Server error' }); // 500 Internal Server Error
res.notImplemented({ error: 'Not implemented' }); // 501 Not Implemented
res.badGateway({ error: 'Bad gateway' }); // 502 Bad Gateway
res.serviceUnavailable({ error: 'Unavailable' }); // 503 Service Unavailable
}
Content Methods
import { Request, Response } from 'balda-js';
@get('/api/data')
async getData(req: Request, res: Response) {
// Manually set headers
res.setHeader('X-Custom-Header', 'value');
// JSON response
res.json({ message: 'Hello World' });
// Text response
res.text('Hello World');
// HTML response
res.html('<h1>Hello World</h1>');
// Redirect
res.redirect('/new-location');
// Download file
res.download('/path/to/file.pdf', 'filename.pdf');
// Send tries to understand the best content type to send based on the body (better to use specific methods)
res.send({ message: 'Hello World' });
}
Header Management
import { Request, Response } from 'balda-js';
@get('/api/data')
async getData(req: Request, res: Response) {
// Set headers
res.setHeader('Content-Type', 'application/json');
res.setHeader('Cache-Control', 'no-cache');
// Set multiple headers
res.setHeaders({
'Content-Type': 'application/json',
'X-Custom-Header': 'value'
});
// Get headers
const contentType = res.getHeader('Content-Type');
res.json({ data: 'value' });
}
Cookie Management
import { Request, Response } from 'balda-js';
@post('/login')
async login(req: Request, res: Response) {
// Set cookies
res.setCookie('sessionId', 'abc123', {
httpOnly: true,
secure: true,
maxAge: 24 * 60 * 60 * 1000 // 24 hours
});
// Clear cookies
res.clearCookie('oldSession');
res.json({ message: 'Logged in' });
}
Request Validation
Built-in Validation
The built in validation is based on the zod library, you can use it to validate the request body or query parameters.
The validated body is passed as the next argument to the route handler (can be stacked with other validation decorators).
import { Request, Response, validate } from 'balda-js';
import z from 'zod';
const CreateUserSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
age: z.number().min(18)
});
@post('/users')
@validate.body(CreateUserSchema)
async createUser(req: Request, res: Response, body: z.infer<typeof CreateUserSchema>) {
// body is validated and typed
const { name, email, age } = body;
res.created({ name, email, age });
}
Query Parameter Validation
import { Request, Response, validate } from 'balda-js';
import z from 'zod';
const QuerySchema = z.object({
page: z.number().min(1).optional(),
limit: z.number().min(1).max(100).optional()
});
@get('/users')
@validate.query(QuerySchema)
async getUsers(req: Request, res: Response, query: any) {
const { page = 1, limit = 10 } = query;
res.json({ page, limit });
}
The built-in validation only supports synchronous Zod schemas. Async refinements (e.g. .refine(async () => ...)) or async transforms are not supported and will throw an error.
If you need async validation, use Zod's .parseAsync() or .safeParseAsync() methods directly in your handler:
@post('/users')
async createUser(req: Request, res: Response) {
const result = await CreateUserSchema.safeParseAsync(req.body);
if (!result.success) {
return res.badRequest(result.error);
}
res.created(result.data);
}
Response Serialization
Schema-based Serialization
import { Request, Response, serialize } from 'balda-js';
import z from 'zod';
const UserSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string()
});
@get('/users')
@serialize(z.array(UserSchema))
async getUsers(req: Request, res: Response) {
const users = await userService.findAll();
res.json(users);
}
@get('/users/:id')
@serialize(UserSchema)
async getUser(req: Request, res: Response) {
const user = await userService.findById(req.params.id);
res.json(user);
}
Multiple Response Schemas
import { Request, Response, serialize } from 'balda-js';
import z from 'zod';
@get('/users/:id')
@serialize(UserSchema)
@serialize(z.object({ error: z.string() }), { status: 404 })
async getUser(req: Request, res: Response) {
const user = await userService.findById(req.params.id);
if (!user) {
return res.notFound({ error: 'User not found' });
}
res.json(user);
}
Error Handling
Custom Error Responses
import { Request, Response } from 'balda-js';
@get('/users/:id')
async getUser(req: Request, res: Response) {
try {
const user = await userService.findById(req.params.id);
if (!user) {
return res.notFound({
error: 'User not found',
code: 'USER_NOT_FOUND'
});
}
res.json(user);
} catch (error) {
console.error('Error fetching user:', error);
res.internalServerError({
error: 'Failed to fetch user',
code: 'FETCH_ERROR'
});
}
}
Global Error Handler
import { Server } from 'balda-js';
server.setErrorHandler((req, res, next, error) => {
console.error('Error:', error);
if (error.name === 'ValidationError') {
return res.badRequest({
error: 'Validation failed',
details: error.message
});
}
if (error.name === 'UnauthorizedError') {
return res.unauthorized({ error: 'Authentication required' });
}
res.internalServerError({ error: 'Internal server error' });
});
Request Lifecycle
Middleware Chain
import { Server, controller, get, post, middleware, validate } from 'balda-js';
import { Request, Response } from 'balda-js';
import z from 'zod';
// 1. Global middleware
server.use(logger);
server.use(cors);
// 2. Controller middleware
@controller('/users')
@middleware(authMiddleware)
export class UsersController {
// 3. Route middleware
@get('/:id', { middleware: [rateLimit] })
// 4. Validation
@validate.params(z.object({ id: z.string() }))
// 5. Route handler
async getUser(req: Request, res: Response) {
// 6. Response serialization
res.json({ user: req.params.id });
}
}
TypeScript Support
Request Extensions
import { Request, Response } from 'balda-js';
interface AuthenticatedRequest extends Request {
user: {
id: number;
email: string;
role: string;
};
}
@get('/profile')
@middleware(authMiddleware)
async getProfile(req: AuthenticatedRequest, res: Response) {
// req.user is now typed
res.json({
id: req.user.id,
email: req.user.email,
role: req.user.role
});
}
Response Types
import { Request, Response } from 'balda-js';
interface ApiResponse<T> {
data: T;
message?: string;
timestamp: string;
}
@get('/users')
async getUsers(req: Request, res: Response) {
const users = await userService.findAll();
const response: ApiResponse<typeof users> = {
data: users,
message: 'Users retrieved successfully',
timestamp: new Date().toISOString()
};
res.json(response);
}
Best Practices
1. Consistent Response Format
// Good: Consistent structure
res.json({
success: true,
data: { id: 1, name: 'John' },
message: 'User created successfully'
});
// Error response
res.badRequest({
success: false,
error: 'Validation failed',
details: ['Name is required']
});
2. Proper Status Codes
// Use appropriate status codes
res.created({ id: 1 }); // 201 for new resources
res.noContent(); // 204 for successful deletion
res.badRequest({ error: '' }); // 400 for client errors
res.notFound({ error: '' }); // 404 for missing resources
3. Input Validation
@post('/users')
@validate.body(CreateUserSchema)
async createUser(req: Request, res: Response, body: CreateUser) {
// body is validated and typed
const user = await userService.create(body);
res.created(user);
}
4. Error Handling
@get('/users/:id')
async getUser(req: Request, res: Response) {
try {
const user = await userService.findById(req.params.id);
if (!user) {
return res.notFound({ error: 'User not found' });
}
res.json(user);
} catch (error) {
console.error('Database error:', error);
res.internalServerError({ error: 'Failed to fetch user' });
}
}
5. Security Headers
// Set security headers
res.setHeaders({
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block'
});
The request and response objects in Balda.js provide a powerful and intuitive API for building robust web applications with proper error handling, validation, and type safety.