@jamx-framework/adapter-cloudflare
v1.0.0
Published
JAMX Framework — Cloudflare Workers adapter
Maintainers
Readme
@jamx-framework/adapter-cloudflare
Descripción
Adaptador de JAMX Framework para Cloudflare Workers. Proporciona una capa de integración que permite ejecutar handlers y routers de JAMX en el entorno serverless de Cloudflare, convirtiendo las peticiones y respuestas nativas de Cloudflare (Request/Response) a la interfaz unificada de JAMX (JamxRequest/JamxResponseBuilder).
Cómo funciona
El adaptador implementa dos funciones principales:
- createCloudflareHandler: Convierte un handler JAMX en un worker exportable con método
fetch - createCloudflareRouter: Crea un router que selecciona handlers basado en el método HTTP
El flujo de ejecución:
- Cloudflare Workers recibe una petición HTTP
toJamxRequest()convierte elRequestnativo aJamxRequest(extrae método, URL, headers, query params, body)- Se ejecuta el handler/routers de JAMX con el
JamxRequesty unCloudflareResponseBuilder CloudflareResponseBuilderacumula status, headers y bodytoResponse()convierte el builder en unResponsenativo de Cloudflare- El worker devuelve la respuesta
Componentes principales
src/adapter.ts
Contiene toda la implementación principal:
- Interfaz
JamxRequest- Representación unificada de peticiones - Interfaz
JamxResponseBuilder- API para construir respuestas - Clase
CloudflareResponseBuilder- Implementación concreta - Funciones
createCloudflareHandlerycreateCloudflareRouter
src/index.ts
Punto de entrada público que re-exporta las APIs principales.
Uso básico
Handler simple
// worker.ts
import { createCloudflareHandler } from '@jamx-framework/adapter-cloudflare';
export default createCloudflareHandler(async (req, res) => {
res.json({ message: 'Hello from JAMX on Cloudflare Workers!' });
});Router por método HTTP
import { createCloudflareRouter } from '@jamx-framework/adapter-cloudflare';
const router = createCloudflareRouter({
GET: async (req, res) => {
res.ok({ method: 'GET', path: req.path });
},
POST: async (req, res) => {
const body = req.body as { name?: string };
res.created({ name: body?.name });
},
PUT: async (req, res) => {
res.ok({ method: 'PUT' });
},
DELETE: async (req, res) => {
res.noContent();
},
});
export default router;Manejo de query params y body
export default createCloudflareHandler(async (req, res) => {
// Query params
const page = req.query['page'] ?? '1';
const limit = req.query['limit'] ?? '10';
// Body (JSON)
const body = req.body as { name?: string; email?: string } | undefined;
if (req.method === 'POST' && body?.name && body?.email) {
// Procesar creación
res.created({ name: body.name, email: body.email });
} else {
// Listar recursos
res.ok({
page: Number(page),
limit: Number(limit),
items: []
});
}
});Middleware de autenticación
const authMiddleware = async (req: JamxRequest, res: JamxResponseBuilder, next: () => void) => {
const authHeader = req.headers['authorization'];
if (!authHeader) {
res.json({ error: 'Unauthorized' }, 401);
return;
}
const token = authHeader.split(' ')[1];
try {
const user = await verifyToken(token);
req.locals.user = user;
next();
} catch (err) {
res.json({ error: 'Invalid token' }, 401);
}
};
export default createCloudflareHandler(authMiddleware, async (req, res) => {
res.json({ user: req.locals.user });
});API Reference
Tipos
JamxRequest
interface JamxRequest {
method: string; // Método HTTP (GET, POST, etc.)
url: string; // URL completa
path: string; // Solo el path (sin dominio ni query)
headers: Record<string, string>; // Headers normalizados a minúsculas
query: Record<string, string>; // Query parameters
body: unknown; // Cuerpo de la petición (JSON, texto, etc.)
params: Record<string, string>; // Parámetros de ruta (para routers)
locals: Record<string, unknown>; // Datos locales para middleware
}JamxResponseBuilder
Métodos disponibles:
status(code: number): this- Establece el código de estadoheader(name: string, value: string): this- Añade un headerjson(body: unknown, status?: number): void- Envía respuesta JSONtext(body: string, status?: number): void- Envía respuesta textosend(body: string, status?: number): void- Envía body crudook(body: unknown): void- Respuesta 200 OKcreated(body: unknown): void- Respuesta 201 CreatednoContent(): void- Respuesta 204 No ContentnotFound(message?: string): void- Respuesta 404 Not Foundraw: { writeHead(status, headers): void; end(body): void; statusCode?: number }- Acceso a API nativa
Funciones
createCloudflareHandler
function createCloudflareHandler(
handler: JamxHandler
): { fetch(request: Request): Promise<Response> }Convierte un handler JAMX en un worker exportable.
createCloudflareRouter
function createCloudflareRouter(
handlers: Partial<Record<string, JamxHandler>>
): { fetch(request: Request): Promise<Response> }Crea un router que selecciona el handler según req.method.
Flujo interno
Conversión de Request a JamxRequest
- Se crea un objeto URL desde
request.url - Headers se convierten a
Record<string, string>con claves en minúsculas - Query parameters se extraen de
URL.searchParams - Body se parsea según
Content-Type:application/json→request.json()text/*→request.text()- Otros →
undefined
Construcción de Response
CloudflareResponseBuilderacumula internamente:_status(default: 200)_headers(default:{ 'content-type': 'application/json' })_body(string)
- Los métodos como
json(),text(), etc. establecen status, headers y body toResponse()crea unResponsenativo de Cloudflare con los valores acumulados
Manejo de raw
La propiedad raw proporciona acceso a la API nativa de Cloudflare Workers para casos avanzados donde se necesita control total sobre la respuesta.
Configuración
tsconfig.json
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist",
"tsBuildInfoFile": "dist/.tsbuildinfo"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "tests"]
}Scripts disponibles
pnpm build- Compila TypeScript a JavaScriptpnpm dev- Compilación en watch modepnpm test- Ejecuta tests unitariospnpm test:watch- Tests en watch modepnpm type-check- Verifica tipos sin compilarpnpm clean- Limpia archivos compilados
Testing
El paquete incluye tests unitarios en tests/unit/adapter.test.ts que cubren:
- Conversión de Request/Response
- Parseo de body JSON
- Extracción de query params
- Paso de headers
- Estados de respuesta (204, 404, etc.)
- Enrutamiento por método HTTP
- Respuesta 405 para métodos no soportados
Ejecutar tests:
pnpm testConsideraciones de rendimiento
- Zero-copy headers: Los headers se copian una sola vez durante la conversión
- Edge optimized: Diseñado para el entorno edge de Cloudflare con tiempos de inicio mínimos
- Sin dependencias externas: Solo requiere tipos de Cloudflare Workers
- Streaming: El body se almacena en memoria como string; para respuestas streaming se necesitaría extensión
Compatibilidad
- Cloudflare Workers (ES Modules)
- Web API estándar (Request, Response)
- Todos los métodos HTTP
- TypeScript completo con tipos definidos
Dependencias
@types/node- Tipos de Node.js para desarrollovitest- Framework de testingrimraf- Limpieza de directorios
Publicación
El paquete está configurado para publicarse en npm como @jamx-framework/adapter-cloudflare con acceso público.
Integración con CLI
jamx build- Compila el adaptadorjamx deploy- Despliega a Cloudflare Workers (requiere Wrangler configurado)
Ejemplo completo
// worker.ts
import { createCloudflareHandler } from '@jamx-framework/adapter-cloudflare';
interface User {
id: number;
name: string;
email: string;
}
// Base de datos simulada
const users: User[] = [];
export default createCloudflareHandler(async (req, res) => {
switch (req.method) {
case 'GET':
res.ok(users);
break;
case 'POST':
const body = req.body as { name: string; email: string };
if (!body.name || !body.email) {
res.json({ error: 'Name and email required' }, 400);
return;
}
const newUser: User = {
id: users.length + 1,
name: body.name,
email: body.email,
};
users.push(newUser);
res.created(newUser);
break;
default:
res.notFound('Endpoint not found');
}
});