commoneventframework
v1.0.7
Published
Common Event Framework for Lambda AWS
Readme
Common Event Framework (CEF)
Framework Node.js pour AWS Lambda qui normalise les différents types d'événements AWS (API Gateway v1/v2, S3, SQS, SNS, invocation directe) en un CommonEvent unique, et route automatiquement les requêtes vers les handlers déclarés dans un fichier OpenAPI YAML.
Installation
npm install commoneventframeworkInitialiser un projet
La commande cef-init génère automatiquement toute la structure d'un projet prêt à l'emploi :
npx cef-initCela crée les fichiers suivants dans le répertoire courant :
my-lambda/
├── src/
│ ├── index.ts # Point d'entrée Lambda
│ ├── root.yaml # Définition des routes OpenAPI
│ └── handlers/
│ ├── types.ts # Types HandlerFn / InputParserFn
│ └── hello.ts # Handler exemple (GET /hello, GET /hello/:name)
├── .env
├── package.json
└── tsconfig.jsonLes fichiers existants ne sont pas écrasés. Utilisez --force pour forcer la réécriture :
npx cef-init --forceEnsuite :
npm install
npm run dev # Serveur local sur localhost:8888
npm run build:full # Bundle + ZIP pour AWS LambdaDémarrage rapide (manuel)
1. Créer le fichier root.yaml
Créez un fichier root.yaml à côté de votre point d'entrée (src/index.ts). C'est un document OpenAPI 3.0 standard, enrichi de deux extensions custom :
x-handler: référence vers la fonction handler au formatfichier#fonctionx-inputParser(optionnel) : référence vers la fonction de parsing/validation des entrées
openapi: 3.0.3
info:
title: Mon API
version: 1.0.0
paths:
/hello:
get:
x-handler: handlers/hello#getHello
summary: Retourne un message de bienvenue
responses:
'200':
description: Succès
/users/:id:
get:
x-handler: handlers/users#getUser
x-inputParser: handlers/users#parseGetUserInput
summary: Récupère un utilisateur par ID
parameters:
- in: path
name: id
required: true
schema:
type: string
responses:
'200':
description: Utilisateur trouvé
'400':
description: Paramètre manquant
'404':
description: Utilisateur non trouvéNote : les path parameters utilisent la syntaxe
:param(ex./users/:id), compatible avec le moteur de matchingpath-parser.
2. Créer le point d'entrée src/index.ts
import { loadEnv } from "commoneventframework";
loadEnv();
import {
APIGatewayProxyStructuredResultV2,
Context,
buildCommonEvent,
getRouteConfig,
resolveHandlerRef,
Response,
setLambdaContext
} from "commoneventframework";
import { CommonEvent, LambdaEvent } from "commoneventframework/dist/types/commonEvent";
export const handler = async (
event: LambdaEvent,
context: Context
): Promise<APIGatewayProxyStructuredResultV2> => {
setLambdaContext(context);
const commonEvent: CommonEvent = await buildCommonEvent(event);
try {
const routeConfig = getRouteConfig(commonEvent);
if (!routeConfig || !routeConfig.handler) {
return new Response(404, { error: "Route not found" });
}
const handlerFn = resolveHandlerRef(routeConfig.handler);
const parserFn = routeConfig.inputParser
? resolveHandlerRef(routeConfig.inputParser)
: undefined;
if (!handlerFn) {
return new Response(500, { error: `Handler "${routeConfig.handler}" not found` });
}
const input = parserFn ? parserFn(commonEvent) : {};
if (input instanceof Response) return input;
const result = await handlerFn(input, commonEvent);
if (result && 'statusCode' in result) {
return result as APIGatewayProxyStructuredResultV2;
}
return new Response(200, result as object);
} catch (err) {
return new Response(500, err as object);
}
};
// Serveur de développement local
if (process.argv.includes("--local")) {
const { startDevServer } = require("commoneventframework");
startDevServer(handler);
}3. Définir les handlers
Chaque handler est un fichier TypeScript dans src/handlers/ qui exporte une fonction handler et optionnellement une fonction inputParser.
Types
import { CommonEvent } from "commoneventframework/dist/types/commonEvent";
import { APIGatewayProxyStructuredResultV2 } from "commoneventframework";
type InputParserFn = (event: CommonEvent) => any;
type HandlerFn = (input: any, event: CommonEvent) => Promise<APIGatewayProxyStructuredResultV2 | object>;Exemple : src/handlers/hello.ts
import { HandlerFn } from "./types";
export const getHello: HandlerFn = async () => {
return { message: "Bonjour !" };
};Le résultat est automatiquement enveloppé dans une Response(200, ...) par le point d'entrée.
Exemple avec inputParser : src/handlers/users.ts
import { CommonEvent } from "commoneventframework/dist/types/commonEvent";
import { Response } from "commoneventframework";
import { InputParserFn, HandlerFn } from "./types";
interface GetUserInput {
id: string;
}
export const parseGetUserInput: InputParserFn = (event: CommonEvent) => {
const id = event.pathParameters?.id;
if (!id) {
return new Response(400, { error: "Missing path parameter: id" });
}
return { id } as GetUserInput;
};
export const getUser: HandlerFn = async (input: GetUserInput) => {
// ... logique métier
return { user: { id: input.id, name: "Jean" } };
};Validation : si l'inputParser retourne une instance de
Response(ex.new Response(400, ...)), celle-ci est renvoyée directement au client sans appeler le handler.
Concepts clés
CommonEvent
Tous les événements AWS sont normalisés en un objet CommonEvent :
type CommonEvent = {
path: string;
method: string;
body: string | null;
pathParameters: { [name: string]: string | undefined } | undefined;
queryStringParameters: { [name: string]: string | undefined } | undefined;
headers: { [name: string]: string | undefined } | undefined;
version: string;
};Le champ version indique la source de l'événement ("1.0" pour API Gateway v1, "2.0" pour v2, "commonEvent" pour le serveur de dev, etc.).
Événements supportés
| Source AWS | method | path |
|--------------------|-------------------|-------------------------|
| API Gateway v1 | Méthode HTTP | Resource path |
| API Gateway v2 | Méthode HTTP | Route key |
| SQS | PUT | /sqs/{queueName} |
| S3 | PUT | /s3/{bucketName} |
| SNS → S3 | PUT | /s3/{bucketName} |
| Invocation directe | PUT | path de l'événement |
Résolution fichier#fonction
La référence x-handler: handlers/search#searchProducts est résolue dynamiquement :
- Le chemin
handlers/searchest résolu relativement au répertoire du module principal - Le module est chargé via
require() - L'export nommé
searchProductsest extrait et mis en cache
Classe Response
Wrapper autour de APIGatewayProxyStructuredResultV2 qui ajoute automatiquement les headers CORS :
import { Response } from "commoneventframework";
new Response(200, { data: "ok" });
new Response(400, { error: "Bad request" });
new Response(500, { error: "Internal error" }, { "X-Custom": "value" });Headers CORS par défaut :
Access-Control-Allow-Origin: valeur deprocess.env.FRONT_END_URLou*Access-Control-Allow-Methods:OPTIONS,GET,POST,PUT,DELETEAccess-Control-Allow-Credentials:trueContent-Type:application/json
Routes intégrées
Le framework enregistre automatiquement ces routes :
| Route | Description |
|---------------|------------------------------------------------------|
| GET /doc | Interface Swagger UI (via Scalar) |
| GET /doc/spec | Spécification OpenAPI au format JSON |
| GET /context | Informations sur le contexte Lambda |
| GET /dependencies | Liste des dépendances du projet (package.json) |
Développement local
Le framework inclut un serveur HTTP qui simule API Gateway.
Démarrer le serveur
node dist/index.js --local
# ou avec ts-node / nodemon
npx ts-node src/index.ts --local --port 8088Le serveur démarre sur localhost:8888 par défaut (configurable via le 3e argument de process.argv).
Simuler différents types d'événements
Le serveur de dev intercepte les URLs spéciales pour simuler les sources AWS :
| URL du serveur | Type d'événement simulé |
|------------------|-------------------------|
| POST /invoke | Invocation Lambda directe |
| POST /sqs | Message SQS |
| POST /s3event | Événement S3 |
| Toute autre URL | API Gateway (CommonEvent)|
Build et déploiement
Le package fournit un script de build via la commande cef-build :
# Bundle uniquement (esbuild)
npx cef-build
# Bundle + ZIP pour déploiement Lambda
npx cef-build --fullOu dans votre package.json :
{
"scripts": {
"build": "cef-build",
"build:full": "cef-build --full"
}
}Le build utilise esbuild pour bundler src/index.ts vers dist/index.js, puis optionnellement crée dist/dist.zip prêt à être déployé sur AWS Lambda.
Utilitaires
Le package expose également quelques utilitaires :
loadEnv()
Charge les fichiers .env et .env.local depuis le répertoire courant. Doit être appelé avant tout import dépendant de variables d'environnement.
import { loadEnv } from "commoneventframework";
loadEnv();setLambdaContext(context)
Stocke le contexte Lambda pour qu'il soit accessible depuis les routes intégrées (/context).
getEnvValue(key, fallback?)
Récupère une variable d'environnement. Lance une erreur si la variable est absente et qu'aucun fallback n'est fourni.
import { getEnvValue } from "commoneventframework/dist/utils/getEnvValue";
const dbHost = getEnvValue("DB_HOST", "localhost");Structure recommandée d'un projet
my-lambda/
├── src/
│ ├── index.ts # Point d'entrée Lambda + handler principal
│ ├── root.yaml # Définition des routes (OpenAPI + x-handler)
│ └── handlers/
│ ├── types.ts # Types HandlerFn et InputParserFn
│ ├── hello.ts # Handler exemple
│ └── users.ts # Handler avec inputParser
├── dist/ # Sortie du build (esbuild)
├── .env # Variables d'environnement
├── package.json
└── tsconfig.json