Tabla de Contenidos
Descripcion General
@fiado/gateway-adapter es la libreria core del ecosistema Fiado que actua como router central para todos los eventos que recibe una funcion Lambda. Proporciona:
- Enrutamiento automatico de eventos API Gateway, SQS y EventBridge
- Transformacion de requests/responses con tipado fuerte
- Sistema de autorizacion basado en decoradores y features
- Decodificacion de tokens JWT de Cognito
- Respuestas HTTP estandarizadas con CORS configurado
- Encriptacion JWE opcional para datos sensibles
- Registro de actividad de sesion automatico
Proposito
En el modelo de desarrollo Fiado, cada Lambda recibe multiples tipos de eventos:
- API Gateway: Peticiones HTTP (publicas, privadas, backoffice)
- SQS: Mensajes de colas para procesamiento asincrono
- EventBridge: Eventos entre servicios
Esta libreria unifica el manejo de todos estos eventos en un unico punto de entrada.
Arquitectura
LAMBDA HANDLER
index.ts
|
v
+---------------+
| GatewayAdapter | <-- Router Principal
+-------+-------+
|
+-------------------+-------------------+
v v v
+-----------------+ +-----------------+ +-----------------+
|ApiGatewayAdapter| |QueueGatewayAdapter| |EventBridgeAdapter|
+--------+--------+ +--------+--------+ +--------+--------+
| | |
v v v
+-----------------+ +-----------------+ +-----------------+
| Controllers | | SQS Subscriber | | EB Subscriber |
| Public/Private/ | |IMessageSubscriber| |IEventBridgeSub |
| Backoffice | | | | |
+-----------------+ +-----------------+ +-----------------+
Flujo de Deteccion de Eventos
El GatewayAdapter detecta automaticamente el tipo de evento:
- API Gateway: Si
event.httpMethod existe
- SQS: Si
event.Records[0].eventSource === 'aws:sqs'
- EventBridge: Si
event.source === 'EVENT_BRIDGE'
Instalacion
npm install @fiado/gateway-adapter
Peer Dependencies
{
"@fiado/logger": "^1.0.2",
"@fiado/type-kit": "^2.1.35",
"inversify": "^6.2.2",
"reflect-metadata": "^0.2.2"
}
Configuracion
Registro en el Contenedor IoC
import { Container } from "inversify";
import {
gatewayAdapterBindings,
TypeQueue,
TypeEventBridge,
IController,
IMessageSubscriber,
IEventBridgeMessageSubscriber
} from "@fiado/gateway-adapter";
const container = new Container();
container.load(gatewayAdapterBindings);
container.bind<IController>('PublicController').to(PublicController);
container.bind<IController>('PrivateController').to(PrivateController);
container.bind<IController>('BackofficeController').to(BackofficeController);
container.bind<IMessageSubscriber<any>>("SQSMessageSubscriber").to(MySqsSubscriber);
container.bind<() => IMessageSubscriber<any>>(TypeQueue.SQSMessageSubscriberFactory)
.toAutoFactory("SQSMessageSubscriber");
container.bind<IEventBridgeMessageSubscriber<any>>("EventBridgeMessageSubscriber")
.to(MyEventBridgeSubscriber);
container.bind<() => IEventBridgeMessageSubscriber<any>>(
TypeEventBridge.EventBridgeMessageSubscriberFactory
).toAutoFactory("EventBridgeMessageSubscriber");
export { container };
Entry Point del Lambda
import 'reflect-metadata';
import warmer from "lambda-warmer";
import { container } from './container.config';
import { GatewayAdapter, TypeGateway } from '@fiado/gateway-adapter';
import { log } from "@fiado/logger";
export const handler = async (event: any, context: any) => {
if (await warmer(event)) {
return 'warmed';
}
log.info(`Event received: ${JSON.stringify(event)}`);
const gatewayAdapter = container.get<GatewayAdapter>(TypeGateway.GatewayAdapter);
return await gatewayAdapter.execute(event);
}
GatewayAdapter - Router Principal
El GatewayAdapter es el componente central que detecta el tipo de evento y lo enruta al adaptador correspondiente.
Tipos de Eventos Soportados
| API Gateway | event.httpMethod existe | ApiGatewayAdapter |
| SQS | event.Records[0].eventSource === 'aws:sqs' | QueueGatewayAdapter |
| EventBridge | event.source === 'EVENT_BRIDGE' | EventBridgeGatewayAdapter |
Uso
import { GatewayAdapter, TypeGateway } from '@fiado/gateway-adapter';
const gatewayAdapter = container.get<GatewayAdapter>(TypeGateway.GatewayAdapter);
const result = await gatewayAdapter.execute(event);
API Gateway
ApiGatewayRequest
Interface tipada que representa una peticion HTTP transformada:
interface ApiGatewayRequest<TBody> {
body: TBody | null;
headers: { [key: string]: string | undefined };
method: string;
pathParameter: { [key: string]: string | undefined };
queryStringParameters: { [key: string]: string };
multiValueQueryStringParameters: { [key: string]: string[] };
token: TokenPayload | undefined | null;
operationName: string | null;
base64EncodedFile?: string;
requestContext: { identity?: { sourceIp?: string } };
}
Controladores
Los controladores se identifican por prefijo en el operationName:
public* | PublicController | Endpoints publicos (internet) |
private* | PrivateController | Endpoints privados (VPC) |
backoffice* | BackofficeController | Endpoints administrativos |
agent* / agents* | AgentController | Endpoints de agentes |
Ejemplo de Controlador
import { injectable, inject } from 'inversify';
import {
IController,
ApiGatewayRequest,
ApiResponse,
AuthorizedFeatures,
Feature,
Endpoint,
HttpMethod,
SecurityType
} from '@fiado/gateway-adapter';
@injectable()
export class PublicController implements IController {
constructor(
@inject("IUserManager") private userManager: IUserManager
) {}
@AuthorizedFeatures(Feature.ANONIMUS)
@Endpoint({
method: HttpMethod.GET,
path: '/users/:id',
summary: 'Obtener usuario por ID',
tags: ['PublicController'],
security: SecurityType.CognitoAppAuthorizer
})
async getUser(request: ApiGatewayRequest<void>): Promise<any> {
const userId = request.pathParameter?.id;
const user = await this.userManager.getById(userId);
return ApiResponse.success({ data: user });
}
@AuthorizedFeatures(Feature.ANONIMUS)
@Endpoint({
method: HttpMethod.POST,
path: '/users',
summary: 'Crear usuario',
tags: ['PublicController'],
security: SecurityType.CognitoAppAuthorizer
})
async createUser(request: ApiGatewayRequest<CreateUserDto>): Promise<any> {
const user = await this.userManager.create(request.body);
return ApiResponse.created({ data: user });
}
}
TokenService
Servicio para decodificar tokens JWT de Cognito:
import { TokenService } from '@fiado/gateway-adapter';
const tokenService = new TokenService();
const payload = tokenService.decodeToken(bearerToken);
Colas SQS
IMessageSubscriber
Interface para procesar mensajes de SQS:
export interface IMessageSubscriber<T> {
handle(messageBody: T, receiptHandle: string): Promise<void>;
deleteMessage(queueUrl: string, receiptHandle: string): Promise<void>;
}
Implementacion de Subscriber
import { injectable } from 'inversify';
import { IMessageSubscriber } from '@fiado/gateway-adapter';
import { log } from '@fiado/logger';
@injectable()
export class OrderMessageSubscriber implements IMessageSubscriber<OrderMessage> {
async handle(messageBody: OrderMessage, receiptHandle: string): Promise<void> {
log.info('Processing order message', { orderId: messageBody.orderId });
await this.processOrder(messageBody);
}
async deleteMessage(queueUrl: string, receiptHandle: string): Promise<void> {
}
private async processOrder(order: OrderMessage): Promise<void> {
}
}
IMessagePublisher
Interface para publicar mensajes a SQS:
export interface IMessagePublisher<T> {
publish(messageBody: T, options?: PublishOptions): Promise<void>;
}
EventBridge
IEventBridgeMessageSubscriber
Interface para procesar eventos de EventBridge:
import { EventBridgeMessageRequest } from "@fiado/type-kit";
export interface IEventBridgeMessageSubscriber<TPayload> {
handle(message: EventBridgeMessageRequest<TPayload>): Promise<void>;
}
Implementacion de Subscriber
import { injectable } from 'inversify';
import { IEventBridgeMessageSubscriber } from '@fiado/gateway-adapter';
import { EventBridgeMessageRequest } from '@fiado/type-kit';
@injectable()
export class PaymentEventSubscriber implements IEventBridgeMessageSubscriber<PaymentEvent> {
async handle(message: EventBridgeMessageRequest<PaymentEvent>): Promise<void> {
const { payload, eventType } = message;
switch (eventType) {
case 'PAYMENT_COMPLETED':
await this.handlePaymentCompleted(payload);
break;
case 'PAYMENT_FAILED':
await this.handlePaymentFailed(payload);
break;
}
}
}
Decoradores
@AuthorizedFeatures
Decorador para definir que features/permisos pueden acceder a un metodo:
import { AuthorizedFeatures, Feature } from '@fiado/gateway-adapter';
@AuthorizedFeatures(Feature.ANONIMUS)
async publicEndpoint(request: ApiGatewayRequest<any>): Promise<any> { }
@AuthorizedFeatures(Feature.PAYROLL)
async payrollEndpoint(request: ApiGatewayRequest<any>): Promise<any> { }
@AuthorizedFeatures(Feature.ADMIN, Feature.MANAGER)
async adminEndpoint(request: ApiGatewayRequest<any>): Promise<any> { }
Feature Enum
export enum Feature {
ANONIMUS = "ANONIMUS",
TEST = "test",
FEATURE1 = "feature1",
PAYROLL = "PAYROLL"
}
@Endpoint
Decorador para definir metadata del endpoint (usado para generacion OpenAPI):
import { Endpoint, HttpMethod, SecurityType } from '@fiado/gateway-adapter';
@Endpoint({
method: HttpMethod.POST,
path: '/payments',
summary: 'Procesar pago',
tags: ['PaymentController'],
security: SecurityType.CognitoAppAuthorizer,
queryParams: ['currency'],
pathParams: ['id']
})
async processPayment(request: ApiGatewayRequest<PaymentDto>): Promise<any> { }
HttpMethod Enum
export enum HttpMethod {
GET = 'get',
POST = 'post',
PUT = 'put',
DELETE = 'delete'
}
SecurityType Enum
export enum SecurityType {
CognitoAppAuthorizer = 'CognitoAppAuthorizer',
CognitoBackofficeAuthorizer = 'CognitoBackofficeAuthorizer',
CognitoCombinedAuthorizer = 'CognitoCombinedAuthorizer',
CognitoAgentAuthorizer = 'CognitoAgentAuthorizer'
}
Sistema de Respuestas
ApiResponse
Clase estatica para construir respuestas HTTP estandarizadas:
import { ApiResponse } from '@fiado/gateway-adapter';
return ApiResponse.success({ data: result, msg: 'Operacion exitosa' });
return ApiResponse.created({ data: newResource });
return ApiResponse.badRequest({ code: 'VALIDATION_ERROR', msg: 'Campo requerido' });
return ApiResponse.unauthorized({ msg: 'Token invalido' });
return ApiResponse.forbidden({ msg: 'Acceso denegado' });
return ApiResponse.notFound({ msg: 'Recurso no encontrado' });
return ApiResponse.tooEarly({ msg: 'Intente mas tarde' });
return ApiResponse.internalServerError({ msg: 'Error interno' });
return ApiResponse.redirect({ data: { url: 'https://...' } });
return ApiResponse.redirectTo('https://example.com/callback', 302);
Estructura de Respuesta
Todas las respuestas siguen el formato estandar:
{
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Methods": "*",
"Content-Type": "application/json"
},
body: {
code: "PROCESS_OK",
date: "2024-01-15T...",
data: { ... },
description: null,
msg: null,
encrypted: false
}
}
ResponseOptions
interface ResponseOptions<T> {
data?: T;
code?: string;
msg?: string;
description?: string;
encrypted?: boolean;
}
Encriptacion JWE
JweEncryptionService
Servicio para encriptar respuestas sensibles usando JWE (JSON Web Encryption):
Configuracion
Variables de entorno para habilitar encriptacion:
Environment:
Variables:
JWE_ENCRYPTION_APP: "true"
JWE_ENCRYPTION_APP_ENDPOINTS: "publicGetBalance,publicGetTransactions"
JWE_ENCRYPTION_BACKOFFICE: "true"
JWE_ENCRYPTION_BACKOFFICE_ENDPOINTS: "backofficeGetUserData"
Flujo de Encriptacion
- Cliente envia header
x-session-key con session key encriptada (JWE con llave publica RSA)
- Lambda descifra session key usando llave privada de Secrets Manager
- Respuesta
data se encripta con AES-256-GCM usando session key
- Cliente descifra respuesta con su session key
Uso Automatico
La encriptacion se aplica automaticamente si:
- El endpoint esta configurado para encriptar
- El cliente envia el header
x-session-key
- El servicio puede inicializarse correctamente
return ApiResponse.success({ data: sensitiveData });
{
code: "PROCESS_OK",
data: "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0...",
encrypted: true
}
Estructura del Proyecto
src/
├── index.ts # Exportaciones publicas
├── container.config.ts # ContainerModule con bindings
├── GatewayAdapter.ts # Router principal
│
├── apigateway/ # Modulo API Gateway
│ ├── abstractions/
│ │ ├── ApiGatewayRequest.ts
│ │ ├── ApiGatewayResponse.ts
│ │ ├── FeatureEnum.ts
│ │ ├── IController.ts
│ │ ├── ResponseOptions.ts
│ │ ├── StandardResponse.ts
│ │ └── TypeGateway.ts
│ ├── decorators/
│ │ └── AuthorizedFeatures.ts
│ └── services/
│ ├── ActivitySessionService.ts
│ ├── ApiGatewayAdapter.ts
│ ├── ApiResponse.ts
│ ├── JweEncryptionService.ts
│ └── TokenService.ts
│
├── queue/ # Modulo SQS
│ ├── abstractions/
│ │ ├── IMessagePublisher.ts
│ │ ├── IMessageSubscriber.ts
│ │ ├── IPublishOptions.ts
│ │ └── TypeQueue.ts
│ ├── decorators/
│ └── services/
│ └── QueueGatewayAdapter.ts
│
├── eventBridge/ # Modulo EventBridge
│ ├── abstractions/
│ │ ├── IEventBridgeMessageSubscriber.ts
│ │ └── TypeEventBridge.ts
│ └── service/
│ └── EventBridgeGatewayAdapter.ts
│
└── endpoint/ # Decorador @Endpoint
└── endpointDecorator.ts
Dependencias
Runtime
@fiado/api-invoker | ^1.4.6 | Invocacion de APIs internas |
@fiado/logger | ^1.0.2 | Logging estructurado |
@fiado/type-kit | ^2.1.35 | DTOs y tipos compartidos |
aws-lambda | ^1.0.7 | Tipos de AWS Lambda |
inversify | ^6.2.2 | Inyeccion de dependencias |
jose | ^4.15.5 | Encriptacion JWE |
jsonwebtoken | ^9.0.2 | Decodificacion JWT |
reflect-metadata | ^0.2.2 | Metadatos para decoradores |
uuid | ^9.0.1 | Generacion de UUIDs |
Desarrollo
@types/jsonwebtoken | ^9.0.6 | Tipos de jsonwebtoken |
Compilacion y Publicacion
Build
npm run build
Genera archivos .js y .d.ts en el directorio bin/.
Publicacion
npm version patch|minor|major
npm publish
Fiado Inc. | @fiado/gateway-adapter v1.1.59
`