Fail fast on misconfiguration, not during production
Missing env vars cause runtime errors deep in the stack instead of failing at boot. "not_authed" at request time when the real issue is a missing token at startup. Boot checks catch these instantly before any code runs.
After this lesson, you'll:
- Remove an env var → app fails instantly with clear error message
- See
Environment validation failedinstead of cryptic runtime errors - Get type-safe autocomplete for
env.SLACK_BOT_TOKENthroughout your code
# ❌ Before boot checks (confusing):
$ slack run
[ERROR] UnhandledPromiseRejectionWarning: Error: An API error occurred: not_authed
at validateToken (node_modules/@slack/web-api/dist/WebClient.js:789:23)
# ✅ After boot checks (explicit):
$ slack run
Invalid environment configuration: [{
"path": ["SLACK_BOT_TOKEN"],
"message": "SLACK_BOT_TOKEN is required"
}]
Error: Environment validation failedOutcome
Harden the repo so it fails fast on environment misconfigurations, missing secrets, and also confirms AI connectivity.
Fast Track
- Create
server/env.tswith Zod schema for required env vars - Import at top of
app.tsto validate on boot - Test by removing an env var and confirming fail-fast behavior
Using Zod for env validation
Zod is a TypeScript-first schema validation library. You define what data should look like, and Zod validates it at runtime while providing type safety. You'll use the pattern of defining a schema → safeParse() → handle success/errors throughout the course.
Create server/env.ts to validate required environment variables at boot:
import { z } from "zod";
export const envSchema = z.object({
SLACK_SIGNING_SECRET: z.string().min(1, "SLACK_SIGNING_SECRET is required"),
SLACK_BOT_TOKEN: z.string().min(1, "SLACK_BOT_TOKEN is required"),
// AI gateway auth can come from an explicit API key or from Vercel's OIDC integration.
// We don't hard-require either here to keep boot checks focused on Slack secrets.
AI_GATEWAY_API_KEY: z.string().optional(),
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
});
const result = envSchema.safeParse(process.env);
if (!result.success) {
console.error("Invalid environment configuration:", result.error.issues);
throw new Error("Environment validation failed");
}
export const env = result.data;Import the env module at the top of server/app.ts to fail fast during startup. Replace all process.env.* references with env.*:
import { App, LogLevel } from "@slack/bolt";
import { VercelReceiver } from "@vercel/slack-bolt";
import registerListeners from "./listeners";
import { env } from "./env";
const logLevel =
env.NODE_ENV === "development" ? LogLevel.DEBUG : LogLevel.INFO;
const receiver = new VercelReceiver({
logLevel,
});
const app = new App({
token: env.SLACK_BOT_TOKEN,
signingSecret: env.SLACK_SIGNING_SECRET,
receiver,
deferInitialization: true,
logLevel,
});
registerListeners(app);
export { app, receiver };Note: You'll also need to update any other files that reference process.env.SLACK_BOT_TOKEN, process.env.SLACK_SIGNING_SECRET, or process.env.AI_GATEWAY_API_KEY to use the env object instead. The main touchpoint is server/app.ts, but check listeners and utilities if you added direct process.env references elsewhere.
Try It
-
Test fail-fast behavior:
- Comment out
SLACK_BOT_TOKENin.env - Run
slack run— it should fail immediately with:Invalid environment configuration: [ { "path": ["SLACK_BOT_TOKEN"], "message": "SLACK_BOT_TOKEN is required" } ] Error: Environment validation failed
- Comment out
-
Restore and verify:
- Uncomment
SLACK_BOT_TOKEN - Run
slack run— app starts successfully
- Uncomment
Commit
git add -A
git commit -m "feat(boot): add environment validation with Zod
- Create env.ts with required variable schema
- Fail fast on missing or invalid configuration
- Type-safe environment variable access throughout app"Done-When
- Created
server/env.tswith Zod validation schema - App fails immediately when required env vars are missing
- Type-safe access to environment variables via
envobject
Was this helpful?