Dev API
Durante emberkit dev, EmberKit puede interceptar /api y /api/* y ejecutar tu lógica de backend en Node — sin un servidor proxy separado.
Producción
El middleware Dev API es solo para desarrollo. En producción, conecta las APIs a través de tu host (Cloudflare Workers, servidor Node, etc.). El sitio Orange Ember usa worker.ts en producción y devApiPlugin en local.
Cuándo usarlo
| Enfoque | Ideal para |
|---|---|
Basado en archivos src/routes/_api/* | Handlers REST colocalizados con rutas, similar a Next.js Route Handlers |
| Módulo handler personalizado | Routers estilo Express existentes, migraciones Turso en primera petición, handleApiRequest compartido para dev y Workers |
Recomendado — emberkit.config.ts
import { defineConfig } from '@emberkit/core';
import { emberkitVitePlugin } from '@emberkit/core/vite-plugin';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
mode: 'ssr',
devApi: {
handler: './src/server/api-router.node.ts',
export: 'handleApiRequestNode',
prefix: '/api', // optional, default '/api'
},
vite: {
plugins: [emberkitVitePlugin(), tailwindcss()],
},
});
emberkitVitePlugin() automáticamente:
- Carga
devApidesde este archivo y registra middleware dev - Habilita
sqlRawPluginpara imports*.sql?raw - Recurre a
src/routes/_api/*cuando se omitedevApi
Opción B — plugins Vite explícitos (avanzado)
Solo necesario si no puedes usar devApi en emberkit.config.ts o necesitas un orden de plugins personalizado:
import {
devApiPlugin,
emberkitVitePlugin,
sqlRawPlugin,
} from '@emberkit/core/vite-plugin';
export const appVitePlugins = [
sqlRawPlugin(),
devApiPlugin({
handler: './src/server/api-router.node.ts',
export: 'handleApiRequestNode',
}),
emberkitVitePlugin(),
];
No dupliques devApi en config y devApiPlugin al mismo tiempo.
Handler Node personalizado
Exporta una función con la firma IncomingMessage / ServerResponse de Node:
// src/server/api-router.node.ts
import type { IncomingMessage, ServerResponse } from 'node:http';
import { handleApiRequest } from './api-router.ts';
export async function handleApiRequestNode(
req: IncomingMessage,
res: ServerResponse,
): Promise<void> {
const host = req.headers.host ?? 'localhost';
const url = `http://${host}${req.url ?? '/'}`;
const request = new Request(url, { method: req.method, headers: req.headers });
const response = await handleApiRequest(request, env);
// copy status, headers, body to res ...
}
Comparte handleApiRequest(request, env) entre dev (Node) y producción (Workers) para mantener el comportamiento consistente.
Usa createNodeDevApiHandler para evitar duplicar el puente seguro de headers/cookies:
import { createNodeDevApiHandler } from '@emberkit/core/vite-plugin';
import { handleApiRequest } from './api-router.ts';
export const handleApiRequestNode = createNodeDevApiHandler(
handleApiRequest,
() => ({
TURSO_DATABASE_URL: process.env.TURSO_DATABASE_URL ?? '',
// ...
}),
);
Rutas API basadas en archivos
Añade handlers bajo src/routes/_api/:
src/routes/_api/
health.ts → GET /api/health
blog/
list.ts → GET /api/blog/list
Cuando devApi no está en config y existe al menos una ruta _api, EmberKit habilita automáticamente el routing dev API basado en archivos.
devApi personalizado anula auto routing
Si configuras devApi.handler, las rutas _api basadas en archivos no se usan salvo que las invoques desde tu handler.
Cómo se enrutan las peticiones
- El pathname de la URL es
/apio empieza con/api/. - El middleware dev se ejecuta antes del render SSR de páginas (SSR omite URLs API).
- El módulo handler se carga una vez vía
server.ssrLoadModule()y se cachea.
Pruebas locales
pnpm dev
curl -s "http://localhost:4321/api/health"
curl -s "http://localhost:4321/api/blog/list?lang=en"
Si el puerto está ocupado, EmberKit elige el siguiente libre — consulta el banner del dev server.
Exports de Vite
Desde @emberkit/core/vite-plugin:
| Export | Descripción |
|---|---|
devApiPlugin(options) | Plugin Vite para handler personalizado |
registerDevApiMiddleware(server, options) | Registrar middleware en un dev server existente |
registerFileBasedDevApiMiddleware(server) | Registrar routing de archivos _api |
isApiRequest(url) | Devuelve true para /api y /api/* |
incomingMessageToRequest(req) | Convertir petición Node a Fetch Request |
writeFetchResponseToNode(res, response) | Escribir Fetch Response a Node |