@noony-serverless/core
Version:
A Middy base framework compatible with Firebase and GCP Cloud Functions with TypeScript
256 lines • 8.17 kB
TypeScript
import { BaseMiddleware, Context } from '../core';
/**
* Middleware to inject dependencies into the request context using typedi.
* This allows handlers to access shared services or data via context.container.
*
* @template TBody - The type of the request body payload (preserves type chain)
* @template TUser - The type of the authenticated user (preserves type chain)
* @implements {BaseMiddleware<TBody, TUser>}
*
* @example
* Basic service injection:
* ```typescript
* import { Container } from 'typedi';
* import { Handler, DependencyInjectionMiddleware } from '@noony-serverless/core';
*
* // Define services
* class UserService {
* constructor(private userRepo: Map<string, any>) {}
*
* async findById(id: string) {
* return this.userRepo.get(id);
* }
*
* async create(userData: any) {
* const id = generateId();
* this.userRepo.set(id, { id, ...userData });
* return { id, ...userData };
* }
* }
*
* class EmailService {
* async sendWelcomeEmail(email: string) {
* console.log(`Sending welcome email to ${email}`);
* return { sent: true, email };
* }
* }
*
* // Initialize services
* const services = [
* { id: UserService, value: new UserService(new Map()) },
* { id: EmailService, value: new EmailService() },
* { id: 'config', value: { apiUrl: 'https://api.example.com' } }
* ];
*
* const userHandler = new Handler()
* .use(new DependencyInjectionMiddleware(services))
* .handle(async (context) => {
* const userService = context.container?.get(UserService);
* const emailService = context.container?.get(EmailService);
* const config = context.container?.get('config');
*
* const user = await userService.create({ name: 'John', email: 'john@example.com' });
* await emailService.sendWelcomeEmail(user.email);
*
* return { success: true, user, apiUrl: config.apiUrl };
* });
* ```
*
* @example
* Database and caching services:
* ```typescript
* class DatabaseService {
* async query(sql: string, params: any[]) {
* // Mock database query
* return { rows: [{ id: 1, name: 'Test' }] };
* }
* }
*
* class CacheService {
* private cache = new Map();
*
* get(key: string) {
* return this.cache.get(key);
* }
*
* set(key: string, value: any, ttl: number = 3600) {
* this.cache.set(key, value);
* setTimeout(() => this.cache.delete(key), ttl * 1000);
* }
* }
*
* const services = [
* { id: 'db', value: new DatabaseService() },
* { id: 'cache', value: new CacheService() }
* ];
*
* const dataHandler = new Handler()
* .use(new DependencyInjectionMiddleware(services))
* .handle(async (context) => {
* const db = context.container?.get('db');
* const cache = context.container?.get('cache');
*
* const cacheKey = `users:${context.params.id}`;
* let user = cache.get(cacheKey);
*
* if (!user) {
* const result = await db.query('SELECT * FROM users WHERE id = ?', [context.params.id]);
* user = result.rows[0];
* cache.set(cacheKey, user);
* }
*
* return { success: true, user, fromCache: !!cache.get(cacheKey) };
* });
* ```
*
* @example
* Business logic services with complex dependencies:
* ```typescript
* class PaymentService {
* async processPayment(amount: number, method: string) {
* return { transactionId: 'txn_123', status: 'completed', amount };
* }
* }
*
* class OrderService {
* constructor(
* private paymentService: PaymentService,
* private emailService: EmailService,
* private inventoryService: any
* ) {}
*
* async createOrder(orderData: any) {
* // Complex business logic
* const payment = await this.paymentService.processPayment(orderData.total, orderData.paymentMethod);
* await this.emailService.sendWelcomeEmail(orderData.customerEmail);
* return { orderId: 'order_123', payment };
* }
* }
*
* const paymentService = new PaymentService();
* const emailService = new EmailService();
* const inventoryService = { checkStock: () => true };
*
* const services = [
* { id: PaymentService, value: paymentService },
* { id: EmailService, value: emailService },
* { id: 'inventory', value: inventoryService },
* { id: OrderService, value: new OrderService(paymentService, emailService, inventoryService) }
* ];
*
* const checkoutHandler = new Handler()
* .use(new DependencyInjectionMiddleware(services))
* .handle(async (context) => {
* const orderService = context.container?.get(OrderService);
* const order = await orderService.createOrder(context.req.parsedBody);
* return { success: true, order };
* });
* ```
*/
export declare class DependencyInjectionMiddleware<TBody = unknown, TUser = unknown> implements BaseMiddleware<TBody, TUser> {
private services;
constructor(services: {
id: any;
value: any;
}[]);
before(context: Context<TBody, TUser>): Promise<void>;
}
/**
* Factory function that creates a dependency injection middleware.
* Creates a new container instance for each request to avoid shared state issues.
*
* @template TBody - The type of the request body payload (preserves type chain)
* @template TUser - The type of the authenticated user (preserves type chain)
* @param services - Array of service definitions with id and value
* @returns BaseMiddleware object with dependency injection logic
*
* @example
* Simple services injection:
* ```typescript
* import { Handler, dependencyInjection } from '@noony-serverless/core';
*
* const logger = {
* info: (msg: string) => console.log(`[INFO] ${msg}`),
* error: (msg: string) => console.error(`[ERROR] ${msg}`)
* };
*
* const config = {
* apiKey: process.env.API_KEY,
* environment: process.env.NODE_ENV
* };
*
* const services = [
* { id: 'logger', value: logger },
* { id: 'config', value: config }
* ];
*
* const apiHandler = new Handler()
* .use(dependencyInjection(services))
* .handle(async (context) => {
* const logger = context.container?.get('logger');
* const config = context.container?.get('config');
*
* logger.info(`Processing request in ${config.environment}`);
* return { success: true, environment: config.environment };
* });
* ```
*
* @example
* Repository pattern with database:
* ```typescript
* class UserRepository {
* constructor(private db: any) {}
*
* async findAll() {
* return await this.db.users.findMany();
* }
*
* async findById(id: string) {
* return await this.db.users.findUnique({ where: { id } });
* }
* }
*
* const mockDb = {
* users: {
* findMany: () => Promise.resolve([{ id: '1', name: 'John' }]),
* findUnique: ({ where }: any) => Promise.resolve({ id: where.id, name: 'John' })
* }
* };
*
* const userRepo = new UserRepository(mockDb);
*
* const userHandler = new Handler()
* .use(dependencyInjection([
* { id: UserRepository, value: userRepo },
* { id: 'database', value: mockDb }
* ]))
* .handle(async (context) => {
* const repo = context.container?.get(UserRepository);
* const users = await repo.findAll();
* return { success: true, users };
* });
* ```
*
* @example
* Empty services for middleware registration:
* ```typescript
* // Sometimes you just want to enable DI without initial services
* const baseHandler = new Handler()
* .use(dependencyInjection()) // Empty services array
* .use(async (context, next) => {
* // Add services dynamically based on request
* const requestService = new RequestSpecificService(context.req.headers);
* context.container?.set('requestService', requestService);
* return next();
* })
* .handle(async (context) => {
* const service = context.container?.get('requestService');
* return { success: true, data: service.process() };
* });
* ```
*/
export declare const dependencyInjection: <TBody = unknown, TUser = unknown>(services?: {
id: any;
value: any;
}[]) => BaseMiddleware<TBody, TUser>;
//# sourceMappingURL=dependencyInjectionMiddleware.d.ts.map