Type Extensions
Extend Request, Response, and AzuraClient with custom properties
Type Extensions 🔧
AzuraJS allows you to extend core types (Request, Response, AzuraClient) with custom properties and methods, similar to Express but with full TypeScript support.
Quick Start 🚀
TypeScript
// types/express.d.ts
declare module 'azura' {
interface RequestServer {
user?: { id: string; name: string };
session?: any;
}
interface ResponseServer {
sendSuccess(data: any): this;
}
}
// Implementation
app.use((req, res, next) => {
res.sendSuccess = function(data: any) {
return this.status(200).json({ success: true, data });
};
next();
});
// Usage - now fully typed!
app.get('/data', (req, res) => {
res.sendSuccess({ message: 'Hello' }); // TypeScript knows about sendSuccess!
});JavaScript
// No types needed - just extend!
app.use((req, res, next) => {
res.sendSuccess = function(data) {
return this.status(200).json({ success: true, data });
};
next();
});Extending Request 📥
Adding User Property
// types.ts
declare module 'azura' {
interface RequestServer {
user?: {
id: string;
email: string;
role: 'admin' | 'user';
};
isAuthenticated(): boolean;
}
}
// middleware/auth.ts
app.use((req, res, next) => {
const token = req.header('authorization')?.replace('Bearer ', '');
if (token) {
req.user = decodeToken(token);
}
req.isAuthenticated = function() {
return !!this.user;
};
next();
});
// routes/protected.ts
app.get('/profile', (req, res) => {
if (!req.isAuthenticated()) { // TypeScript knows this method!
return res.status(401).json({ error: 'Unauthorized' });
}
res.json({ user: req.user }); // TypeScript knows user property!
});Adding Session
declare module 'azura' {
interface RequestServer {
session?: {
id: string;
data: Record<string, any>;
};
startSession(data?: any): void;
destroySession(): void;
}
}
const sessions = new Map();
app.use((req, res, next) => {
const sessionId = req.cookies['session-id'];
if (sessionId && sessions.has(sessionId)) {
req.session = sessions.get(sessionId);
}
req.startSession = function(data = {}) {
const id = generateId();
this.session = { id, data };
sessions.set(id, this.session);
res.cookie('session-id', id, { httpOnly: true });
};
req.destroySession = function() {
if (this.session) {
sessions.delete(this.session.id);
this.session = undefined;
res.clearCookie('session-id');
}
};
next();
});Extending Response 📤
Custom Response Methods
declare module 'azura' {
interface ResponseServer {
sendSuccess(data: any, message?: string): this;
sendError(message: string, code?: number): this;
paginate(data: any[], page: number, limit: number): this;
notFound(message?: string): this;
badRequest(message?: string): this;
}
}
app.use((req, res, next) => {
res.sendSuccess = function(data: any, message?: string) {
return this.status(200).json({
success: true,
message: message || 'Request successful',
data,
timestamp: new Date().toISOString()
});
};
res.sendError = function(message: string, code = 400) {
return this.status(code).json({
success: false,
error: message,
timestamp: new Date().toISOString()
});
};
res.paginate = function(data: any[], page: number, limit: number) {
const startIndex = (page - 1) * limit;
const endIndex = page * limit;
return this.status(200).json({
success: true,
data: data.slice(startIndex, endIndex),
pagination: {
page,
limit,
total: data.length,
totalPages: Math.ceil(data.length / limit),
hasNext: endIndex < data.length,
hasPrev: page > 1
}
});
};
res.notFound = function(message = 'Not found') {
return this.status(404).json({ error: message });
};
res.badRequest = function(message = 'Bad request') {
return this.status(400).json({ error: message });
};
next();
});
// Usage
app.get('/users', (req, res) => {
const users = getUsers();
res.sendSuccess(users); // Fully typed!
});
app.get('/user/:id', (req, res) => {
const user = findUser(req.params.id);
if (!user) {
return res.notFound('User not found');
}
res.sendSuccess(user);
});Extending AzuraClient 🚀
Adding Health Check
declare module 'azura' {
interface AzuraClient {
health(path?: string): void;
version(version: string, path?: string): void;
enableCors(options?: CorsOptions): void;
}
}
// Extend prototype
import { AzuraClient } from 'azura';
AzuraClient.prototype.health = function(path = '/health') {
this.get(path, (req, res) => {
res.json({
status: 'healthy',
uptime: process.uptime(),
memory: process.memoryUsage(),
timestamp: Date.now()
});
});
};
AzuraClient.prototype.version = function(version: string, path = '/version') {
this.get(path, (req, res) => {
res.json({ version });
});
};
AzuraClient.prototype.enableCors = function(options = {}) {
const { origin = '*', methods = 'GET,POST,PUT,DELETE', headers = 'Content-Type,Authorization' } = options;
this.use((req, res, next) => {
res.set('Access-Control-Allow-Origin', origin);
res.set('Access-Control-Allow-Methods', methods);
res.set('Access-Control-Allow-Headers', headers);
if (req.method === 'OPTIONS') {
return res.status(204).send();
}
next();
});
};
// Usage
const app = new AzuraClient();
app.health(); // Adds /health endpoint
app.version('1.0.0'); // Adds /version endpoint
app.enableCors(); // Enables CORSJavaScript Extensions 📜
No TypeScript? No problem! Extend in pure JavaScript:
const { AzuraClient } = require('azura');
const app = new AzuraClient();
// Method 1: Extend in middleware
app.use((req, res, next) => {
// Add to response
res.sendSuccess = function(data) {
return this.status(200).json({ success: true, data });
};
// Add to request
req.isLocalhost = function() {
return this.ip === '127.0.0.1' || this.ip === '::1';
};
next();
});
// Method 2: Extend prototype
AzuraClient.prototype.health = function(path) {
const healthPath = path || '/health';
this.get(healthPath, (req, res) => {
res.json({
status: 'ok',
uptime: process.uptime()
});
});
};
app.health(); // Now available!Practical Examples 💡
Database Access
declare module 'azura' {
interface RequestServer {
db: PrismaClient; // or any ORM
}
}
const prisma = new PrismaClient();
app.use((req, res, next) => {
req.db = prisma;
next();
});
// Now use req.db anywhere!
app.get('/users', async (req, res) => {
const users = await req.db.user.findMany();
res.json(users);
});Request Validation Helper
declare module 'azura' {
interface RequestServer {
validate(schema: any): any;
requireFields(...fields: string[]): any;
}
}
app.use((req, res, next) => {
req.validate = function(schema: any) {
const { error, value } = schema.validate(this.body);
if (error) throw new Error(error.message);
return value;
};
req.requireFields = function(...fields: string[]) {
const missing = fields.filter(f => !this.body[f]);
if (missing.length > 0) {
throw new Error(`Missing fields: ${missing.join(', ')}`);
}
return this.body;
};
next();
});
// Usage
app.post('/create', (req, res) => {
try {
req.requireFields('name', 'email');
const data = req.body;
// Create resource...
res.sendSuccess(data);
} catch (error) {
res.badRequest(error.message);
}
});Response Timing
declare module 'azura' {
interface ResponseServer {
_startTime?: number;
}
}
app.use((req, res, next) => {
res._startTime = Date.now();
const originalJson = res.json.bind(res);
res.json = function(data: any) {
const responseTime = Date.now() - (this._startTime || 0);
this.set('X-Response-Time', `${responseTime}ms`);
if (typeof data === 'object' && data !== null) {
data._meta = {
responseTime: `${responseTime}ms`,
timestamp: new Date().toISOString()
};
}
return originalJson(data);
};
next();
});Best Practices 💎
- Always declare types in a .d.ts file
- Use module augmentation (
declare module 'azura') - Implement in middleware or at application start
- Keep extensions minimal - don't bloat core objects
- Document custom properties for team members
- Use namespaces to avoid conflicts
- Test extensions thoroughly
Complete Example 🎯
// types/azura-extensions.d.ts
declare module 'azura' {
interface RequestServer {
user?: User;
session?: Session;
isAuthenticated(): boolean;
db: PrismaClient;
}
interface ResponseServer {
sendSuccess(data: any, message?: string): this;
sendError(message: string, code?: number): this;
notFound(message?: string): this;
}
interface AzuraClient {
health(path?: string): void;
}
}
// app.ts
import { AzuraClient } from 'azura';
import { PrismaClient } from '@prisma/client';
const app = new AzuraClient();
const prisma = new PrismaClient();
// Setup extensions
import './middleware/extensions';
// Enable health check
app.health();
// Use typed extensions
app.get('/users', async (req, res) => {
if (!req.isAuthenticated()) {
return res.notFound();
}
const users = await req.db.user.findMany();
res.sendSuccess(users, 'Users retrieved');
});API Reference 📖
Extendable Interfaces
RequestServer- HTTP request objectResponseServer- HTTP response objectAzuraClient- Main application class
Extension Methods
declare module 'azura'- Module augmentationinterface RequestServer extends ...- Extend requestinterface ResponseServer extends ...- Extend response
Examples 📚
Check out complete examples:
- type-extensions.example.ts - TypeScript extensions
- javascript-extensions.example.js - JavaScript extensions
