Skip to content

Configuration

Environment variables and configuration files.

Environment Variables

Required Variables

Variable Description Example
NEXT_PUBLIC_AUTH_API_URL BetterAuth service URL https://auth.example.com
NEXT_PUBLIC_API_BASE_URL Core API URL https://api.example.com
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY Stripe public key pk_live_xxx

Optional Variables

Variable Description Default
NEXT_PUBLIC_WS_PORT WebSocket streaming port 7103
LOKI_URL Loki logging endpoint (console only)
LOG_LEVEL Minimum log level info
NODE_ENV Environment name development

Example .env.local

# Authentication
NEXT_PUBLIC_AUTH_API_URL=http://localhost:8787

# Core API
NEXT_PUBLIC_API_BASE_URL=http://localhost:8080

# Stripe
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxx

# Streaming
NEXT_PUBLIC_WS_PORT=7103

# Logging (optional)
LOKI_URL=http://localhost:3100

# Environment
NODE_ENV=development

Production Environment

# Authentication
NEXT_PUBLIC_AUTH_API_URL=https://auth.getcoordinator.ai

# Core API
NEXT_PUBLIC_API_BASE_URL=https://api.getcoordinator.ai

# Stripe
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_xxx

# Streaming
NEXT_PUBLIC_WS_PORT=7103

# Logging
LOKI_URL=https://loki.getcoordinator.ai

# Environment
NODE_ENV=production

Configuration Files

next.config.ts

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  // External packages for server-side only
  serverExternalPackages: ["winston", "winston-loki", "snappy"]
};

export default nextConfig;

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [
      { "name": "next" }
    ],
    "paths": {
      "@/*": ["./*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}

components.json (shadcn/ui)

{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "default",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "config": "tailwind.config.ts",
    "css": "app/globals.css",
    "baseColor": "slate",
    "cssVariables": true
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils"
  }
}

eslint.config.mjs

import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const compat = new FlatCompat({
  baseDirectory: __dirname
});

const eslintConfig = [
  ...compat.extends("next/core-web-vitals", "next/typescript")
];

export default eslintConfig;

postcss.config.mjs

const config = {
  plugins: {
    "@tailwindcss/postcss": {}
  }
};

export default config;

Path Aliases

Configured in tsconfig.json:

{
  "paths": {
    "@/*": ["./*"]
  }
}

Usage:

import { Button } from "@/components/ui/button";
import { useAuth } from "@/hooks/use-auth";
import { fetchJSON } from "@/lib/utils";

Runtime Configuration

API Base URL

Computed at runtime in hooks:

const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:8080";
const basePath = `${API_BASE_URL}/api/v1/actions`;

Auth URL

Configured in auth client:

export const authClient = createAuthClient({
  baseURL: process.env.NEXT_PUBLIC_AUTH_API_URL || "http://localhost:8787"
});

WebSocket URL

Computed with organization metadata:

const serverHostURL = organization.metadata?.serverHostURL;
const wsPort = process.env.NEXT_PUBLIC_WS_PORT || "7103";
const wsUrl = `ws://${serverHostURL}:${wsPort}/ws/stream/${cameraId}`;

Feature Flags

Currently no feature flag system. Consider implementing:

// Example future implementation
const FEATURES = {
  training: process.env.NEXT_PUBLIC_ENABLE_TRAINING === "true",
  passkeys: process.env.NEXT_PUBLIC_ENABLE_PASSKEYS === "true"
};

Validation

Environment variables are accessed directly. For validation, consider:

// lib/env.ts
import { z } from "zod";

const envSchema = z.object({
  NEXT_PUBLIC_AUTH_API_URL: z.string().url(),
  NEXT_PUBLIC_API_BASE_URL: z.string().url(),
  NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: z.string().startsWith("pk_")
});

export const env = envSchema.parse({
  NEXT_PUBLIC_AUTH_API_URL: process.env.NEXT_PUBLIC_AUTH_API_URL,
  NEXT_PUBLIC_API_BASE_URL: process.env.NEXT_PUBLIC_API_BASE_URL,
  NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY
});

Secrets Management

Never Commit

  • .env.local
  • API keys
  • Stripe secret keys
  • Database credentials

.gitignore

.env.local
.env.*.local

Production Secrets

Use environment variable injection in deployment: - Docker: -e flags or .env file - Kubernetes: ConfigMaps/Secrets - Vercel: Environment Variables UI - Railway/Render: Environment settings