SvelteKit, Astro, and Nuxt
Run typed envapt config in a SvelteKit, Astro, or Nuxt server on Node, with the start command and startup checks each framework needs.
This page covers SvelteKit 3 with adapter-node, Astro 7 with @astrojs/node, and Nuxt 4's Node server. Each one runs your server code on Node, where envapt loads its node build and reads process.env and your .env files with no setup. An adapter for Workers or an edge runtime loads the portable build instead, which Cloudflare Workers and Edge runtimes explain. Next.js has its own page.
| SvelteKit 3 | Astro 7 | Nuxt 4 | |
|---|---|---|---|
| Build runs your server modules | yes | prerendered pages only | no |
NODE_ENV when the built server starts | unset | unset | unset |
| Hook that runs before the first request | init in hooks.server.ts | none | a Nitro plugin |
@Envapt from envapt | build stops | first request throws | works |
@Envapt from envapt/legacy | works | works | works |
Each framework also declares variables its own way, with SvelteKit's defineEnvVars, Astro's astro:env, and Nuxt's runtimeConfig. Use those for values the framework passes to its own APIs and to the browser. Use envapt when code outside the framework, such as a worker or a script, imports the same config module. It also gives you converters like Converters.Time and the file order that follows ENVIRONMENT=staging.
Start the server with NODE_ENV set
With NODE_ENV unset, Envapter.environment returns 'development', so envapt skips .env.production. Set it in the start command:
NODE_ENV=production node build # SvelteKit with adapter-node
NODE_ENV=production node dist/server/entry.mjs # Astro with @astrojs/node
NODE_ENV=production node .output/server/index.mjs # NuxtNuxt makes this easy to miss. Its build replaces each process.env.NODE_ENV in your server code with the string "production", so a check in your own code says production. envapt reads process.env at runtime, where NODE_ENV is still unset.
None of these servers copies your .env files into process.env. envapt reads the files itself, in its own order, and ENVIRONMENT=staging in the start command makes it read .env.staging too.
SvelteKit
Put your reads in one server module, so the init hook below can import the same file your routes do:
import { , } from 'envapt';
export const = .(
{
: .,
: .
},
'camelCase'
);getRequiredAll turns SESSION_TTL=15m into config.sessionTtl, the number 900000. If DATABASE_URL and SESSION_TTL are both unset, it throws one MissingEnvValue error (305) that lists both.
vite build runs your server modules during its analysis step. If a route imports config and a variable is unset on the build machine, the build stops:
error during build:
EnvaptError [305]: Missing required environment variables: DATABASE_URL.Set the variables where you build. If your build machine can't have them, export a function that calls getRequiredAll and call it from your routes, the way Next.js does.
The server can still start with values that differ from the build machine's. To check them at startup, import config in the init hook:
import type { ServerInit } from '@sveltejs/kit/hooks';
export const init: ServerInit = async () => {
await import('./lib/server/config');
};SvelteKit runs init before it serves a request. If init throws, the process exits with code 1 and never starts listening.
SvelteKit's own defineEnvVars reads only process.env. If you use it alongside envapt, SvelteKit can't find a variable defined only in .env, and the server stops with env_invalid. envapt reads the same variable from the file.
SyntaxError: Invalid or unexpected token
vite build stops with this error if a class uses @Envapt from envapt. Vite 8 leaves those decorators uncompiled in the server bundle, and Node can't parse them. To get them compiled, set experimentalDecorators in tsconfig.json and switch to envapt/legacy, which declares each field as static readonly in place of static accessor. The Browser page has the same fix for Vite 8 in client code.
Astro
When the first request for a route arrives, Astro imports that route's module. If config is missing a variable, that route returns 500, but the rest of the site keeps working. Astro has no hook that runs when the server starts, and src/middleware.ts also loads on the first request. To catch a bad config right after a deploy, point your health check at a route that imports config.
During astro build, NODE_ENV is production. If a page exports prerender = true, Astro renders it then, and the values it reads come from .env.production on the build machine. They stay in the HTML until you build again.
Astro builds its server bundle with Vite 8 too. The build passes, but the first request to a route that imports a class using @Envapt from envapt throws SyntaxError: Invalid or unexpected token. Switch to envapt/legacy with experimentalDecorators, the same fix as for SvelteKit.
Nuxt
Nuxt's build doesn't run your server routes, so it passes even if variables are unset. To catch them when the server starts, add a Nitro plugin. Nitro is the server Nuxt builds, and it runs each file in server/plugins once at startup, before it listens:
import '../lib/config';
export default defineNitroPlugin(() => {});server/lib/config.ts holds the same getRequiredAll call as the SvelteKit sample. The import runs that call. If a variable is missing or doesn't convert, the plugin throws and the process exits with code 1.
Nuxt's server build compiles @Envapt from envapt, so static accessor fields work without experimentalDecorators.
Next.js
Read typed config in a Next.js app from server code, client components, and edge routes, and catch a missing variable at build time or at server start.
Building on envapt
Read config from a library, framework, or CLI that depends on envapt, so the source, environment, and .env files the app sets up reach your reads.