Saltar al contenido

app.config.ts: configurar el proyecto y su despliegue

El único fichero de configuración de SolidStart. defineConfig de @solidjs/start/config envuelve a Vinxi y expone una superficie declarativa sobre las tres capas: la opción ssr elige entre renderizar en el servidor o no, el bloque server pasa a Nitro el preset de despliegue y reglas como prerender, y el bloque vite afina el bundler y sus plugins, con forma de función para configurar por router. El gran superpoder es el preset de Nitro: el mismo proyecto compila para Node, Vercel, Netlify, Cloudflare, Deno o una salida estática cambiando una sola línea.

defineConfig: la superficie declarativa única

El fichero exporta el resultado de defineConfig, una función que envuelve a la de Vinxi y le añade los valores por defecto de SolidStart. Un proyecto recién creado no necesita nada dentro:

TypeScript
// app.config.tsimport { defineConfig } from "@solidjs/start/config";// vacio ya es un proyecto valido: SSR en streaming, preset por defectoexport default defineConfig({});

A partir de ese objeto vacío, cada clave que añades es una instrucción para una de las capas. Las tres que importan de entrada son ssr para el modo de render, server para todo lo relativo a Nitro y vite para el bundler. Conviene tener siempre presente ese reparto: cuando busques dónde poner una opción, primero pregúntate a qué capa pertenece, y el bloque se elige solo.

El superpoder: presets de despliegue

El bloque server se entrega tal cual a Nitro, y su opción más valiosa es preset. Un preset es la receta que Nitro usa para empaquetar tu build hacia una plataforma concreta: cambia el formato del servidor, las APIs disponibles y el artefacto final. Sin preset explícito, el valor por defecto es node-server, que produce un servidor Node ejecutable en .output.

TypeScript
import { defineConfig } from "@solidjs/start/config";export default defineConfig({  server: {    // el mismo codigo, otro destino: Cloudflare como modulo Worker    preset: "cloudflare-module",  },});

La lista de presets de Nitro es larga y compartida con todo el ecosistema UnJS: node-server, vercel, netlify, cloudflare-pages, cloudflare-module, deno-deploy, bun, aws-lambda y static, entre otros. Y como el preset también se puede fijar por variable de entorno, tu pipeline de CI puede desplegar el mismo commit a destinos distintos sin tocar el repositorio:

Shell
# el mismo proyecto, otro destino, sin editar app.config.tsSERVER_PRESET=vercel npm run build
TypeScript
export default defineConfig({  server: {    prerender: {      routes: ["/", "/sobre", "/blog"],      crawlLinks: true,    },  },});

Afinar Vite y sus plugins

El bloque vite se funde con la configuración del bundler, y es donde añades plugins, alias de resolución o ajustes de build. Añadir Tailwind, por ejemplo, es registrar su plugin de Vite aquí:

TypeScript
import { defineConfig } from "@solidjs/start/config";import tailwindcss from "@tailwindcss/vite";export default defineConfig({  vite: {    plugins: [tailwindcss()],  },});

Hay un matiz que solo tiene sentido recordando la lección primera: Vinxi no compila un único bundle, sino varios routers de build —cliente, servidor, funciones de servidor—. Por eso vite acepta también la forma de función, que recibe cuál es el router en curso y te deja devolver una configuración distinta para cada uno. Es la vía para, por ejemplo, aplicar un plugin solo al cliente.

TypeScript
export default defineConfig({  vite({ router }) {    // router puede ser client, server o server-function    return {      resolve: { alias: { "~": "/src" } },    };  },});

Dos claves más redondean el fichero. ssr conmuta el modo de render —true por defecto, false para SPA—, tema de la última lección. Y middleware apunta a un fichero que intercepta cada petición antes de las rutas, el sitio natural para autenticación o cabeceras globales.

TypeScript
export default defineConfig({  ssr: true,  middleware: "./src/middleware.ts",  server: { preset: "netlify" },});

El middleware es código de servidor que corre en cada petición antes de que el enrutador elija una ruta, así que es el sitio natural para resolver la sesión, imponer redirecciones o poblar el locals de la petición que luego leerás con getRequestEvent. Y para los ajustes finos del compilador de Solid en sí —no de Vite en general— existe el bloque solid, que pasa opciones directamente a vite-plugin-solid; rara vez lo tocarás, pero saber que está evita que busques esas opciones en el sitio equivocado.

Una lectura por capas

La forma disciplinada de trabajar este fichero es no verlo como una lista plana de opciones, sino como tres conversaciones separadas que comparten un archivo. Cuando algo no encaja, la pregunta correcta nunca es qué opción de SolidStart lo resuelve, sino a qué capa pertenece el problema: si es de compilación va a vite, si es de servidor o despliegue va a server, y si es de dónde se renderiza va a ssr. Ese reflejo te ahorra buscar en la documentación equivocada y te deja componer configuraciones que combinan las tres capas sin confundir sus fronteras.

Merece la pena interiorizar también que este fichero se evalúa en tiempo de build, no en cada petición: es JavaScript que corre una vez cuando Vinxi arranca o empaqueta, de modo que puedes leer variables de entorno o calcular valores dentro de él, pero no depende del ciclo de una solicitud concreta. Esa distinción explica por qué el preset se puede fijar por variable de entorno del proceso de build y por qué la configuración es estática respecto al tráfico: cuando el servidor ya está sirviendo, app.config.ts hace mucho que terminó su trabajo.

Lo que decides por petición —autenticación, cabeceras condicionales, redirecciones según el usuario— no vive aquí sino en el middleware o en las propias rutas, que sí corren en cada solicitud. Confundir ambos planos es un error sutil pero caro: intentar ramificar la configuración según datos de una petición no funciona, porque para cuando llega la primera petición este archivo ya es historia. Reserva app.config.ts para lo que es cierto de todo el despliegue y deja lo que cambia por solicitud para las capas que viven en el tiempo de la petición.