Claude-skill-registry add-env-variable

Add a new environment variable to the application. Use when adding configuration for external services, feature flags, or application settings. Triggers on "add env", "environment variable", "config variable".

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/add-env-variable" ~/.claude/skills/majiayu000-claude-skill-registry-add-env-variable && rm -rf "$T"
manifest: skills/data/add-env-variable/SKILL.md
safety · automated scan (low risk)
This is a pattern-based risk scan, not a security review. Our crawler flagged:
  • references .env files
  • references API keys
Always read a skill's source content before installing. Patterns alone don't mean the skill is malicious — but they warrant attention.
source content

Add Environment Variable

Adds a new environment variable with Zod validation. All environment variables must be defined in

src/env.ts
and documented in
.env.example
.

Quick Reference

Files to modify:

  1. src/env.ts
    - Add to schema and mapping
  2. .env.example
    - Document the variable
  3. tests/env.test.ts
    - Add validation tests

Instructions

Step 1: Add to Schema in
src/env.ts

Add the variable to the

envSchema
object:

const envSchema = z.object({
  // ... existing variables ...

  // Your new variable (with comment explaining purpose)
  NEW_VARIABLE: z.string(), // Required string
  // OR
  NEW_VARIABLE: z.string().optional(), // Optional string
  // OR
  NEW_VARIABLE: z.string().default("default-value"), // With default
  // OR
  NEW_VARIABLE: z.coerce.number().default(3000), // Number with coercion
  // OR
  NEW_VARIABLE: z.string().url(), // URL validation
});

Step 2: Add to Mapping Object

Add the variable to

mappedEnv
using the
getEnv()
helper (which reads prefixed variables):

const mappedEnv = {
  // ... existing mappings ...
  NEW_VARIABLE: getEnv("NEW_VARIABLE"),
};

Step 3: Document in
.env.example

Add the variable with a descriptive comment, using the prefix (default:

BT_
):

# Description of what this variable is for
BT_NEW_VARIABLE=example-value

Note: The prefix is defined in

src/env.ts
as
const PREFIX = "BT"
. Change this when creating a new service from the template.

Step 4: Add Tests in
tests/env.test.ts

Add test cases for the new variable:

it("accepts valid NEW_VARIABLE", () => {
  const parsed = envSchema.parse({ NEW_VARIABLE: "valid-value" });
  expect(parsed.NEW_VARIABLE).toBe("valid-value");
});

it("defaults NEW_VARIABLE if missing", () => {
  const parsed = envSchema.parse({});
  expect(parsed.NEW_VARIABLE).toBe("default-value");
});

// OR for optional
it("accepts missing NEW_VARIABLE", () => {
  const parsed = envSchema.parse({});
  expect(parsed.NEW_VARIABLE).toBeUndefined();
});

// OR for required
it("rejects missing NEW_VARIABLE", () => {
  expect(() => envSchema.parse({})).toThrow();
});

Common Patterns

All examples use the

getEnv()
helper and
BT_
prefix:

Required String

// Schema
MY_API_KEY: z.string(),

// Mapping
MY_API_KEY: getEnv("MY_API_KEY"),

// .env.example
BT_MY_API_KEY=your-api-key-here

Optional String

// Schema
OPTIONAL_FEATURE: z.string().optional(),

// Mapping
OPTIONAL_FEATURE: getEnv("OPTIONAL_FEATURE"),

// .env.example
# Optional: Enable feature X
# BT_OPTIONAL_FEATURE=enabled

String with Default

// Schema
LOG_LEVEL: z.string().default("info"),

// Mapping
LOG_LEVEL: getEnv("LOG_LEVEL"),

// .env.example
BT_LOG_LEVEL=info

Number with Coercion

// Schema
RATE_LIMIT: z.coerce.number().default(100),

// Mapping
RATE_LIMIT: getEnv("RATE_LIMIT"),

// .env.example
BT_RATE_LIMIT=100

URL Validation

// Schema
WEBHOOK_URL: z.string().url().optional(),

// Mapping
WEBHOOK_URL: getEnv("WEBHOOK_URL"),

// .env.example
# Webhook endpoint for notifications
BT_WEBHOOK_URL=https://example.com/webhook

Enum Values

// Schema
NODE_ENV: z.enum(["development", "test", "production"]).default("development"),

// Mapping
NODE_ENV: getEnv("NODE_ENV"),

// .env.example
BT_NODE_ENV=development

Boolean (as string)

// Schema
ENABLE_FEATURE: z.string().transform(v => v === "true").default("false"),

// Mapping
ENABLE_FEATURE: getEnv("ENABLE_FEATURE"),

// .env.example
BT_ENABLE_FEATURE=false

Usage in Code

Always import from

@/env
, never use
process.env
directly:

import { env } from "@/env";

// Correct
const apiKey = env.MY_API_KEY;

// Wrong - bypasses validation
const apiKey = process.env.MY_API_KEY;

Full Example: Adding Email Service Config

1. Update
src/env.ts

const envSchema = z.object({
  // ... existing ...

  // Email service configuration
  EMAIL_API_KEY: z.string().optional(),
  EMAIL_FROM_ADDRESS: z.string().email().optional(),
  EMAIL_PROVIDER: z.enum(["sendgrid", "mailgun"]).default("sendgrid"),
});

const mappedEnv = {
  // ... existing ...
  EMAIL_API_KEY: getEnv("EMAIL_API_KEY"),
  EMAIL_FROM_ADDRESS: getEnv("EMAIL_FROM_ADDRESS"),
  EMAIL_PROVIDER: getEnv("EMAIL_PROVIDER"),
};

2. Update
.env.example

# Email service configuration
BT_EMAIL_API_KEY=your-email-api-key
BT_EMAIL_FROM_ADDRESS=noreply@example.com
BT_EMAIL_PROVIDER=sendgrid

3. Update
tests/env.test.ts

it("accepts valid email configuration", () => {
  const parsed = envSchema.parse({
    EMAIL_API_KEY: "test-key",
    EMAIL_FROM_ADDRESS: "test@example.com",
    EMAIL_PROVIDER: "mailgun",
  });
  expect(parsed.EMAIL_API_KEY).toBe("test-key");
  expect(parsed.EMAIL_FROM_ADDRESS).toBe("test@example.com");
  expect(parsed.EMAIL_PROVIDER).toBe("mailgun");
});

it("defaults EMAIL_PROVIDER to sendgrid", () => {
  const parsed = envSchema.parse({});
  expect(parsed.EMAIL_PROVIDER).toBe("sendgrid");
});

it("rejects invalid EMAIL_FROM_ADDRESS", () => {
  expect(() =>
    envSchema.parse({ EMAIL_FROM_ADDRESS: "not-an-email" }),
  ).toThrow();
});

it("rejects invalid EMAIL_PROVIDER", () => {
  expect(() => envSchema.parse({ EMAIL_PROVIDER: "invalid" })).toThrow();
});

What NOT to Do

  • Do NOT use
    process.env
    directly in application code
  • Do NOT forget to add the mapping in
    mappedEnv
  • Do NOT skip documenting in
    .env.example
  • Do NOT skip adding tests for validation rules
  • Do NOT store secrets in
    .env.example
    (use placeholder values)

See Also

  • create-utility-service
    - Services that use environment config
  • test-schema
    - Testing Zod schemas (similar patterns)