Skip to main content
The backend is a NestJS server that lives in apps/backend. It handles the REST API, authentication (Better Auth + Google OAuth), database access via Prisma, and agent routing.

Prerequisites

  • Repo cloned and dependencies installed (bun install)
  • PostgreSQL running locally

Environment variables

Copy the example file:
cp apps/backend/.env.example apps/backend/.env

Database

# PostgreSQL connection string (can be omitted if set in root .env)
DATABASE_URL=postgres://postgres:password@localhost:5432/zuko_dev

Authentication

# Random secret for Better Auth session signing (min 32 chars)
BETTER_AUTH_SECRET=your-secret-here

# Google OAuth — create credentials at https://console.cloud.google.com/apis/credentials
# Authorized redirect URI: http://localhost:3001/auth/callback/google
GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-google-client-secret

# Enable email/password login in addition to Google OAuth
BETTER_AUTH_INCLUDE_EMAILS_AUTH=true

URLs & CORS

BACKEND_URL=http://localhost:3001
FRONTEND_URL=http://localhost:3000

# List of allowed CORS origins (comma-separated)
TRUSTED_ORIGINS=http://localhost:3000,http://localhost:3001

AI & agents

OPENAI_API_KEY=sk-...

# Model for the in-process AI agent. Format: "<provider>/<model>"
# Supported: openai/gpt-4.1, openai/gpt-4o, anthropic/claude-sonnet-4-5, etc.
AGENT_MODEL=openai/gpt-4.1

# Shared secret that authenticates /api/agents/* endpoint calls
AGENT_TOKEN=your-agent-token

Tracing (optional)

LANGSMITH_TRACING=false
LANGSMITH_API_KEY=
LANGSMITH_PROJECT=zuko

Sandbox (Sprites)

# false = local Node.js execution (development default)
# true  = remote Sprites sandboxes (production)
SPRITES_ENABLED=false
SPRITES_TOKEN=
SPRITES_API_BASE_URL=https://sprites.fly.dev
AGENT_BRANCH=main
WORKING_DIR=/home/sprite/zuko
See Sandbox Setup for full configuration details.

Database setup

Create the database

createdb zuko_dev

Generate the Prisma client

bun nx run @zuko/models:prisma:generate

Run migrations

bun nx run @zuko/models:prisma:migrate -- --name init

Seed test data

Creates a test user (e2e@example.com / TestPassword123!) and sample CRM data:
bun nx run @zuko/models:seed

Browse the database

bun nx run @zuko/models:prisma:studio
# Opens Prisma Studio at http://localhost:5555

Google OAuth

  1. Go to Google Cloud Console → Credentials
  2. Click Create Credentials → OAuth client ID
  3. Choose Web application and fill in:
    • Name: Zuko (local)
    • Authorized JavaScript origins: http://localhost:3000
    • Authorized redirect URIs: http://localhost:3001/auth/callback/google
  4. Copy the Client ID and Client Secret
  5. Paste them into apps/backend/.env as GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET

Apollo.io OAuth

Required to enable the Apollo.io integration (contact enrichment, people/company search).
  1. Go to Apollo.io Developer Portal and open OAuth Registration
  2. Create a new OAuth app and fill in:
    • App name: Zuko (local) (or any name)
    • Redirect URL: http://localhost:3000/api/integrations/apollo/callback
    • Scopes: select all of the following (must match APOLLO_SCOPES in apollo-integration.service.ts):
      • read_user_profile
      • contacts_search
      • organizations_search
      • organizations_enrich
      • organizations_bulk_enrich
      • credit_usage_stats_read
      • emailer_campaigns_search
      • emailer_campaigns_create
      • emailer_campaigns_update
      • emailer_campaigns_add_contact_ids
      • emailer_campaigns_approve
  3. Copy the Client ID and Client Secret
  4. Add to apps/backend/.env:
APOLLO_CLIENT_ID=your-apollo-client-id
APOLLO_CLIENT_SECRET=your-apollo-client-secret
APOLLO_REDIRECT_URI=http://localhost:3000/api/integrations/apollo/callback
APOLLO_AUTHORIZATION_ENDPOINT=https://mcp.apollo.io/mcp/oauth_metadata/redirect_to_authorize
APOLLO_TOKEN_ENDPOINT=https://mcp.apollo.io/api/v1/oauth/token
APOLLO_REVOCATION_ENDPOINT=https://mcp.apollo.io/api/v1/oauth/revoke
Once configured, users can connect their Apollo account from Settings → Connections.

Start the development server

bun nx run @zuko/backend:serve
The API is available at http://localhost:3001.
EndpointDescription
http://localhost:3001REST API
http://localhost:3001/authBetter Auth routes

Useful commands

bun nx run @zuko/backend:serve         # Start backend only
bun nx run @zuko/backend:build         # Production build
bun nx run @zuko/backend:test          # Unit tests
bun nx run @zuko/backend:lint          # Lint

# Database
bun nx run @zuko/models:prisma:generate                   # Regenerate Prisma client
bun nx run @zuko/models:prisma:migrate -- --name <name>   # New migration
bun nx run @zuko/models:prisma:studio                     # DB browser (port 5555)
bun nx run @zuko/models:prisma:reset                      # Reset DB (destructive!)
bun nx run @zuko/models:seed                              # Seed test data

Troubleshooting

Ensure PostgreSQL is running:
# macOS
brew services start postgresql@16

# Linux
sudo systemctl start postgresql
Test the connection:
psql -U postgres -d zuko_dev
Check DATABASE_URL format: postgres://user:password@host:port/dbname
Clear the cached client and regenerate:
rm -rf node_modules/.prisma/
bun nx run @zuko/models:prisma:generate
  • Ensure TRUSTED_ORIGINS includes both http://localhost:3000 and http://localhost:3001 - Clear browser cookies and retry - Confirm the Google OAuth redirect URI is exactly http://localhost:3001/auth/callback/google
lsof -ti:3001 | xargs kill -9