defineServerRuntime
Declara en server/runtime.ts las sesiones, los servicios, la política de Socket y el cierre de la aplicación, creados una vez por proceso.
Se autoimporta en el código de servidor: server/ y <script server>.
import { defineServerRuntime } from "@meekojs/meeko/server";defineServerRuntime(factory) es el export por defecto de server/runtime.ts: declara los recursos de la aplicación que viven lo que vive el proceso. Meeko llama a la fábrica una vez por proceso con { env, production, platform, prerendering, root } y usa lo que devuelve: las sesiones, los servicios que llegan a cada petición como context.services, la política de Socket, el origen público, el observador de errores y el cierre.
Desde 1.7, el tipo de services se infiere de este archivo: context.services.db está tipado en páginas, API, RPC, Socket y middleware de servidor sin conversiones.
Uso#
// server/runtime.ts
import { defineServerRuntime, SessionManager } from "@meekojs/meeko/server";
import { createDatabase } from "./utils/database";
export default defineServerRuntime(async ({ env, production, prerendering }) => {
const db = await createDatabase(env.DATABASE_URL);
return {
sessions: new SessionManager({ store: db.sessionStore(), production: production && !prerendering }),
services: { db },
publicOrigin: env.MEEKO_PUBLIC_ORIGIN,
onError: (error, requestId) => console.error("request failed", requestId, error),
close: () => db.close(),
};
});// server/api/users/[id].get.ts: services tipado, sin `as`
// const user = await context.services.db.users.find(params.id);Tipo#
function defineServerRuntime<
Env = Readonly<Record<string, string | undefined>>,
Services = unknown,
>(
factory: ServerRuntimeDefinition<Env, Services>,
): ServerRuntimeDefinition<Env, Services>interface ServerRuntime<Services = unknown, Env = unknown> {
readonly sessions?: SessionManager<any>
readonly services?: ServerServices<Services, Env>
readonly socket?: {
readonly pubsub?: SocketPubSub
readonly onDeliveryError?: (error: unknown, context: SocketDeliveryErrorContext) => void
readonly allowedOrigins?: readonly string[]
readonly maxMessageBytes?: number
readonly maxBufferBytes?: number
readonly maxPendingDeliveries?: number
readonly sessionRefreshMs?: number
}
readonly publicOrigin?: string
readonly onError?: ServerErrorObserver
readonly processErrors?: ProcessErrorPolicy | false
readonly close?: () => void | Promise<void>
}Parámetros#
| Parámetro | Tipo | Por defecto | Descripción |
|---|---|---|---|
factory |
(context) => ServerRuntime | Promise<ServerRuntime> |
obligatorio | Crea los recursos. Recibe env (las variables de entorno), production, platform ({ name, env, waitUntil }) prerendering, true cuando meeko build prerenderiza sin tráfico de clientes, y, desde 1.8, root, la ruta absoluta de la raíz del proyecto (undefined en Workers). |
Lo que devuelve la fábrica:
| Campo | Tipo | Por defecto | Descripción |
|---|---|---|---|
sessions |
SessionManager |
en memoria en desarrollo, Redis con MEEKO_REDIS_URL en producción |
Desde 1.2, opcional. Las sesiones de la aplicación. Unas explícitas siempre ganan. |
services |
objeto, o (request, { platform }) => objeto |
ninguno | Llegan a cada petición como RequestContext.services: un objeto compartido o una función por petición. |
socket |
{ allowedOrigins?, maxMessageBytes?, pubsub?, … } |
ninguno | Política de Socket: orígenes, límites y el broker de broadcast. |
publicOrigin |
URL | la dirección del servidor | El origen público tras un proxy, para comprobar Origin en HTTP y Socket. MEEKO_PUBLIC_ORIGIN lo sustituye. |
onError |
(error, requestId, info?) => void |
registro mínimo | Los fallos internos, con su ID de petición; desde 1.3, también los del proceso. |
processErrors |
política | informar y, ante una excepción, cerrar | Desde 1.3. Qué hacer con las promesas rechazadas y las excepciones del proceso. |
close |
() => void | Promise<void> |
ninguno | Se ejecuta al detener el servidor, después de cerrar las sesiones. |
Valor devuelto#
La misma fábrica, para exportarla por defecto. Sin función lanza TypeError: defineServerRuntime requires a factory function.
Ejemplos#
Servicios por petición#
Una función crea los servicios de cada petición, con la plataforma (por ejemplo, las variables de un Worker):
// server/runtime.ts
import { defineServerRuntime } from "@meekojs/meeko/server";
import { createDatabase } from "./utils/database";
export default defineServerRuntime(async ({ env }) => {
const db = await createDatabase(env.DATABASE_URL);
return {
services: (request, { platform }) => ({ db, region: platform.env.REGION ?? "local", locale: request.headers.get("accept-language") ?? "es" }),
close: () => db.close(),
};
});Sesiones en Redis y Socket entre instancias#
// server/runtime.ts
import { defineServerRuntime, RedisSessionStore, RedisSocketPubSub, SessionManager } from "@meekojs/meeko/server";
import { createClient } from "redis";
export default defineServerRuntime(async ({ env, production, prerendering }) => {
if (prerendering) return { services: {} };
const redis = createClient({ url: env.REDIS_URL });
const subscriber = redis.duplicate();
await Promise.all([redis.connect(), subscriber.connect()]);
return {
sessions: new SessionManager({ store: new RedisSessionStore(redis, { prefix: "app:prod:session:" }), production }),
socket: { pubsub: new RedisSocketPubSub({ publisher: redis, subscriber, prefix: "app:prod:socket:" }) },
close: async () => { await Promise.all([redis.quit(), subscriber.quit()]); },
};
});Con prerendering, el build no sirve tráfico: basta un store en memoria, que es el que crea Meeko sin sessions.
Archivos del proyecto con root#
Desde 1.8, root dice dónde está el proyecto en meeko dev, en meeko start y en la salida de Node, Bun o Deno, que el build deja en .meeko/output/…, donde import.meta.url ya no apunta a server/. Si la salida se despliega fuera del proyecto, indíquelo con MEEKO_ROOT.
// server/runtime.ts
import { join } from 'node:path'
import { defineServerRuntime } from '@meekojs/meeko/server'
export default defineServerRuntime(({ root }) => {
if (!root) throw new Error('Este servidor necesita el sistema de archivos del proyecto')
return { services: { content: join(root, 'content') } }
})Véase Runtime de servidor.
Un servicio que usa una página#
<!-- app/pages/users/[id].meeko -->
<script server lang="ts">
const { params, context } = usePage();
const row = await context.services.db.users.find(params.id); // tipado desde server/runtime.ts
if (!row) notFound();
export const user = { id: row.id, name: row.name };
</script>
<template><h1>{user.name}</h1></template>Errores comunes#
Convertir context.services a otro tipo#
Desde 1.7, context.services ya tiene el tipo de server/runtime.ts. Una conversión a un tipo incompatible es un error.
const cache = context.services as { cache: Map<string, string> };meeko check da TS2352: Conversion of type '{ db: …; region: string; }' to type '{ cache: Map<string, string>; }' may be a mistake because neither type sufficiently overlaps with the other. Quita la conversión y lee los servicios tal cual.
const user = await context.services.db.users.find(params.id);Crear recursos por petición dentro de los servicios compartidos#
Un objeto de services se crea una vez y lo comparten todas las peticiones: guardar ahí datos de un usuario los expone a los demás, sin ningún error.
export default defineServerRuntime(() => ({ services: { currentUser: null as string | null } }));export default defineServerRuntime(() => ({
services: (request) => ({ locale: request.headers.get("accept-language") ?? "es" }),
}));Los datos del usuario se leen de context.session en cada petición.
Exportar un objeto en lugar de la fábrica#
export default defineServerRuntime({ services: {} });defineServerRuntime lanza al cargar el módulo TypeError: defineServerRuntime requires a factory function.
export default defineServerRuntime(() => ({ services: {} }));Notas#
- Una vez por proceso. La fábrica se ejecuta al arrancar (y en
meeko buildconprerendering: true); en Workers, una vez por aislamiento. Conecta aquí las bases de datos y los clientes, no en cada petición. - Tipos de los servicios (
1.7).meeko check,meeko devy el editor generan.meeko/types/services.d.tsa partir de este archivo;RequestContextsin argumento usa ese tipo (AppServices). Con el runtime en JavaScript o sinservices, esunknown. Un tipo escrito (RequestContext<MisServicios>) sigue ganando. - Secretos. Este archivo nunca llega al navegador: lee aquí las variables de entorno.
meeko dev,build,start,previewycheckcargan el.envdel proyecto. - Cierre.
closese ejecuta tras cerrar las sesiones, al detener el servidor;serveNodeespera también las tareas deplatform.waitUntil. - Errores del proceso. Sin
processErrors, un rechazo sin manejar se informa y el servidor sigue; una excepción no capturada se informa y el servidor se cierra de forma ordenada con código 1.