Acceder al request: getRequestEvent, headers y cookies
Dentro de una función de servidor no hay props ni contexto reactivo que te digan quién pide: hay una petición HTTP, y se accede a ella con `getRequestEvent` de `solid-js/web`. Se estudia la anatomía del `RequestEvent` —el `request` entrante con sus headers y cookies, el `response` saliente donde fijas estado y `Set-Cookie`, el `locals` tipado que rellena el middleware, y el `nativeEvent` de Vinxi para los helpers de bajo nivel—, por qué en el cliente devuelve `undefined`, y por qué la petición es la única fuente de verdad por-petición en un proceso que sirve a muchos.
getRequestEvent: el puente a la petición
getRequestEvent, importado de solid-js/web, devuelve el evento de la petición que se está atendiendo ahora mismo en el servidor. No recibe argumentos: sabe cuál es la petición actual gracias al almacenamiento asíncrono local que el runtime mantiene por petición. Como solo existe una petición en el servidor, en el cliente devuelve undefined —allí no hay ninguna petición que atender—, lo que lo convierte también en una señal de entorno: si hay evento, estás en el servidor.
import { getRequestEvent } from "solid-js/web";export async function idioma() { "use server"; const evento = getRequestEvent(); const cabecera = evento?.request.headers.get("accept-language") ?? "es"; return cabecera.split(",")[0]; // negociacion simple de idioma}Leer la entrada y escribir la salida
El RequestEvent tiene dos caras. La entrante es event.request, un objeto Request estándar de la plataforma: de él lees headers, method, url y, a través de la cabecera cookie, las cookies del cliente. La saliente es event.response, donde depositas lo que la respuesta debe llevar: un status distinto y cabeceras como Set-Cookie. Es importante ver la simetría —una cara es lo que llega, la otra lo que se irá— porque muchas operaciones de sesión consisten precisamente en leer una cookie de request y escribir otra en response.
import { getRequestEvent } from "solid-js/web";export async function marcarVisita() { "use server"; const evento = getRequestEvent(); if (!evento) return; const cookies = evento.request.headers.get("cookie") ?? ""; const yaVino = cookies.includes("visitante="); if (!yaVino) { evento.response.headers.append("set-cookie", "visitante=1; Path=/; HttpOnly"); evento.response.status = 201; // primera visita } return { recurrente: yaVino };}locals y nativeEvent: contexto y bajo nivel
Las dos piezas restantes resuelven necesidades distintas. event.locals es un contenedor por petición para datos que un middleware calcula una vez y muchas funciones consumen: el usuario autenticado, un identificador de traza, un nonce de CSP. Es el canal correcto para pasar contexto sin variables de módulo —que, como viste en el nivel de estado, se comparten entre peticiones en el servidor—. Y se tipa ampliando una interfaz global, de modo que leer locals sea tan seguro como leer cualquier objeto tipado.
// app.d.ts — tipar los locals una sola vezdeclare module "@solidjs/start/env" { interface RequestEventLocals { usuario?: { id: string; rol: "admin" | "editor" }; }}export async function panelAdmin() { "use server"; const usuario = getRequestEvent()?.locals.usuario; // tipado, puesto por el middleware // ...decidir en funcion del usuario}Cuando necesitas algo que la cara de alto nivel no ofrece, event.nativeEvent te da el evento subyacente de Vinxi, sobre el que operan los helpers del ecosistema —lectura y escritura de cookies con parseo, por ejemplo—. La advertencia de la lección anterior vuelve con fuerza: esos helpers no hacen treeshaking, así que solo se importan en archivos exclusivamente de servidor.
Cookies con parseo: los helpers de Vinxi
Manipular cookies a mano —concatenar la cabecera Set-Cookie, parsear la cadena cookie con split— funciona pero es frágil: te toca gestionar el escapado, Max-Age, HttpOnly, SameSite y el resto de atributos sin red de seguridad. Los helpers getCookie y setCookie de vinxi/http operan sobre el nativeEvent y hacen ese trabajo por ti, leyendo y escribiendo cookies con parseo y opciones tipadas. El precio, ya conocido, es que no hacen treeshaking: viven en archivos marcados con la directiva de archivo.
// data/tema.ts — solo de servidor"use server";import { getRequestEvent } from "solid-js/web";import { getCookie, setCookie } from "vinxi/http";export async function leerTema() { const evento = getRequestEvent()!; // en este archivo siempre hay evento return getCookie(evento.nativeEvent, "tema") ?? "claro";}export async function fijarTema(valor: "claro" | "oscuro") { const evento = getRequestEvent()!; setCookie(evento.nativeEvent, "tema", valor, { httpOnly: true, sameSite: "lax", path: "/", maxAge: 60 * 60 * 24 * 365, });}El patrón de sesión completo se apoya en esta base y en useSession, que cifra y firma una cookie de sesión sobre el mismo nativeEvent; lo verás en detalle al tratar autenticación, pero su cimiento es exactamente el que acabas de ver: toda la identidad de la petición se lee y se escribe a través de su evento, nunca de un estado global del proceso.