Packages
@httpjpg/env
Type-safe environment variable validation with Zod
@httpjpg/env
Type-safe environment variable validation using Zod and @t3-oss/env-nextjs.
Installation
pnpm add @httpjpg/envFeatures
- ✅ Runtime validation - Validates env vars at build time
- ✅ TypeScript types - Autocomplete for all env vars
- ✅ Server/Client separation - Clear separation of env vars
- ✅ Zod schemas - Custom validation rules
- ✅ Error messages - Clear error messages for missing vars
Configuration
The package validates these environment variables:
Server-side Variables
// Only accessible on server
STORYBLOK_MANAGEMENT_TOKEN: z.string().min(1)
STORYBLOK_SPACE_ID: z.string().min(1)
STORYBLOK_PREVIEW_TOKEN: z.string().min(1)
SPOTIFY_CLIENT_ID: z.string().min(1)
SPOTIFY_CLIENT_SECRET: z.string().min(1)
SPOTIFY_REFRESH_TOKEN: z.string().min(1)Client-side Variables
// Accessible in browser (must have NEXT_PUBLIC_ prefix)
NEXT_PUBLIC_STORYBLOK_ACCESS_TOKEN: z.string().min(1)
NEXT_PUBLIC_ENVIRONMENT: z.enum(['development', 'staging', 'production'])Usage
Import Environment Variables
import { env } from '@httpjpg/env';
// Server-side only
const token = env.STORYBLOK_MANAGEMENT_TOKEN;
const spaceId = env.STORYBLOK_SPACE_ID;
// Client-side accessible
const publicToken = env.NEXT_PUBLIC_STORYBLOK_ACCESS_TOKEN;TypeScript Autocomplete
import { env } from '@httpjpg/env';
env. // TypeScript shows all available env varsEnvironment Files
.env.example
Template for required variables:
# Storyblok Management API
STORYBLOK_MANAGEMENT_TOKEN=your-management-token
STORYBLOK_SPACE_ID=your-space-id
# Storyblok Content Delivery API
STORYBLOK_PREVIEW_TOKEN=your-preview-token
NEXT_PUBLIC_STORYBLOK_ACCESS_TOKEN=your-access-token
# Spotify Integration
SPOTIFY_CLIENT_ID=your-client-id
SPOTIFY_CLIENT_SECRET=your-client-secret
SPOTIFY_REFRESH_TOKEN=your-refresh-token
# Environment
NEXT_PUBLIC_ENVIRONMENT=development.env.local
Your actual values (never commit):
STORYBLOK_MANAGEMENT_TOKEN=abc123...
STORYBLOK_SPACE_ID=123456
# ... etcValidation Errors
If a required env var is missing, you'll see:
❌ Invalid environment variables:
{
STORYBLOK_MANAGEMENT_TOKEN: [ 'Required' ]
}Adding New Variables
1. Update Schema
Edit packages/env/src/env.mjs:
import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";
export const env = createEnv({
server: {
// Add server-side var
MY_NEW_SECRET: z.string().min(1),
},
client: {
// Add client-side var (must have NEXT_PUBLIC_ prefix)
NEXT_PUBLIC_MY_VAR: z.string().optional(),
},
runtimeEnv: {
MY_NEW_SECRET: process.env.MY_NEW_SECRET,
NEXT_PUBLIC_MY_VAR: process.env.NEXT_PUBLIC_MY_VAR,
},
});2. Update .env.example
# My Feature
MY_NEW_SECRET=your-secret
NEXT_PUBLIC_MY_VAR=your-value3. Use in Code
import { env } from '@httpjpg/env';
const secret = env.MY_NEW_SECRET; // Server-only
const publicVar = env.NEXT_PUBLIC_MY_VAR; // Client-accessibleCustom Validation
Use Zod for custom validation:
server: {
// URL validation
API_URL: z.string().url(),
// Email validation
ADMIN_EMAIL: z.string().email(),
// Number with min/max
PORT: z.coerce.number().min(1000).max(9999),
// Enum
LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']),
// Optional with default
TIMEOUT: z.coerce.number().default(5000),
}Best Practices
- Never commit .env.local - Add to .gitignore
- Use NEXT_PUBLIC_ prefix for client-side vars
- Keep secrets server-side - Don't expose in browser
- Update .env.example when adding new vars
- Use strict validation - Add constraints with Zod
- Document each variable - Add comments in .env.example
Security
✅ Do
// Server-side API calls
const api = await fetch('https://api.example.com', {
headers: {
'Authorization': `Bearer ${env.SECRET_API_KEY}`,
},
});❌ Don't
// DON'T expose secrets to client
const ClientComponent = () => {
// This would expose the secret!
const secret = env.SECRET_API_KEY; // ❌ ERROR
};Client-side Usage
// Client components can only access NEXT_PUBLIC_ vars
'use client';
export function ClientComponent() {
const publicVar = env.NEXT_PUBLIC_STORYBLOK_ACCESS_TOKEN; // ✅ OK
}Troubleshooting
Missing Required Variable
Error: Missing required environment variables:
- STORYBLOK_MANAGEMENT_TOKENSolution: Add the variable to .env.local
Invalid Value
Error: Invalid environment variables:
{
PORT: [ 'Expected number, received string' ]
}Solution: Check the value type matches the schema
Client-side Access Error
Error: Cannot access server-side env var in client componentSolution: Add NEXT_PUBLIC_ prefix or move logic to server
Integration
Next.js
The env package is automatically imported:
// next.config.ts
import { env } from '@httpjpg/env';
export default {
env: {
CUSTOM_VAR: env.MY_VAR,
},
};Storyblok Sync
// packages/storyblok-sync/src/api.ts
import { env } from '@httpjpg/env';
const token = env.STORYBLOK_MANAGEMENT_TOKEN;
const spaceId = env.STORYBLOK_SPACE_ID;API Routes
// app/api/spotify/route.ts
import { env } from '@httpjpg/env';
export async function GET() {
const clientId = env.SPOTIFY_CLIENT_ID;
// ... use in server-side logic
}