Environment Variables
Environment Variables
In a TypeScript project, .env files are used to manage environment variables, which are configuration settings that can vary between different environments (e.g., development, testing, production). These files typically store sensitive information like API keys, database connection strings, and other environment-specific configurations.
Example .env file
API_KEY=your_api_key_here
DATABASE_URL=your_database_connection_string
π§© Type-Safe Environment Variables in Nuxt 4 with Zod
Managing environment variables safely is crucial in TypeScript projects. Using Zod, you can validate and type your .env
values at runtime to ensure your app never starts with missing or invalid configuration.
π Install Dependencies
First, install Zod:
npm install zod
.env
File π§± Create a
Example .env
file:
API_URL=https://api.example.com
API_KEY=my-secret-key
PUBLIC_ANALYTICS_ID=G-XXXXXXX
π§± Configure Runtime Variables in nuxt.config.ts
In Nuxt, environment variables are divided into two categories:
Private (server-only): accessible only on the server
Public (client-exposed): must be nested under public in your config
Example:
export default defineNuxtConfig({
runtimeConfig: {
apiUrl: process.env.API_URL,
apiKey: process.env.API_KEY,
public: {
analyticsId: process.env.PUBLIC_ANALYTICS_ID,
},
},
});
π§ Define a Zod Schema for Validation
Create a file called env.ts
(or config/env.ts
):
import { z } from "zod";
import { useRuntimeConfig } from "#imports";
// Define your schema
const configSchema = z.object({
apiUrl: z.string().url(),
apiKey: z.string().min(1),
public: z.object({
analyticsId: z.string().min(1),
}),
});
export function getValidatedConfig() {
const runtimeConfig = useRuntimeConfig();
const parsed = configSchema.safeParse(runtimeConfig);
if (!parsed.success) {
console.error("β Invalid environment configuration:", parsed.error.flatten().fieldErrors);
throw new Error("Invalid runtime configuration");
}
return parsed.data;
}
env
Object π§© Use the Typed
Now, anywhere in your app β for example, in a server route, plugin, or composable β you can import and use your validated config safely:
import { getValidatedConfig } from "../utils/env";
export default defineEventHandler(() => {
const config = getValidatedConfig();
console.log(config.apiUrl); // β
string (validated URL)
console.log(config.public.analyticsId); // β
string (validated)
return { message: "Environment variables validated!" };
});
π§ Type Safety in Client Code
On the client side, you can safely access only public variables:
export const useAnalytics = () => {
const config = useRuntimeConfig();
// Public runtime config is type-safe too
const analyticsId = config.public.analyticsId;
console.log("Analytics ID:", analyticsId);
};
β‘ Optional: Split for Different Environments
You can even extend this pattern for multiple environments, e.g., .env.development
, .env.production
, and load them dynamically based on NODE_ENV
.
Example:
dotenv.config({ path: `.env.${process.env.NODE_ENV || "development"}` });
β Benefits
- Runtime validation of environment variables
- Compile-time type safety with TypeScript
- Prevents silent startup errors
- Centralized configuration management
π§ Example Project Structure
my-app/
ββ app/
β ββ env.ts
β ββ index.ts
| |- app.config.ts
ββ .env
ββ package.json
ββ nuxt.config.ts
ββ tsconfig.json
With this setup, your environment variables are validated, parsed, and type-safe, preventing runtime surprises and improving developer confidence.