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.

Cuándo usarlo

EnfoqueIdeal para
Basado en archivos src/routes/_api/*Handlers REST colocalizados con rutas, similar a Next.js Route Handlers
Módulo handler personalizadoRouters 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 devApi desde este archivo y registra middleware dev
  • Habilita sqlRawPlugin para imports *.sql?raw
  • Recurre a src/routes/_api/* cuando se omite devApi

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.

Cómo se enrutan las peticiones

  1. El pathname de la URL es /api o empieza con /api/.
  2. El middleware dev se ejecuta antes del render SSR de páginas (SSR omite URLs API).
  3. 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:

ExportDescripció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

Relacionado