restifyx.js
Version:
Advanced API endpoint handler system with automatic documentation
271 lines • 8.49 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.All = exports.Head = exports.Options = exports.Patch = exports.Delete = exports.Put = exports.Post = exports.Get = void 0;
exports.Controller = Controller;
exports.Use = Use;
exports.RouteMetadata = RouteMetadata;
exports.Validate = Validate;
exports.RateLimit = RateLimit;
exports.Cache = Cache;
exports.WebSocketEvent = WebSocketEvent;
exports.extractEndpoints = extractEndpoints;
exports.extractWebSocketEvents = extractWebSocketEvents;
require("reflect-metadata");
const MetadataKeys = {
BASE_PATH: "restifyx:base-path",
MIDDLEWARE: "restifyx:middleware",
ENDPOINTS: "restifyx:endpoints",
VALIDATION: "restifyx:validation",
RATE_LIMIT: "restifyx:rate-limit",
CACHE: "restifyx:cache",
SWAGGER: "restifyx:swagger",
WEBSOCKET: "restifyx:websocket",
};
/**
* Decorator for controller class
*
* @param basePath - Base path for all routes in the controller
* @returns Class decorator
*
* @example
* \`\`\`typescript
* @Controller('/api/users')
* class UserController {
* // ...
* }
* \`\`\`
*/
function Controller(basePath) {
return (target) => {
Reflect.defineMetadata(MetadataKeys.BASE_PATH, basePath, target);
};
}
/**
* Decorator for middleware on controller or method
*
* @param middleware - Middleware function(s) to apply
* @returns Class or method decorator
*
* @example
* \`\`\`typescript
* @Controller('/api/users')
* @Use(authMiddleware)
* class UserController {
* // ...
* }
* \`\`\`
*/
function Use(...middleware) {
return (target, propertyKey) => {
if (propertyKey) {
const existingMiddleware = Reflect.getMetadata(MetadataKeys.MIDDLEWARE, target, propertyKey) || [];
Reflect.defineMetadata(MetadataKeys.MIDDLEWARE, [...existingMiddleware, ...middleware], target, propertyKey);
}
else {
const existingMiddleware = Reflect.getMetadata(MetadataKeys.MIDDLEWARE, target) || [];
Reflect.defineMetadata(MetadataKeys.MIDDLEWARE, [...existingMiddleware, ...middleware], target);
}
};
}
/**
* Create a route decorator for a specific HTTP method
*
* @param method - HTTP method
* @returns Method decorator factory
*/
function createRouteDecorator(method) {
return (path = "") => {
return (target, propertyKey, descriptor) => {
const endpoints = Reflect.getMetadata(MetadataKeys.ENDPOINTS, target.constructor) || [];
endpoints.push({
method,
path,
handler: descriptor.value,
name: propertyKey.toString(),
});
Reflect.defineMetadata(MetadataKeys.ENDPOINTS, endpoints, target.constructor);
};
};
}
/**
* Decorator for GET routes
*
* @param path - Route path
* @returns Method decorator
*
* @example
* \`\`\`typescript
* @Controller('/api/users')
* class UserController {
* @Get('/:id')
* async getUser(req: Request, res: Response) {
* // ...
* }
* }
* \`\`\`
*/
exports.Get = createRouteDecorator("GET");
/**
* Decorator for POST routes
*
* @param path - Route path
* @returns Method decorator
*/
exports.Post = createRouteDecorator("POST");
/**
* Decorator for PUT routes
*
* @param path - Route path
* @returns Method decorator
*/
exports.Put = createRouteDecorator("PUT");
/**
* Decorator for DELETE routes
*
* @param path - Route path
* @returns Method decorator
*/
exports.Delete = createRouteDecorator("DELETE");
/**
* Decorator for PATCH routes
*
* @param path - Route path
* @returns Method decorator
*/
exports.Patch = createRouteDecorator("PATCH");
/**
* Decorator for OPTIONS routes
*
* @param path - Route path
* @returns Method decorator
*/
exports.Options = createRouteDecorator("OPTIONS");
/**
* Decorator for HEAD routes
*
* @param path - Route path
* @returns Method decorator
*/
exports.Head = createRouteDecorator("HEAD");
exports.All = createRouteDecorator("ALL");
/**
* Decorator for defining route metadata
*
* @param metadata - Endpoint metadata
* @returns Method decorator
*
* @example
* \`\`\`typescript
* @Controller('/api/users')
* class UserController {
* @Get('/:id')
* @RouteMetadata({
* name: 'Get User',
* description: 'Get a user by ID',
* tags: ['users'],
* responses: {
* '200': {
* description: 'User found',
* },
* '404': {
* description: 'User not found',
* }
* }
* })
* async getUser(req: Request, res: Response) {
* // ...
* }
* }
* \`\`\`
*/
function RouteMetadata(metadata) {
return (target, propertyKey, descriptor) => {
const endpoints = Reflect.getMetadata(MetadataKeys.ENDPOINTS, target.constructor) || [];
const endpointIndex = endpoints.findIndex((endpoint) => endpoint.handler === descriptor.value);
if (endpointIndex !== -1) {
endpoints[endpointIndex] = {
...endpoints[endpointIndex],
...metadata,
};
Reflect.defineMetadata(MetadataKeys.ENDPOINTS, endpoints, target.constructor);
}
};
}
function Validate(schema) {
return (target, propertyKey, descriptor) => {
Reflect.defineMetadata(MetadataKeys.VALIDATION, schema, target, propertyKey);
};
}
function RateLimit(options) {
return (target, propertyKey, descriptor) => {
Reflect.defineMetadata(MetadataKeys.RATE_LIMIT, options, target, propertyKey);
};
}
function Cache(options) {
return (target, propertyKey, descriptor) => {
Reflect.defineMetadata(MetadataKeys.CACHE, options, target, propertyKey);
};
}
function WebSocketEvent(eventName, namespace) {
return (target, propertyKey, descriptor) => {
Reflect.defineMetadata(MetadataKeys.WEBSOCKET, { name: eventName, namespace }, target, propertyKey);
};
}
/**
* Extract endpoints from a controller class
*
* @param controllerClass - Controller class
* @returns Array of endpoint metadata
*/
function extractEndpoints(controllerClass) {
const basePath = Reflect.getMetadata(MetadataKeys.BASE_PATH, controllerClass) || "";
const controllerMiddleware = Reflect.getMetadata(MetadataKeys.MIDDLEWARE, controllerClass) || [];
const endpoints = Reflect.getMetadata(MetadataKeys.ENDPOINTS, controllerClass) || [];
return endpoints.map((endpoint) => {
let methodMiddleware = [];
let validationSchema = null;
let rateLimit = null;
let cache = null;
if (endpoint.name !== undefined) {
methodMiddleware =
Reflect.getMetadata(MetadataKeys.MIDDLEWARE, controllerClass.prototype, endpoint.name) || [];
validationSchema = Reflect.getMetadata(MetadataKeys.VALIDATION, controllerClass.prototype, endpoint.name);
rateLimit = Reflect.getMetadata(MetadataKeys.RATE_LIMIT, controllerClass.prototype, endpoint.name);
cache = Reflect.getMetadata(MetadataKeys.CACHE, controllerClass.prototype, endpoint.name);
}
if (validationSchema) {
const { validateRequest } = require("../core/validation");
methodMiddleware.unshift(validateRequest(validationSchema));
}
if (rateLimit) {
const { rateLimitMiddleware } = require("../utils/rate-limiter");
methodMiddleware.unshift(rateLimitMiddleware(rateLimit));
}
if (cache) {
const { cacheMiddleware } = require("../utils/cache");
methodMiddleware.unshift(cacheMiddleware(cache));
}
return {
...endpoint,
path: `${basePath}${endpoint.path}`,
middleware: [...controllerMiddleware, ...methodMiddleware],
};
});
}
function extractWebSocketEvents(controllerClass) {
const prototype = controllerClass.prototype;
const events = [];
const methodNames = Object.getOwnPropertyNames(prototype).filter((name) => name !== "constructor" && typeof prototype[name] === "function");
methodNames.forEach((methodName) => {
const wsMetadata = Reflect.getMetadata(MetadataKeys.WEBSOCKET, prototype, methodName);
if (wsMetadata) {
events.push({
handler: prototype[methodName],
eventName: wsMetadata.name,
namespace: wsMetadata.namespace,
});
}
});
return events;
}
//# sourceMappingURL=index.js.map