@apollo-deploy/config
v1.0.1
Published
Shared configuration package for all Apollo applications. This package provides a single source of truth for configuration that is shared across `apollo-app`, `apollo-auth`, and `apollo-marketing`.
Readme
@apollo-deploy/config
Shared configuration package for all Apollo applications. This package provides a single source of truth for configuration that is shared across apollo-app, apollo-auth, and apollo-marketing.
Overview
The config package centralizes shared configuration to ensure consistency across all Apollo applications while maintaining the ability for apps to override specific values when needed.
Key Benefits
- Single Source of Truth: All shared configuration lives in one place
- Type Safety: Full TypeScript support with IntelliSense and type checking
- Consistency: Ensures all applications use the same configuration structure
- Flexibility: Applications can override specific values while inheriting shared defaults
- Maintainability: Update configuration once, propagate to all applications
- Validation: Environment variables are validated at runtime using Zod schemas
Package Structure
packages/config/
├── src/ # Source files
│ ├── auth/ # Authentication configuration
│ │ ├── index.ts # Auth barrel export
│ │ ├── session.ts # Session management config
│ │ ├── password.ts # Password policy config
│ │ └── verification.ts # Email/password verification config
│ │
│ ├── api/ # API configuration
│ │ ├── index.ts # API barrel export
│ │ ├── endpoints.ts # API endpoint URLs
│ │ ├── request.ts # Request settings (timeout, retry)
│ │ └── cache.ts # Cache configuration
│ │
│ ├── g11n/ # Globalization configuration
│ │ ├── index.ts # G11n barrel export
│ │ ├── locales.ts # Supported locales
│ │ └── i18n.ts # i18n configuration
│ │
│ ├── connectors/ # Platform connector definitions
│ │ ├── index.ts # Connectors barrel export
│ │ ├── types.ts # Connector type definitions
│ │ └── definitions.ts # Connector configurations
│ │
│ ├── theme/ # Theme configuration
│ │ ├── index.ts # Theme barrel export
│ │ ├── colors.ts # Color scheme
│ │ ├── typography.ts # Typography settings
│ │ └── animation.ts # Animation configuration
│ │
│ ├── env/ # Environment configuration
│ │ └── index.ts # Environment exports (placeholder)
│ │
│ └── index.ts # Main barrel export (re-exports all domains)
│
├── dist/ # Build output (generated)
│ ├── **/*.js # Compiled JavaScript
│ └── **/*.d.ts # TypeScript declarations
│
├── eslint/ # ESLint configuration
│ └── base.js # Base ESLint config
│
├── tailwind/ # Tailwind configuration
│ └── preset.js # Tailwind preset
│
├── typescript/ # TypeScript configuration
│ ├── base.json # Base tsconfig
│ └── nextjs.json # Next.js-specific tsconfig
│
├── package.json # Package manifest
├── tsconfig.json # TypeScript configuration
└── README.md # This fileDirectory Organization
Each domain module follows a consistent structure:
Domain directory (
auth/,api/, etc.)- Contains all configuration related to that domain
- Organized by concern (e.g.,
session.ts,password.ts)
Domain index.ts
- Re-exports all configuration from the domain
- Provides a unified domain config object
- Acts as the public API for the domain
Main index.ts
- Re-exports all domain modules
- Provides the unified
configobject - Includes comprehensive JSDoc documentation
Installation
The package is automatically available in the monorepo workspace. Add it to your app's dependencies:
{
"dependencies": {
"@apollo-deploy/config": "*"
}
}Usage
Importing Configuration
There are three ways to import configuration, each with different use cases:
1. Import from Specific Domain Modules (Recommended for Tree-Shaking)
// Import from specific domain modules
import { authConfig } from '@apollo-deploy/config/auth';
import { apiConfig } from '@apollo-deploy/config/api';
import { SUPPORTED_LOCALES } from '@apollo-deploy/config/g11n';
import { CONNECTORS } from '@apollo-deploy/config/connectors';
import { themeConfig } from '@apollo-deploy/config/theme';Best for: When you only need specific configuration domains and want optimal tree-shaking.
2. Import from Main Barrel Export
// Import multiple configs from main barrel
import { authConfig, apiConfig, themeConfig } from '@apollo-deploy/config';Best for: When you need multiple configuration domains and want cleaner imports.
3. Import Unified Config Object
// Import the unified config object
import { config } from '@apollo-deploy/config';
// Access nested configuration
const sessionDuration = config.auth.session.duration;
const apiTimeout = config.api.request.timeout;
const primaryColor = config.theme.colors.primary.light;Best for: When you need to access configuration from multiple domains in one place, or when passing configuration as a single object.
Domain Modules
Authentication (@apollo-deploy/config/auth)
Provides session management, password policies, and verification settings.
import { authConfig } from '@apollo-deploy/config/auth';
// Access session configuration
const sessionDuration = authConfig.session.duration;
const cookieSettings = authConfig.session.cookie;
// Access password policy
const minLength = authConfig.password.minLength;API (@apollo-deploy/config/api)
Provides API endpoint definitions, request settings, and cache configuration.
import { apiConfig } from '@apollo-deploy/config/api';
// Access API endpoints
const baseUrl = apiConfig.endpoints.baseUrl;
// Access request configuration
const timeout = apiConfig.request.timeout;
const retrySettings = apiConfig.request.retry;Globalization (@apollo-deploy/config/g11n)
Provides locale definitions and i18n configuration.
import { g11nConfig, SUPPORTED_LOCALES } from '@apollo-deploy/config/g11n';
// Access supported locales
const locales = SUPPORTED_LOCALES;
const defaultLocale = g11nConfig.defaultLocale;Connectors (@apollo-deploy/config/connectors)
Provides platform connector definitions (Google Play, App Store, etc.).
import { CONNECTORS } from '@apollo-deploy/config/connectors';
// Access connector definitions
const googlePlayConnector = CONNECTORS.find(c => c.id === 'google-play');Theme (@apollo-deploy/config/theme)
Provides color schemes, typography, and animation settings.
import { themeConfig } from '@apollo-deploy/config/theme';
// Access theme settings
const defaultMode = themeConfig.mode.defaultMode;
const colors = themeConfig.colors;Environment (@apollo-deploy/config/env)
Provides environment variable validation and type-safe access.
Note: The environment module is currently a placeholder and will be populated during the environment configuration migration task.
import { env, isDevelopment, getApiUrl } from '@apollo-deploy/config/env';
// Type-safe environment access
const apiUrl = env.NEXT_PUBLIC_API_URL;
// Helper functions
if (isDevelopment()) {
console.log('Running in development mode');
}
const url = getApiUrl();Overriding Configuration
Applications can override shared configuration by importing and extending it. This pattern allows apps to customize specific values while inheriting all other shared defaults.
Basic Override Pattern
// In apps/apollo-app/src/config/app.config.ts
import { authConfig as sharedAuthConfig } from '@apollo-deploy/config/auth';
export const authConfig = {
...sharedAuthConfig,
session: {
...sharedAuthConfig.session,
duration: 14 * 24 * 60 * 60 * 1000, // Override: 14 days instead of 7
},
};Override Pattern Examples
Example 1: Override Nested Configuration
// Override API timeout for a specific app
import { apiConfig as sharedApiConfig } from '@apollo-deploy/config/api';
export const apiConfig = {
...sharedApiConfig,
request: {
...sharedApiConfig.request,
timeout: 60000, // Override: 60 seconds instead of default 30
retry: {
...sharedApiConfig.request.retry,
maxRetries: 5, // Override: 5 retries instead of default 3
},
},
};Example 2: Override Multiple Domains
// Override configuration from multiple domains
import { authConfig, apiConfig } from '@apollo-deploy/config';
export const appConfig = {
auth: {
...authConfig,
session: {
...authConfig.session,
duration: 14 * 24 * 60 * 60 * 1000, // 14 days
},
},
api: {
...apiConfig,
request: {
...apiConfig.request,
timeout: 60000, // 60 seconds
},
},
};Example 3: Override with Environment-Specific Values
// Override based on environment
import { authConfig as sharedAuthConfig } from '@apollo-deploy/config/auth';
const isDevelopment = process.env.NODE_ENV === 'development';
export const authConfig = {
...sharedAuthConfig,
session: {
...sharedAuthConfig.session,
// Longer session in development for convenience
duration: isDevelopment
? 30 * 24 * 60 * 60 * 1000 // 30 days in dev
: 7 * 24 * 60 * 60 * 1000, // 7 days in prod
cookie: {
...sharedAuthConfig.session.cookie,
// Secure cookies only in production
secure: !isDevelopment,
},
},
};Example 4: Override Using the Unified Config Object
// Override using the unified config object
import { config } from '@apollo-deploy/config';
export const appConfig = {
...config,
auth: {
...config.auth,
session: {
...config.auth.session,
duration: 14 * 24 * 60 * 60 * 1000,
},
},
};Example 5: Partial Override with Type Safety
// Create a type-safe partial override
import type { Config } from '@apollo-deploy/config';
import { config } from '@apollo-deploy/config';
// TypeScript will enforce correct structure
const overrides: Partial<Config> = {
auth: {
...config.auth,
session: {
...config.auth.session,
duration: 14 * 24 * 60 * 60 * 1000,
},
},
};
export const appConfig = {
...config,
...overrides,
};Override Pattern Best Practices
- Import the shared config - Always start with the shared configuration as your base
- Spread the base - Use object spread (
...) to preserve all shared values - Override selectively - Only override the specific values you need to change
- Maintain type safety - TypeScript will ensure your overrides are type-compatible
- Document overrides - Add comments explaining why the override is necessary
- Test overrides - Verify that your overrides work as expected and don't break functionality
- Keep overrides minimal - The more you override, the more you diverge from shared standards
- Consider environment variables - For values that change between environments, use environment variables instead of overrides
When to Override vs. When to Modify Shared Config
Override in your app when:
- The configuration is truly app-specific (e.g., longer session for admin app)
- You're experimenting with a value before making it shared
- The value differs based on app-specific requirements
Modify shared config when:
- Multiple apps need the same change
- The configuration represents a business rule that should be consistent
- You're fixing a bug or improving a default value
Common Override Pitfalls
❌ Don't: Override without spreading the base
// BAD: This loses all other session properties
export const authConfig = {
session: {
duration: 14 * 24 * 60 * 60 * 1000,
},
};✅ Do: Always spread the base configuration
// GOOD: This preserves all other session properties
export const authConfig = {
...sharedAuthConfig,
session: {
...sharedAuthConfig.session,
duration: 14 * 24 * 60 * 60 * 1000,
},
};❌ Don't: Override with incompatible types
// BAD: TypeScript will catch this error
export const authConfig = {
...sharedAuthConfig,
session: {
...sharedAuthConfig.session,
duration: "14 days", // Error: should be number, not string
},
};✅ Do: Maintain type compatibility
// GOOD: Type-safe override
export const authConfig = {
...sharedAuthConfig,
session: {
...sharedAuthConfig.session,
duration: 14 * 24 * 60 * 60 * 1000, // Correct type: number
},
};Real-World Usage Examples
Example 1: Using Auth Config in a Login Component
// In apps/apollo-auth/src/components/auth/login-form.tsx
import { authConfig } from '@apollo-deploy/config/auth';
import { useState } from 'react';
export function LoginForm() {
const [rememberMe, setRememberMe] = useState(false);
const handleSubmit = async (email: string, password: string) => {
// Use session duration from config
const sessionDuration = rememberMe
? authConfig.session.rememberMeDuration
: authConfig.session.duration;
// Use cookie settings from config
const cookieOptions = {
...authConfig.session.cookie,
maxAge: sessionDuration,
};
// Authenticate user...
};
return (
<form onSubmit={handleSubmit}>
{/* Form fields */}
{authConfig.session.enableRememberMe && (
<label>
<input
type="checkbox"
checked={rememberMe}
onChange={(e) => setRememberMe(e.target.checked)}
/>
Remember me for {authConfig.session.rememberMeDuration / (24 * 60 * 60 * 1000)} days
</label>
)}
</form>
);
}Example 2: Using API Config in a Data Fetching Hook
// In apps/apollo-app/src/hooks/use-fetch-data.ts
import { apiConfig } from '@apollo-deploy/config/api';
import useSWR from 'swr';
export function useFetchData<T>(endpoint: string) {
const fetcher = async (url: string) => {
const controller = new AbortController();
const timeoutId = setTimeout(
() => controller.abort(),
apiConfig.request.timeout
);
try {
const response = await fetch(url, {
signal: controller.signal,
credentials: apiConfig.request.withCredentials ? 'include' : 'omit',
});
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
} catch (error) {
clearTimeout(timeoutId);
throw error;
}
};
return useSWR<T>(
`${apiConfig.endpoints.baseUrl}${endpoint}`,
fetcher,
{
revalidateOnFocus: false,
dedupingInterval: apiConfig.cache.defaultTTL,
}
);
}Example 3: Using Theme Config in a Theme Provider
// In apps/apollo-app/src/providers/theme-provider.tsx
import { themeConfig } from '@apollo-deploy/config/theme';
import { createContext, useContext, useEffect, useState } from 'react';
type Theme = 'light' | 'dark' | 'system';
const ThemeContext = createContext<{
theme: Theme;
setTheme: (theme: Theme) => void;
}>({
theme: themeConfig.mode.defaultMode,
setTheme: () => {},
});
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<Theme>(() => {
if (!themeConfig.mode.enablePersistence) {
return themeConfig.mode.defaultMode;
}
const stored = localStorage.getItem(themeConfig.mode.storageKey);
return (stored as Theme) || themeConfig.mode.defaultMode;
});
useEffect(() => {
if (themeConfig.mode.enablePersistence) {
localStorage.setItem(themeConfig.mode.storageKey, theme);
}
// Apply theme to document
const root = document.documentElement;
root.classList.remove('light', 'dark');
if (theme === 'system') {
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
root.classList.add(systemTheme);
} else {
root.classList.add(theme);
}
}, [theme]);
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
export const useTheme = () => useContext(ThemeContext);Example 4: Using Connector Config in a Settings Page
// In apps/apollo-app/src/features/settings/components/connectors-list.tsx
import { CONNECTORS } from '@apollo-deploy/config/connectors';
export function ConnectorsList() {
return (
<div className="grid gap-4">
{CONNECTORS.map((connector) => (
<div key={connector.id} className="border rounded-lg p-4">
<div className="flex items-center gap-3">
{connector.icon}
<div>
<h3 className="font-semibold">{connector.name}</h3>
<p className="text-sm text-muted-foreground">
{connector.description}
</p>
</div>
</div>
{connector.supportedRegions && (
<div className="mt-2 text-sm">
<span className="font-medium">Regions:</span>{' '}
{connector.supportedRegions.join(', ')}
</div>
)}
<button className="mt-3">
{connector.connectLabel || 'Connect'}
</button>
</div>
))}
</div>
);
}Example 5: Using G11n Config in an i18n Setup
// In apps/apollo-app/src/lib/i18n.ts
import { SUPPORTED_LOCALES, DEFAULT_LOCALE, createI18nConfig } from '@apollo-deploy/config/g11n';
import i18next from 'i18next';
import { initReactI18next } from 'react-i18next';
// Create app-specific i18n config with custom namespaces
const i18nConfig = createI18nConfig([
'common',
'auth',
'settings',
'webhooks',
'workflows',
]);
i18next
.use(initReactI18next)
.init({
...i18nConfig,
resources: {
// Load translations for each supported locale
...SUPPORTED_LOCALES.reduce((acc, locale) => {
acc[locale.code] = {
common: require(`@/locales/${locale.code}/common.json`),
auth: require(`@/locales/${locale.code}/auth.json`),
settings: require(`@/locales/${locale.code}/settings.json`),
webhooks: require(`@/locales/${locale.code}/webhooks.json`),
workflows: require(`@/locales/${locale.code}/workflows.json`),
};
return acc;
}, {} as Record<string, any>),
},
});
export default i18next;Example 6: Using Unified Config Object
// In apps/apollo-app/src/lib/app-config.ts
import { config } from '@apollo-deploy/config';
// Access multiple config domains from one import
export function getAppMetadata() {
return {
// Auth settings
sessionDuration: config.auth.session.duration,
passwordMinLength: config.auth.password.minLength,
// API settings
apiBaseUrl: config.api.endpoints.baseUrl,
apiTimeout: config.api.request.timeout,
// Theme settings
defaultTheme: config.theme.mode.defaultMode,
primaryColor: config.theme.colors.primary.light,
// G11n settings
defaultLocale: config.g11n.defaultLocale,
supportedLocales: config.g11n.supportedLocales.map(l => l.code),
// Connectors
availableConnectors: config.connectors.length,
};
}Development
Building
npm run buildType Checking
npm run typecheckLinting
npm run lintAdding New Configuration
When adding new configuration to this package, follow these steps to ensure consistency and maintainability.
Step-by-Step Guide
1. Choose the Right Domain
Place configuration in the appropriate domain module:
auth/- Authentication, authorization, sessions, passwordsapi/- API endpoints, request settings, cache configurationg11n/- Locales, translations, internationalizationconnectors/- Third-party platform integrationstheme/- Colors, typography, animations, UI stylingenv/- Environment variables, validation schemas
If your configuration doesn't fit any existing domain, consider creating a new domain module.
2. Define TypeScript Interfaces
Create clear, well-documented interfaces with JSDoc comments:
// In packages/config/src/auth/mfa.ts
/**
* Multi-factor authentication configuration
*
* Controls MFA behavior including supported methods,
* backup codes, and enforcement policies.
*/
export interface MfaConfig {
/**
* Whether MFA is enabled for the application
* @default true
*/
enabled: boolean;
/**
* Supported MFA methods
* @default ['totp', 'sms']
*/
supportedMethods: ('totp' | 'sms' | 'email')[];
/**
* Number of backup codes to generate
* @default 10
*/
backupCodeCount: number;
/**
* Whether to enforce MFA for all users
* @default false
*/
enforceForAllUsers: boolean;
/**
* Grace period before MFA enforcement (in days)
* @default 7
*/
enforcementGracePeriod: number;
}
/**
* Multi-factor authentication configuration
*
* @example
* ```typescript
* import { MFA_CONFIG } from '@apollo-deploy/config/auth';
*
* if (MFA_CONFIG.enabled) {
* // Show MFA setup flow
* }
* ```
*/
export const MFA_CONFIG: MfaConfig = {
enabled: true,
supportedMethods: ['totp', 'sms'],
backupCodeCount: 10,
enforceForAllUsers: false,
enforcementGracePeriod: 7,
} as const;3. Export from Domain Index
Add exports to the domain's index.ts:
// In packages/config/src/auth/index.ts
export * from './session';
export * from './password';
export * from './verification';
export * from './mfa'; // Add new export
// Update the unified authConfig if needed
import { SESSION_CONFIG } from './session';
import { PASSWORD_POLICY } from './password';
import { EMAIL_VERIFICATION_CONFIG, PASSWORD_RESET_CONFIG } from './verification';
import { MFA_CONFIG } from './mfa'; // Import new config
export const authConfig = {
session: SESSION_CONFIG,
password: PASSWORD_POLICY,
emailVerification: EMAIL_VERIFICATION_CONFIG,
passwordReset: PASSWORD_RESET_CONFIG,
mfa: MFA_CONFIG, // Add to unified config
} as const;4. Update Main Barrel (if needed)
The main index.ts already re-exports all domains, but you may need to update the unified config object:
// In packages/config/src/index.ts
// The domain exports are already handled by:
export * from './auth';
// Update the unified config object if you added a new domain
export const config = {
auth: authConfig,
api: apiConfig,
theme: themeConfig,
connectors: CONNECTORS,
g11n: { /* ... */ },
// Add new domain here if you created one
} as const;5. Document in README
Add usage examples to this README:
#### Multi-Factor Authentication (`@apollo-deploy/config/auth`)
Provides MFA configuration including supported methods and enforcement policies.
\`\`\`typescript
import { MFA_CONFIG } from '@apollo-deploy/config/auth';
// Check if MFA is enabled
if (MFA_CONFIG.enabled) {
// Show MFA setup
}
// Get supported methods
const methods = MFA_CONFIG.supportedMethods;
\`\`\`6. Add JSDoc Comments
Document all exported values and types with JSDoc:
/**
* Brief description of what this config does
*
* More detailed explanation if needed. Include:
* - What problem it solves
* - When to use it
* - Any important caveats
*
* @example
* ```typescript
* import { CONFIG_NAME } from '@apollo-deploy/config/domain';
*
* // Show usage example
* const value = CONFIG_NAME.property;
* ```
*
* @see {@link RelatedType} for related configuration
*/
export const CONFIG_NAME = { /* ... */ } as const;Best Practices for New Configuration
Use
as constfor immutabilityexport const CONFIG = { value: 'example', } as const; // Makes the object deeply readonlyProvide sensible defaults
export const CONFIG = { enabled: true, // Safe default timeout: 30000, // Reasonable timeout } as const;Use environment variables for runtime values
export const CONFIG = { apiUrl: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000', debug: process.env.NODE_ENV === 'development', } as const;Group related configuration
// Good: Related settings grouped together export const SESSION_CONFIG = { duration: 7 * 24 * 60 * 60 * 1000, refreshThreshold: 24 * 60 * 60 * 1000, enableRefresh: true, } as const; // Avoid: Scattered individual exports export const SESSION_DURATION = 7 * 24 * 60 * 60 * 1000; export const REFRESH_THRESHOLD = 24 * 60 * 60 * 1000; export const ENABLE_REFRESH = true;Use TypeScript enums or union types for fixed values
export type AuthMethod = 'password' | 'oauth' | 'magic-link'; export const AUTH_CONFIG = { supportedMethods: ['password', 'oauth'] as AuthMethod[], } as const;Avoid business logic in config
// ❌ Bad: Business logic in config export const CONFIG = { calculateDiscount: (price: number) => price * 0.9, }; // ✅ Good: Pure data export const CONFIG = { discountPercentage: 0.9, } as const;Consider override-ability
// Make it easy for apps to override export const CONFIG = { // Flat structure is easier to override timeout: 30000, retries: 3, } as const; // Instead of deeply nested export const CONFIG = { request: { settings: { timeout: { value: 30000 }, }, }, } as const;
Testing New Configuration
After adding new configuration:
Build the package
cd packages/config npm run buildCheck type generation
# Verify .d.ts files were created ls dist/**/*.d.tsTest imports in an app
// In apps/apollo-app/src/test-config.ts import { NEW_CONFIG } from '@apollo-deploy/config/domain';
console.log(NEW_CONFIG);
4. **Verify TypeScript IntelliSense**
- Open an app file
- Type `import { ` and verify your new config appears in autocomplete
- Hover over the import to see JSDoc documentation
5. **Test overrides**
```typescript
// Verify apps can override your config
import { NEW_CONFIG } from '@apollo-deploy/config/domain';
const overridden = {
...NEW_CONFIG,
someProperty: 'new value',
};Troubleshooting
Module Resolution Errors
Problem: You see errors like Cannot find module '@apollo-deploy/config' or Module not found: Can't resolve '@apollo-deploy/config'
Solutions:
Check package.json dependencies
{ "dependencies": { "@apollo-deploy/config": "*" } }Ensure
@apollo-deploy/configis listed in your app'spackage.json.Install dependencies
# From workspace root npm install # Or using Turborepo turbo installRestart TypeScript server
- In VS Code:
Cmd/Ctrl + Shift + P→ "TypeScript: Restart TS Server" - Or restart your IDE
- In VS Code:
Clear build cache
# Clear Turborepo cache turbo clean # Clear Next.js cache rm -rf apps/*/.next # Reinstall npm install
Type Errors
Problem: You see type errors when importing from @apollo-deploy/config, such as Property 'session' does not exist on type 'typeof import(...)'
Solutions:
Build the config package
# From workspace root npm run build # Or build just the config package cd packages/config && npm run buildCheck tsconfig.json paths
{ "compilerOptions": { "paths": { "@apollo-deploy/config": ["../../packages/config/src"], "@apollo-deploy/config/*": ["../../packages/config/src/*"] } } }Verify type declarations exist
# Check that .d.ts files were generated ls packages/config/dist/**/*.d.tsRestart TypeScript server (see above)
Import Path Errors
Problem: Imports work in some files but not others, or you get inconsistent behavior
Solutions:
Use consistent import paths
// ✅ GOOD: Use package name import { authConfig } from '@apollo-deploy/config'; // ❌ BAD: Don't use relative paths to packages import { authConfig } from '../../packages/config/src/auth';Check for typos in import paths
// ✅ GOOD import { authConfig } from '@apollo-deploy/config/auth'; // ❌ BAD: Typo in path import { authConfig } from '@apollo-deploy/config/auths';
Circular Dependencies
Problem: You see warnings like Circular dependency detected or build failures related to circular imports
Solutions:
Check for app imports in config package
# Search for imports from apps in config package grep -r "from.*apps/" packages/config/src/The config package should NEVER import from applications.
Move app-specific logic out of config If you find app-specific code in the config package, move it to the appropriate app's
src/config/directory.Use proper dependency direction
- ✅ Apps can import from
@apollo-deploy/config - ❌
@apollo-deploy/configcannot import from apps
- ✅ Apps can import from
Override Not Working
Problem: Your configuration override doesn't seem to take effect
Solutions:
Verify you're using the overridden config
// Make sure you're importing YOUR config, not the shared one // ❌ BAD: Still using shared config import { authConfig } from '@apollo-deploy/config'; // ✅ GOOD: Using your overridden config import { authConfig } from '@/config/app.config';Check object spread order
// ❌ BAD: Override comes first, gets overwritten by spread export const authConfig = { session: { duration: 14 * 24 * 60 * 60 * 1000 }, ...sharedAuthConfig, }; // ✅ GOOD: Spread first, then override export const authConfig = { ...sharedAuthConfig, session: { ...sharedAuthConfig.session, duration: 14 * 24 * 60 * 60 * 1000, }, };Verify nested spreading
// ❌ BAD: Missing nested spread export const authConfig = { ...sharedAuthConfig, session: { duration: 14 * 24 * 60 * 60 * 1000, // Lost other session properties! }, }; // ✅ GOOD: Spread nested objects too export const authConfig = { ...sharedAuthConfig, session: { ...sharedAuthConfig.session, // Preserve other properties duration: 14 * 24 * 60 * 60 * 1000, }, };
Build Failures
Problem: Build fails with errors related to @apollo-deploy/config
Solutions:
Check build order Ensure
turbo.jsonbuilds the config package before apps:{ "pipeline": { "build": { "dependsOn": ["^build"] } } }Build config package first
# Build config package explicitly turbo build --filter=@apollo-deploy/config # Then build your app turbo build --filter=apollo-appCheck for syntax errors in config files
# Type check the config package cd packages/config npm run typecheck
Hot Reload Not Working
Problem: Changes to config package don't trigger hot reload in development
Solutions:
Restart development server
# Stop dev server (Ctrl+C) # Then restart npm run devCheck Turborepo watch configuration Ensure
turbo.jsonhas proper watch setup:{ "pipeline": { "dev": { "dependsOn": ["^build"], "cache": false } } }Use Turborepo dev mode
# Use Turborepo to run dev servers turbo dev
Environment Variable Issues
Problem: Environment variables aren't being validated or accessed correctly
Solutions:
Check .env files exist
# Each app should have .env or .env.local ls apps/*/.env*Verify environment variable names
// ✅ GOOD: Correct Next.js public variable NEXT_PUBLIC_API_URL=https://api.example.com // ❌ BAD: Missing NEXT_PUBLIC_ prefix API_URL=https://api.example.comRestart development server after .env changes Next.js only reads .env files on startup.
Getting Help
If you're still experiencing issues:
Check the migration status
- Some modules (like
env/) may still be placeholders - Refer to the migration tasks document
- Some modules (like
Review the design document
- See
.kiro/specs/shared-config-migration/design.md - Check if your use case is covered
- See
Ask the team
- Share the specific error message
- Include your import statements and usage
- Mention which app you're working in
Check for updates
# Pull latest changes git pull # Reinstall dependencies npm install # Rebuild everything turbo build
Migration Guide
If you're migrating from app-specific configuration to this shared package, follow these steps:
Step 1: Identify Shared Configuration
Review your app's configuration and identify what should be shared:
// In apps/apollo-app/src/config/app.config.ts
// ✅ Shared: Used by multiple apps
export const SESSION_DURATION = 7 * 24 * 60 * 60 * 1000;
export const PASSWORD_MIN_LENGTH = 8;
export const API_TIMEOUT = 30000;
// ❌ App-specific: Unique to this app
export const APP_NAME = 'Apollo Dashboard';
export const APP_PORT = 3001;
export const DASHBOARD_ROUTES = ['/overview', '/analytics'];Step 2: Move Shared Config to Package
Move shared configuration to the appropriate domain in @apollo-deploy/config:
// Move to packages/config/src/auth/session.ts
export const SESSION_CONFIG = {
duration: 7 * 24 * 60 * 60 * 1000,
// ... other session config
} as const;
// Move to packages/config/src/auth/password.ts
export const PASSWORD_POLICY = {
minLength: 8,
// ... other password config
} as const;
// Move to packages/config/src/api/request.ts
export const API_REQUEST_CONFIG = {
timeout: 30000,
// ... other API config
} as const;Step 3: Update Imports in Your App
Replace local imports with @apollo-deploy/config imports:
// Before
import { SESSION_DURATION, PASSWORD_MIN_LENGTH } from '@/config/app.config';
// After
import { SESSION_CONFIG, PASSWORD_POLICY } from '@apollo-deploy/config';
const sessionDuration = SESSION_CONFIG.duration;
const passwordMinLength = PASSWORD_POLICY.minLength;Step 4: Keep App-Specific Config Local
Keep app-specific configuration in your app's config directory:
// In apps/apollo-app/src/config/app.config.ts
// Import shared config
import { authConfig, apiConfig } from '@apollo-deploy/config';
// App-specific metadata
export const APP_CONFIG = {
name: 'Apollo Dashboard',
port: 3001,
routes: {
dashboard: '/overview',
analytics: '/analytics',
},
} as const;
// Override shared config if needed
export const appAuthConfig = {
...authConfig,
session: {
...authConfig.session,
duration: 14 * 24 * 60 * 60 * 1000, // Override: 14 days
},
};Step 5: Remove Duplicate Configuration
After migration, remove duplicate configuration files:
# Remove old config files (if everything is migrated)
rm -rf apps/apollo-app/src/config/auth.config.ts
rm -rf apps/apollo-app/src/config/api.config.ts
# Keep app-specific config
# apps/apollo-app/src/config/app.config.ts (app-specific only)Step 6: Update Tests
Update tests to import from @apollo-deploy/config:
// Before
import { SESSION_DURATION } from '@/config/app.config';
// After
import { SESSION_CONFIG } from '@apollo-deploy/config';
test('session duration', () => {
expect(SESSION_CONFIG.duration).toBe(7 * 24 * 60 * 60 * 1000);
});Step 7: Verify Migration
Build all apps
turbo buildRun all tests
turbo testCheck for duplicate config
# Search for duplicate configuration values grep -r "SESSION_DURATION" apps/ grep -r "PASSWORD_MIN_LENGTH" apps/Verify imports
# Ensure all apps import from @apollo-deploy/config grep -r "from '@apollo-deploy/config'" apps/
Migration Checklist
- [ ] Identified shared vs. app-specific configuration
- [ ] Moved shared config to
@apollo-deploy/config - [ ] Updated imports in all apps
- [ ] Kept app-specific config in app directories
- [ ] Removed duplicate configuration files
- [ ] Updated tests to use new imports
- [ ] Verified all apps build successfully
- [ ] Verified all tests pass
- [ ] Checked for remaining duplicate config
- [ ] Documented any app-specific overrides
Architecture
This package follows the monorepo principle: "Apps are Consumers". Applications are thin shells that compose configuration from shared packages rather than maintaining duplicate config definitions.
Dependency Rules
- ✅ Applications can import from
@apollo-deploy/config - ✅ Config package can import from other packages (e.g.,
zod) - ❌ Config package CANNOT import from applications
- ❌ Config package should not contain business logic
Design Principles
- Single Source of Truth: Shared configuration lives in one place
- Type Safety: Full TypeScript support with strict typing
- Immutability: Configuration objects use
as constfor immutability - Composability: Apps can compose and override configuration
- Documentation: All exports have JSDoc comments
- Discoverability: Clear structure and barrel exports make config easy to find
Frequently Asked Questions
General Questions
Q: Why do we need a shared config package?
A: The shared config package eliminates duplication, ensures consistency across apps, and provides a single source of truth for configuration. It makes it easier to update configuration and reduces the risk of inconsistencies.
Q: Can I still have app-specific configuration?
A: Yes! Keep app-specific configuration in your app's src/config/ directory. Only shared configuration should live in @apollo-deploy/config.
Q: What's the difference between shared and app-specific config?
A: Shared config is used by multiple apps (e.g., session duration, password policy). App-specific config is unique to one app (e.g., app name, app-specific routes).
Import Questions
Q: Should I import from @apollo-deploy/config or @apollo-deploy/config/auth?
A: Both work! Import from specific domains (@apollo-deploy/config/auth) for better tree-shaking, or from the main barrel (@apollo-deploy/config) for convenience.
Q: Can I import the unified config object?
A: Yes! import { config } from '@apollo-deploy/config' gives you access to all configuration in one object.
Q: Why can't I import from @apollo-deploy/config?
A: Make sure the package is listed in your package.json dependencies, run npm install, and restart your TypeScript server.
Override Questions
Q: How do I override shared configuration?
A: Import the shared config, spread it, and override specific values:
import { authConfig } from '@apollo-deploy/config';
export const myAuthConfig = {
...authConfig,
session: {
...authConfig.session,
duration: 14 * 24 * 60 * 60 * 1000,
},
};Q: Will my overrides break when shared config changes?
A: No! As long as you use the spread operator correctly, your overrides will be preserved and you'll inherit non-overridden changes.
Q: Can I override nested configuration?
A: Yes, but remember to spread nested objects too:
export const config = {
...sharedConfig,
nested: {
...sharedConfig.nested, // Don't forget this!
property: 'override',
},
};Q: Should I override in the config package or in my app?
A: Always override in your app. The config package should only contain shared defaults.
Development Questions
Q: Do I need to rebuild the config package after changes?
A: For production builds, yes. In development mode with turbo dev, changes should hot-reload automatically.
Q: How do I add new configuration?
A: See the "Adding New Configuration" section above for a step-by-step guide.
Q: Can the config package import from other packages?
A: Yes, it can import from other shared packages (like zod), but it should NEVER import from applications.
Q: Why am I getting circular dependency warnings?
A: The config package might be importing from an application. Config should only be imported by apps, never the other way around.
Type Safety Questions
Q: Why use as const?
A: as const makes the configuration object deeply readonly and preserves literal types, giving you better type safety and IntelliSense.
Q: How do I get TypeScript IntelliSense for config?
A: Make sure the config package is built (npm run build) and your TypeScript server is running. Hover over imports to see documentation.
Q: Can I use the config in JavaScript files?
A: Yes, but you'll lose type safety. We recommend using TypeScript for better developer experience.
Migration Questions
Q: How do I migrate from app-specific config to shared config?
A: See the "Migration Guide" section above for detailed steps.
Q: What if my app needs different values than other apps?
A: Use the override pattern to customize values for your app while keeping shared defaults.
Q: Do I need to migrate all config at once?
A: No! You can migrate incrementally. Start with the most duplicated configuration and work your way through.
Q: What happens to my old config files after migration?
A: You can delete them if all configuration has been migrated to @apollo-deploy/config or converted to app-specific overrides.
Environment Variables
Q: Where should environment variables be validated?
A: Environment variable validation will be handled by the env/ module (currently a placeholder, to be implemented).
Q: Can I use environment variables in config?
A: Yes! Use process.env.VARIABLE_NAME in your configuration:
export const CONFIG = {
apiUrl: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000',
} as const;Q: Should environment variables be in the config package or apps?
A: Shared environment variable schemas should be in @apollo-deploy/config/env. App-specific environment variables stay in the app.
Related Packages
@apollo-deploy/core- Shared business logic and types@apollo-deploy/components- Shared UI componentspackages/config/eslint- ESLint configurationpackages/config/typescript- TypeScript configuration
Contributing
When contributing to this package:
- Follow the existing structure and patterns
- Add comprehensive JSDoc comments
- Include usage examples in documentation
- Test your changes in at least one application
- Update this README with any new configuration
- Ensure all builds and tests pass
License
This package is part of the Apollo monorepo and follows the same license as the parent project.
