Rutas de API

En esta página

Los plugins pueden exponer rutas de API para su UI admin e integraciones externas. Las rutas se montan bajo /_emdash/api/plugins/<slug>/<nombre-ruta> y se ejecutan dentro del runtime del sandbox con el mismo PluginContext que reciben los hooks.

Esta página cubre plugins sandboxed. La superficie de API para plugins nativos es la misma; la única diferencia es la firma del handler — ver Plugins nativos.

Definir rutas

import type { SandboxedPlugin } from "emdash/plugin";
import { z } from "astro/zod";

export default {
	routes: {
		status: {
			handler: async (_routeCtx, ctx) => {
				return { ok: true, plugin: ctx.plugin.id };
			},
		},
		submissions: {
			input: z.object({
				formId: z.string().optional(),
				limit: z.number().default(50),
				cursor: z.string().optional(),
			}),
			handler: async (routeCtx, ctx) => {
				const { formId, limit, cursor } = routeCtx.input;
				const result = await ctx.storage.submissions.query({
					where: formId ? { formId } : undefined,
					orderBy: { createdAt: "desc" },
					limit, cursor,
				});
				return result;
			},
		},
	},
} satisfies SandboxedPlugin;

Los handlers de rutas sandboxed toman dos argumentos: (routeCtx, ctx).

Filtrar campos de contenido indexados

const result = await ctx.content.list("items", {
	where: {
		fieldFilters: {
			priority: { in: ["urgent", "high"] },
			score: { gte: 80 },
			resolved: false,
		},
	},
});

URLs de rutas

Plugin idNombre de rutaURL
formsstatus/_emdash/api/plugins/forms/status
formssubmissions/_emdash/api/plugins/forms/submissions

Autenticación y CSRF

Las rutas de plugin están autenticadas por defecto. Usa permission para una verificación más específica. Para rutas públicas, marca public: true.

El llamador autenticado

En rutas privadas, routeCtx.user es el usuario autenticado.

Cachear respuestas públicas

routes: {
	catalog: {
		public: true,
		cacheControl: "public, max-age=60, stale-while-revalidate=300",
		handler: async (ctx) => listProducts(ctx),
	},
},

Exponer una ruta como herramienta MCP

mcp: {
	tools: {
		createEvent: {
			description: "Create a calendar event when the user asks to add one.",
			route: "events/create",
			input: createEventInput,
			output: z.object({ id: z.string() }),
			destructive: false,
		},
	},
},

Validación de entrada

input acepta un esquema Zod.

Entrada de query string (GET/HEAD/DELETE)

Usa z.coerce para campos no string.

Valores de retorno

Devuelve cualquier valor serializable a JSON. El dispatcher lo envuelve en { success: true, data: <tu valor> }.

Errores

Lanza un error para una respuesta de error. Para un código de estado específico, lanza un Response.

Métodos HTTP

Las rutas responden a todos los métodos. Bifurca en routeCtx.request.method.

Patrones comunes

Configuración vía KV

Lista paginada

Proxy de API externa

Llamar rutas desde la UI admin

import { usePluginAPI } from "@emdash-cms/admin";
function SettingsPage() {
	const api = usePluginAPI();
	const handleSave = async (settings) => { await api.post("settings/save", settings); };
}

Llamar rutas desde handlers de cola y programados

Usa withEmDashRuntime() de emdash/middleware.

Llamar rutas externamente

Las rutas públicas son llamables directamente. Las rutas privadas necesitan credenciales de sesión o un token API.

Referencia del contexto de ruta

interface SandboxedRouteContext {
	input: unknown;
	request: SandboxedRequest;
	requestMeta?: unknown;
	user?: UserInfo;
}

interface PluginContext {
	plugin: { id: string; version: string };
	storage: PluginStorage;
	kv: KVAccess;
	log: LogAccess;
	site: SiteInfo;
	content?: ContentAccess;
	taxonomies?: TaxonomyAccess;
	media?: MediaAccess;
	http?: HttpAccess;
	users?: UserAccess;
	email?: EmailAccess;
}