Skip to main content

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' });
}
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 });
}
Synchronous Validation Only

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.