v7.0.2-next.0

Edge runtimes

Bind a PortableSource on Vercel Edge, Fastly Compute, and Expo, then read typed config with the same API as everywhere else.

These runtimes have no filesystem and no .env cascade. Import from envapt (the portable build resolves automatically through the edge-light, fastly, and react-native conditions), hand envapt the config object the platform gives you once, and read with the same typed API as everywhere else. On Node-like runtimes, including Vercel's Node.js functions, FileSource binds on import, so even that step is automatic.

NOTE

The filesystem-only config APIs have nothing to do on these runtimes, so they warn and no-op by default. Readers work as normal. See Compatibility for fileApiMode and Errors for the codes.

Vercel

Vercel's Node.js runtime (the default, and the one Vercel now recommends) runs the node build, so FileSource binds on import and reads process.env for you. Nothing to bind.

import { ,  } from 'envapt';

const  = .('ORIGIN_URL', .);

The Edge runtime is the exception. Vercel is moving away from it, but if you use it, it resolves the portable build (edge-light) and exposes your variables on process.env as a real object. Bind it once.

import { , ,  } from 'envapt';

.(new (.));
const  = .('ORIGIN_URL', .);

Fastly Compute

Fastly is the one runtime that hands you no environment object. You read values one key at a time with env from fastly:env, and ConfigStore is the same shape (a keyed .get(), no listing). So name the keys you read and build the object from them. Add the fastly condition to your bundler's resolve conditions so bare envapt resolves the portable build.

import { env } from 'fastly:env';
import { Envapter, PortableSource, Converters } from 'envapt';

Envapter.useSource(
    new PortableSource({
        ORIGIN_URL: env('ORIGIN_URL'),
        API_TOKEN: env('API_TOKEN')
    })
);
const origin = Envapter.getRequired('ORIGIN_URL', Converters.Url);

Custom variables are inlined into the Wasm binary at build time through the --env flag, so changing one needs a redeploy. For values that change without a redeploy, read a ConfigStore by key the same way. See Fastly's environment variables docs.

Expo and React Native

Put your config under extra in app.config.js. It is a real object at runtime through expo-constants, so you bind it whole and envapt does the rest.

import Constants from 'expo-constants';
import { Envapter, PortableSource } from 'envapt';

Envapter.useSource(new PortableSource(Constants.expoConfig?.extra ?? {}));

You cannot pass process.env here. On Metro it is not a runtime object, the bundler replaces each static process.env.EXPO_PUBLIC_* reference with its literal string at build time, so spreading it gives you nothing. If your config already lives in EXPO_PUBLIC_* variables, reference each by name.

import { Envapter, PortableSource, Converters } from 'envapt';

Envapter.useSource(
    new PortableSource({
        API_URL: process.env.EXPO_PUBLIC_API_URL,
        FEATURE_BETA: process.env.EXPO_PUBLIC_FEATURE_BETA
    })
);
const api = Envapter.getRequired('API_URL', Converters.Url);
WARNING

Both Expo surfaces ship in your app bundle. Pass public configuration only, never a secret or a server-side key.

See Expo's environment variables docs for the prefix rules and inlining.

No filesystem

There is no .env cascade on these runtimes. Bind a source before your first read, else an unbound read throws NoSourceBound. PortableSource snapshots its object at construction, so mutating that object afterward does not change what envapt reads. The default @Envapt accessor decorators work on all three, the legacy envapt/legacy form needs a compile step. For the providers and the contract see Sources.

On this page