Infrastructure as Code
Define event stores and subscriptions in TypeScript, deploy with one command
DeltaBase supports Infrastructure as Code (IaC) through TypeScript configuration files. Define your event stores and subscriptions declaratively, and deploy them with a single command.
Quick Start
Section titled “Quick Start”1. Create Configuration File
Section titled “1. Create Configuration File”Create a deltabase.config.ts file in your project root:
import type { InfrastructureConfig as DeltaBaseConfig } from '@delta-base/server';
const config: DeltaBaseConfig = { eventStores: [ { name: 'my-service-events', description: 'Events for my service', subscriptions: [ { id: 'users-webhook', eventFilter: 'user.*', subscriberType: 'webhook', webhook: { url: 'https://my-service.com/api/projections/users/events', headers: { Authorization: 'Bearer my-secret-token', }, }, }, ], }, ],};
export default config;2. Deploy
Section titled “2. Deploy”pnpx @delta-base/cli deploy --api-key your-api-keyThat’s it. Your event store and subscriptions are now live.
Configuration Schema
Section titled “Configuration Schema”Event Store Configuration
Section titled “Event Store Configuration”interface EventStoreConfig { // Required: unique name (3-63 chars, lowercase alphanumeric + dash/underscore) name: string;
// Optional: human-readable description description?: string;
// Optional: storage and retention settings settings?: { retentionPeriodDays?: number; // How long to keep events maxStreamSizeBytes?: number; // Maximum size per stream };
// Optional: webhook subscriptions subscriptions?: SubscriptionConfig[];}Subscription Configuration
Section titled “Subscription Configuration”interface SubscriptionConfig { // Required: unique identifier for this subscription id: string;
// Required: which events to deliver (supports wildcards) eventFilter: string | string[];
// Required: delivery mechanism subscriberType: 'webhook';
// Required for webhook type webhook?: { url: string; // Endpoint URL headers?: Record<string, string>; // Custom headers timeoutMs?: number; // Request timeout retryPolicy?: { maxAttempts: number; // Total retry attempts backoffMinutes: number; // Wait between retries }; };
// Optional: where to start reading events initialPosition?: 'latest' | 'earliest';}Full Example
Section titled “Full Example”Here’s a complete configuration for a service with multiple projections:
import type { InfrastructureConfig as DeltaBaseConfig } from '@delta-base/server';
// Use environment variables for sensitive valuesconst SERVICE_URL = process.env.SERVICE_URL || 'http://localhost:8787';const EVENT_STORE_NAME = process.env.DELTABASE_EVENT_STORE_NAME || 'my-service-dev';const PROJECTION_TOKEN = process.env.PROJECTION_AUTH_TOKEN || 'dev-token';
const config: DeltaBaseConfig = { eventStores: [ { name: EVENT_STORE_NAME, description: 'Authentication and user management service', settings: { retentionPeriodDays: 365, maxStreamSizeBytes: 1073741824, // 1GB }, subscriptions: [ // Users projection { id: 'users-projection', eventFilter: ['user.created', 'user.updated', 'user.deleted'], subscriberType: 'webhook', webhook: { url: `${SERVICE_URL}/api/projections/users/events`, headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${PROJECTION_TOKEN}`, }, retryPolicy: { maxAttempts: 5, backoffMinutes: 2, }, }, }, // Organizations projection { id: 'organizations-projection', eventFilter: [ 'organization.created', 'organization.updated', 'organization.deleted', ], subscriberType: 'webhook', webhook: { url: `${SERVICE_URL}/api/projections/organizations/events`, headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${PROJECTION_TOKEN}`, }, retryPolicy: { maxAttempts: 5, backoffMinutes: 2, }, }, }, // Cross-aggregate projection (users + organizations + memberships) { id: 'user-organizations-projection', eventFilter: [ 'user.created', 'user.updated', 'user.deleted', 'organization.created', 'organization.updated', 'organization.deleted', 'membership.created', 'membership.updated', 'membership.deleted', ], subscriberType: 'webhook', webhook: { url: `${SERVICE_URL}/api/projections/user-organizations/events`, headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${PROJECTION_TOKEN}`, }, retryPolicy: { maxAttempts: 5, backoffMinutes: 2, }, }, }, ], }, ],};
export default config;Deployment Commands
Section titled “Deployment Commands”Basic Deployment
Section titled “Basic Deployment”# Deploy using default config file (deltabase.config.ts)pnpx @delta-base/cli deploy --api-key your-api-key
# Deploy using custom config filepnpx @delta-base/cli deploy --config ./config/production.ts --api-key your-api-key
# Deploy to custom API URLpnpx @delta-base/cli deploy --api-url https://api.your-domain.com --api-key your-api-keyDry Run
Section titled “Dry Run”Preview changes without applying them:
pnpx @delta-base/cli deploy --dry-run --api-key your-api-keyVerbose Output
Section titled “Verbose Output”Enable detailed logging:
pnpx @delta-base/cli deploy --verbose --api-key your-api-keyEnvironment Variables
Section titled “Environment Variables”Configure the CLI with environment variables instead of flags:
# Set API keyexport DELTABASE_API_KEY=your-api-key
# Set API URL (optional, defaults to production)export DELTABASE_API_URL=https://api.delta-base.com
# Deploy without flagspnpx @delta-base/cli deployUsing .env Files
Section titled “Using .env Files”Create a .env file:
DELTABASE_API_KEY=your-api-keyDELTABASE_API_URL=https://api.delta-base.com
# Your service configurationSERVICE_URL=https://my-service.example.comPROJECTION_AUTH_TOKEN=super-secret-tokenDELTABASE_EVENT_STORE_NAME=my-service-productionLoad it before deployment:
source .env && pnpx @delta-base/cli deployEnvironment-Specific Configurations
Section titled “Environment-Specific Configurations”Strategy 1: Environment Variables
Section titled “Strategy 1: Environment Variables”Use the same config file with different environment variables:
const SERVICE_URL = process.env.SERVICE_URL;const EVENT_STORE_NAME = process.env.DELTABASE_EVENT_STORE_NAME;
const config: DeltaBaseConfig = { eventStores: [ { name: EVENT_STORE_NAME, // Different per environment subscriptions: [ { id: 'users-projection', webhook: { url: `${SERVICE_URL}/api/projections/users/events`, // ... }, }, ], }, ],};Deploy with different env files:
# Developmentsource .env.development && pnpx @delta-base/cli deploy
# Stagingsource .env.staging && pnpx @delta-base/cli deploy
# Productionsource .env.production && pnpx @delta-base/cli deployStrategy 2: Separate Config Files
Section titled “Strategy 2: Separate Config Files”Create environment-specific config files:
config/├── deltabase.development.ts├── deltabase.staging.ts└── deltabase.production.tsDeploy the appropriate one:
pnpx @delta-base/cli deploy --config ./config/deltabase.production.tsReconciliation Behavior
Section titled “Reconciliation Behavior”The deploy command performs reconciliation - it compares your config to the current state and makes the minimum necessary changes.
Event Stores
Section titled “Event Stores”| Scenario | Behavior |
|---|---|
| Event store in config but doesn’t exist | Create new event store |
| Event store exists with different settings | Update settings |
| Event store exists and matches config | No change |
Subscriptions
Section titled “Subscriptions”| Scenario | Behavior |
|---|---|
| Subscription in config but doesn’t exist | Create new subscription |
| Subscription exists with different config | Update subscription |
| Subscription exists but not in config | Delete subscription |
| Subscription matches config | No change |
Warning: Subscriptions not in your config file will be deleted. This ensures your config file is the single source of truth.
CI/CD Integration
Section titled “CI/CD Integration”GitHub Actions
Section titled “GitHub Actions”name: Deploy Infrastructure
on: push: branches: [main] paths: - 'deltabase.config.ts'
jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- uses: actions/setup-node@v4 with: node-version: '20'
- name: Deploy DeltaBase Infrastructure run: pnpx @delta-base/cli deploy env: DELTABASE_API_KEY: ${{ secrets.DELTABASE_API_KEY }} SERVICE_URL: ${{ vars.SERVICE_URL }} PROJECTION_AUTH_TOKEN: ${{ secrets.PROJECTION_AUTH_TOKEN }} DELTABASE_EVENT_STORE_NAME: ${{ vars.EVENT_STORE_NAME }}GitLab CI
Section titled “GitLab CI”deploy-infrastructure: stage: deploy image: node:20 script: - npx @delta-base/cli deploy only: - main - tags variables: DELTABASE_API_KEY: $DELTABASE_API_KEY SERVICE_URL: $SERVICE_URLManual Workflow
Section titled “Manual Workflow”For more control, use a manual workflow:
name: Deploy Infrastructure (Manual)
on: workflow_dispatch: inputs: environment: description: 'Environment to deploy' required: true type: choice options: - development - staging - production dry_run: description: 'Dry run (preview only)' required: false type: boolean default: false
jobs: deploy: runs-on: ubuntu-latest environment: ${{ inputs.environment }} steps: - uses: actions/checkout@v4
- uses: actions/setup-node@v4 with: node-version: '20'
- name: Deploy (Dry Run) if: ${{ inputs.dry_run }} run: pnpx @delta-base/cli deploy --dry-run --verbose env: DELTABASE_API_KEY: ${{ secrets.DELTABASE_API_KEY }}
- name: Deploy if: ${{ !inputs.dry_run }} run: pnpx @delta-base/cli deploy env: DELTABASE_API_KEY: ${{ secrets.DELTABASE_API_KEY }}Best Practices
Section titled “Best Practices”1. Version Control Your Config
Section titled “1. Version Control Your Config”Treat deltabase.config.ts like any other infrastructure code:
git add deltabase.config.tsgit commit -m "Add users projection subscription"2. Use Environment Variables for Secrets
Section titled “2. Use Environment Variables for Secrets”Never commit tokens or secrets:
// Goodheaders: { Authorization: `Bearer ${process.env.PROJECTION_AUTH_TOKEN}`,}
// Bad - don't do thisheaders: { Authorization: 'Bearer my-actual-secret-token',}3. Start with Dry Run
Section titled “3. Start with Dry Run”Preview changes before applying:
pnpx @delta-base/cli deploy --dry-run4. Use Descriptive Subscription IDs
Section titled “4. Use Descriptive Subscription IDs”IDs should describe what the subscription does:
// Good{ id: 'users-projection' }{ id: 'order-notifications-webhook' }{ id: 'analytics-events-export' }
// Bad{ id: 'sub1' }{ id: 'webhook' }5. Modular Configuration
Section titled “5. Modular Configuration”For large projects, split your config:
export const usersSubscription = { id: 'users-projection', eventFilter: ['user.created', 'user.updated', 'user.deleted'], // ...};
// config/subscriptions/orders.tsexport const ordersSubscription = { id: 'orders-projection', eventFilter: ['order.created', 'order.shipped'], // ...};
// deltabase.config.tsimport { usersSubscription } from './config/subscriptions/users';import { ordersSubscription } from './config/subscriptions/orders';
const config: DeltaBaseConfig = { eventStores: [ { name: 'my-service', subscriptions: [usersSubscription, ordersSubscription], }, ],};Troubleshooting
Section titled “Troubleshooting””Configuration not found”
Section titled “”Configuration not found””Error: Could not find deltabase.config.tsSolution: Ensure the file exists in your project root, or use --config to specify the path.
”Authentication failed”
Section titled “”Authentication failed””Error: Invalid API keySolution:
- Check your API key is correct
- Ensure it’s set via
--api-keyflag orDELTABASE_API_KEYenvironment variable - Verify the key has the necessary permissions
”Validation error”
Section titled “”Validation error””Error: Event store name must be 3-63 charactersSolution: Event store names must:
- Be 3-63 characters long
- Use only lowercase letters, numbers, dashes, and underscores
- Start with a letter or number
”Webhook URL unreachable”
Section titled “”Webhook URL unreachable””The subscription was created but events aren’t being delivered.
Solution:
- Verify the webhook URL is publicly accessible
- Check your server is running and accepting requests
- Verify the
PROJECTION_AUTH_TOKENis correct on both sides
What’s Next?
Section titled “What’s Next?”- Deploy to Production - Full deployment guide
- Projections - Build read models that receive webhook events
- CLI Reference - Complete CLI documentation