Skip to content

Shared Backend Deployment

github-actions[bot] edited this page Apr 18, 2026 · 2 revisions

Shared-Backend Deployment Guide

Version: 0.9.2 Last Updated: February 10, 2026 Status: Production Ready

Overview

Shared-backend deployment means multiple frontend applications share a single nSelf backend infrastructure. This is the most cost-effective deployment model for organizations running multiple apps.

Recommended For:

  • Organizations with multiple apps (chat, notes, tasks, etc.)
  • Cost optimization (shared infrastructure)
  • Unified data and user management
  • Enterprise deployments
  • Multi-tenant SaaS platforms

Not Recommended For:

  • Single application deployments
  • Independent teams requiring full isolation
  • Simple use cases (use standalone instead)

Architecture

Platform Infrastructure (Shared):
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ api.example.com                         β”‚
β”‚   β”œβ”€β”€ Hasura GraphQL Engine            β”‚
β”‚   β”œβ”€β”€ PostgreSQL Database              β”‚
β”‚   └── Redis Cache                      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ auth.example.com                        β”‚
β”‚   └── Nhost Auth Service               β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ storage.example.com                     β”‚
β”‚   └── MinIO Object Storage             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Frontend Applications (Separate):
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ chat.example.com                        β”‚
β”‚   └── nself-chat (Next.js)             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ notes.example.com                       β”‚
β”‚   └── notes-app (Next.js)              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ tasks.example.com                       β”‚
β”‚   └── tasks-app (Next.js)              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Shared Services

Core Services (Required)

  1. PostgreSQL Database

    • Shared data store for all apps
    • Schema-based isolation per app
    • Row-level security (RLS) for data separation
  2. Hasura GraphQL Engine

    • Unified GraphQL API
    • Per-app permissions
    • Real-time subscriptions
  3. Nhost Auth

    • Centralized authentication
    • Single sign-on (SSO) support
    • Shared user accounts

Optional Services

  1. Redis

    • Session storage
    • Rate limiting
    • Cache layer
  2. MinIO (S3-compatible)

    • File storage
    • Per-app buckets
    • Shared media assets
  3. MeiliSearch

    • Full-text search
    • Per-app indexes

Prerequisites

Infrastructure Requirements

  • Server: 8GB RAM minimum (16GB recommended)
  • CPU: 4 cores minimum (8 cores recommended)
  • Storage: 100GB SSD minimum
  • Network: Static IP address
  • DNS: Wildcard DNS or multiple A records

Software Requirements

  • Docker 24.0+
  • Docker Compose 2.20+
  • nSelf CLI 0.4.2+
  • Nginx or Traefik (reverse proxy)
  • Let's Encrypt (SSL certificates)

Backend Setup

Step 1: Deploy Shared nSelf Backend

# Clone backend repository
git clone https://github.com/yourusername/nself-backend.git
cd nself-backend

# Install nSelf CLI
npm install -g @nself/cli@latest

# Initialize with all services
nself init --demo

Step 2: Configure for Multi-App

Edit .backend/.env:

# Database
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
POSTGRES_DB=shared_backend
POSTGRES_USER=postgres
POSTGRES_PASSWORD=your_secure_password

# Hasura
HASURA_GRAPHQL_ADMIN_SECRET=your_admin_secret
HASURA_GRAPHQL_JWT_SECRET={"type":"HS256","key":"your_jwt_secret_min_32_chars"}
HASURA_GRAPHQL_ENABLE_CONSOLE=false
HASURA_GRAPHQL_CORS_DOMAIN=https://chat.example.com,https://notes.example.com,https://tasks.example.com

# Auth - Multiple redirect URIs
AUTH_CLIENT_URL=https://auth.example.com
AUTH_REDIRECT_URL=https://chat.example.com/auth/callback,https://notes.example.com/auth/callback,https://tasks.example.com/auth/callback
AUTH_SERVER_URL=http://auth:4000

# Storage
MINIO_ROOT_USER=minioadmin
MINIO_ROOT_PASSWORD=your_minio_password

# Redis (optional)
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=your_redis_password

Step 3: Setup Database Schemas

Create schemas for each app:

-- Connect to PostgreSQL
psql -h localhost -U postgres -d shared_backend

-- Create schemas
CREATE SCHEMA IF NOT EXISTS nchat;
CREATE SCHEMA IF NOT EXISTS notes;
CREATE SCHEMA IF NOT EXISTS tasks;

-- Grant permissions
GRANT ALL ON SCHEMA nchat TO postgres;
GRANT ALL ON SCHEMA notes TO postgres;
GRANT ALL ON SCHEMA tasks TO postgres;

Step 4: Configure Hasura Permissions

Per-App Role Isolation:

# hasura/metadata/tables.yaml
- table:
    schema: nchat
    name: messages
  select_permissions:
    - role: nchat_user
      permission:
        columns: "*"
        filter:
          user_id:
            _eq: X-Hasura-User-Id
    - role: notes_user
      permission:
        columns: []
        filter: {}  # No access

- table:
    schema: notes
    name: documents
  select_permissions:
    - role: notes_user
      permission:
        columns: "*"
        filter:
          user_id:
            _eq: X-Hasura-User-Id
    - role: nchat_user
      permission:
        columns: []
        filter: {}  # No access

Step 5: Start Backend Services

# Start all services
nself start

# Verify services
nself status

# Check logs
nself logs hasura
nself logs auth

Expected Output:

βœ“ PostgreSQL     Running     Port 5432
βœ“ Hasura         Running     Port 8080
βœ“ Auth           Running     Port 4000
βœ“ MinIO          Running     Port 9000
βœ“ Redis          Running     Port 6379
βœ“ Admin          Running     Port 3021

Subdomain Routing

Step 1: DNS Configuration

Option A: Wildcard DNS (Recommended)

Type    Name    Value                   TTL
A       @       your.server.ip.address  3600
A       *       your.server.ip.address  3600

This creates:

  • api.example.com
  • auth.example.com
  • storage.example.com
  • chat.example.com
  • notes.example.com
  • tasks.example.com

Option B: Individual A Records

Type    Name        Value                   TTL
A       api         your.server.ip.address  3600
A       auth        your.server.ip.address  3600
A       storage     your.server.ip.address  3600
A       chat        your.server.ip.address  3600
A       notes       your.server.ip.address  3600
A       tasks       your.server.ip.address  3600

Step 2: Nginx Configuration

Main Config (/etc/nginx/nginx.conf):

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log main;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css text/xml text/javascript
               application/json application/javascript application/xml+rss;

    # Include site configs
    include /etc/nginx/sites-enabled/*;
}

Backend API (/etc/nginx/sites-available/api.example.com):

server {
    listen 80;
    server_name api.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name api.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;

    # Hasura GraphQL
    location / {
        proxy_pass http://localhost:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Auth Service (/etc/nginx/sites-available/auth.example.com):

server {
    listen 80;
    server_name auth.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name auth.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Auth service
    location / {
        proxy_pass http://localhost:4000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # CORS headers for auth
        add_header Access-Control-Allow-Origin "https://chat.example.com" always;
        add_header Access-Control-Allow-Origin "https://notes.example.com" always;
        add_header Access-Control-Allow-Origin "https://tasks.example.com" always;
        add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
        add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
        add_header Access-Control-Allow-Credentials "true" always;

        if ($request_method = OPTIONS) {
            return 204;
        }
    }
}

Storage Service (/etc/nginx/sites-available/storage.example.com):

server {
    listen 80;
    server_name storage.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name storage.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # MinIO S3
    location / {
        proxy_pass http://localhost:9000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # CORS for storage
        add_header Access-Control-Allow-Origin "*" always;
        add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
        add_header Access-Control-Allow-Headers "Content-Type" always;
    }
}

Enable Sites:

sudo ln -s /etc/nginx/sites-available/api.example.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/auth.example.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/storage.example.com /etc/nginx/sites-enabled/

sudo nginx -t
sudo systemctl restart nginx

Step 3: SSL Certificates

Wildcard Certificate (Recommended):

# Install certbot with DNS plugin (example: Cloudflare)
sudo apt-get install certbot python3-certbot-dns-cloudflare

# Create credentials file
sudo nano /etc/letsencrypt/cloudflare.ini
# Add:
# dns_cloudflare_api_token = your_cloudflare_api_token

sudo chmod 600 /etc/letsencrypt/cloudflare.ini

# Obtain wildcard certificate
sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  -d example.com \
  -d *.example.com

# Auto-renewal is configured automatically

Individual Certificates:

# Obtain certificate for each subdomain
sudo certbot --nginx -d api.example.com
sudo certbot --nginx -d auth.example.com
sudo certbot --nginx -d storage.example.com

App Deployment

App #1: nself-chat

Environment Variables (.env.local):

# Shared backend URLs
NEXT_PUBLIC_GRAPHQL_URL=https://api.example.com/v1/graphql
NEXT_PUBLIC_AUTH_URL=https://auth.example.com/v1/auth
NEXT_PUBLIC_STORAGE_URL=https://storage.example.com/v1/storage

# App-specific
NEXT_PUBLIC_APP_NAME=nself-chat
NEXT_PUBLIC_PRIMARY_COLOR=#6366f1
NEXT_PUBLIC_ENV=production

# App role (for Hasura permissions)
NEXT_PUBLIC_APP_ROLE=nchat_user

Deploy to Vercel:

# From nself-chat directory
vercel --prod

# Configure domain
# Vercel Dashboard β†’ Domains β†’ Add chat.example.com

Nginx Config (/etc/nginx/sites-available/chat.example.com):

server {
    listen 80;
    server_name chat.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name chat.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Frontend app
    location / {
        proxy_pass http://localhost:3001;  # Different port per app
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

App #2: Notes App

Environment Variables:

# Shared backend URLs (same as chat)
NEXT_PUBLIC_GRAPHQL_URL=https://api.example.com/v1/graphql
NEXT_PUBLIC_AUTH_URL=https://auth.example.com/v1/auth
NEXT_PUBLIC_STORAGE_URL=https://storage.example.com/v1/storage

# App-specific
NEXT_PUBLIC_APP_NAME=notes
NEXT_PUBLIC_PRIMARY_COLOR=#10b981
NEXT_PUBLIC_ENV=production

# App role
NEXT_PUBLIC_APP_ROLE=notes_user

Nginx Config (/etc/nginx/sites-available/notes.example.com):

server {
    listen 80;
    server_name notes.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name notes.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location / {
        proxy_pass http://localhost:3002;  # Different port
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

App #3: Tasks App

Similar configuration with tasks_user role and port 3003.

Auth Configuration

Multiple Redirect URIs

Update Auth service to allow all app callbacks:

# .backend/.env
AUTH_REDIRECT_URL=https://chat.example.com/auth/callback,https://notes.example.com/auth/callback,https://tasks.example.com/auth/callback

Cookie Domain Configuration

Shared Cookie Domain (allows SSO):

// In each app's auth configuration
export const authConfig = {
  cookieDomain: '.example.com',  // Works for all *.example.com
  cookieName: 'shared_auth_token',
  cookieSecure: true,
  cookieHttpOnly: true,
  cookieSameSite: 'strict',
}

Benefits:

  • Single sign-on (SSO) across all apps
  • User logs in once, authenticated everywhere
  • Shared session management

Implementation (src/config/auth.config.ts):

import { CookieOptions } from 'express'

export const cookieConfig: CookieOptions = {
  domain: process.env.NEXT_PUBLIC_COOKIE_DOMAIN || '.example.com',
  httpOnly: true,
  secure: process.env.NODE_ENV === 'production',
  sameSite: 'strict',
  maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
  path: '/',
}

export function setAuthCookie(res: Response, token: string) {
  res.cookie('auth_token', token, cookieConfig)
}

CORS Configuration

Multiple Origins:

// Backend CORS configuration
const allowedOrigins = [
  'https://chat.example.com',
  'https://notes.example.com',
  'https://tasks.example.com',
]

const corsOptions = {
  origin: (origin, callback) => {
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true)
    } else {
      callback(new Error('Not allowed by CORS'))
    }
  },
  credentials: true,
  optionsSuccessStatus: 200,
}

app.use(cors(corsOptions))

Hasura CORS (.backend/.env):

HASURA_GRAPHQL_CORS_DOMAIN=https://chat.example.com,https://notes.example.com,https://tasks.example.com

Database Isolation

Schema-Based Isolation

Each app uses its own PostgreSQL schema:

-- App schemas
CREATE SCHEMA nchat;
CREATE SCHEMA notes;
CREATE SCHEMA tasks;

-- Tables in separate schemas
CREATE TABLE nchat.messages (...);
CREATE TABLE notes.documents (...);
CREATE TABLE tasks.items (...);

Row-Level Security (RLS)

Enforce data isolation at database level:

-- Enable RLS
ALTER TABLE nchat.messages ENABLE ROW LEVEL SECURITY;
ALTER TABLE notes.documents ENABLE ROW LEVEL SECURITY;

-- Policy: Users can only access their own data
CREATE POLICY user_isolation_policy ON nchat.messages
  FOR ALL
  USING (user_id = current_setting('hasura.user_id')::uuid);

CREATE POLICY user_isolation_policy ON notes.documents
  FOR ALL
  USING (user_id = current_setting('hasura.user_id')::uuid);

Per-App User Namespacing

Add app context to user sessions:

// JWT claims
interface JWTClaims {
  'https://hasura.io/jwt/claims': {
    'x-hasura-allowed-roles': string[]
    'x-hasura-default-role': string
    'x-hasura-user-id': string
    'x-hasura-app-context': string  // 'nchat' | 'notes' | 'tasks'
  }
}

// Set on login
const claims = {
  'https://hasura.io/jwt/claims': {
    'x-hasura-allowed-roles': ['nchat_user'],
    'x-hasura-default-role': 'nchat_user',
    'x-hasura-user-id': user.id,
    'x-hasura-app-context': 'nchat',
  },
}

Cost Comparison

Standalone vs Shared-Backend

3 Apps Scenario:

Standalone (3 separate backends):

Service Unit Cost Quantity Total
Frontend (Vercel) $20/mo 3 $60/mo
Backend (DigitalOcean) $24/mo 3 $72/mo
Database (Managed) $15/mo 3 $45/mo
Storage $5/mo 3 $15/mo
Total $192/month

Shared-Backend:

Service Unit Cost Quantity Total
Frontend (Vercel) $20/mo 3 $60/mo
Backend (DigitalOcean 8GB) $48/mo 1 $48/mo
Database (Managed) $30/mo 1 $30/mo
Storage $10/mo 1 $10/mo
Total $148/month

Savings: $44/month (23% reduction)

Scaling Strategies

Horizontal Scaling

Load Balancer + Multiple Frontend Instances:

# Nginx load balancer
upstream chat_app {
    least_conn;
    server 10.0.1.10:3000;
    server 10.0.1.11:3000;
    server 10.0.1.12:3000;
}

server {
    listen 443 ssl http2;
    server_name chat.example.com;

    location / {
        proxy_pass http://chat_app;
    }
}

Backend Scaling:

# Kubernetes HPA (Horizontal Pod Autoscaler)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: hasura-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: hasura
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

Database Scaling

Read Replicas:

# PostgreSQL read replicas
primary:
  host: db-primary.example.com
  port: 5432

replicas:
  - host: db-replica-1.example.com
    port: 5432
  - host: db-replica-2.example.com
    port: 5432

Connection Pooling:

# PgBouncer configuration
[databases]
shared_backend = host=localhost port=5432 dbname=shared_backend

[pgbouncer]
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 20

Security Considerations

Tenant Isolation

  1. Schema-level separation: Each app in separate schema
  2. RLS policies: Enforce at database level
  3. JWT app context: Include app ID in tokens
  4. Hasura permissions: Per-app role restrictions

Network Security

# Firewall rules (ufw example)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp    # SSH
sudo ufw allow 80/tcp    # HTTP
sudo ufw allow 443/tcp   # HTTPS
sudo ufw enable

# Restrict backend ports to localhost only
sudo ufw deny 5432  # PostgreSQL
sudo ufw deny 8080  # Hasura
sudo ufw deny 4000  # Auth

Secrets Management

Use environment-specific secrets:

# Backend secrets
POSTGRES_PASSWORD=<strong-password>
HASURA_GRAPHQL_ADMIN_SECRET=<random-32-chars>
JWT_SECRET=<random-64-chars>

# Rotate secrets regularly (quarterly)

Rate Limiting

# Nginx rate limiting
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

server {
    location /v1/graphql {
        limit_req zone=api_limit burst=20 nodelay;
        proxy_pass http://localhost:8080;
    }
}

Monitoring

Service Health Checks

# Backend health
curl https://api.example.com/healthz

# Auth health
curl https://auth.example.com/healthz

# App health
curl https://chat.example.com/api/health

Prometheus Metrics

# prometheus.yml
scrape_configs:
  - job_name: 'hasura'
    static_configs:
      - targets: ['localhost:8080']

  - job_name: 'postgres'
    static_configs:
      - targets: ['localhost:9187']  # postgres_exporter

  - job_name: 'nginx'
    static_configs:
      - targets: ['localhost:9113']  # nginx_exporter

Grafana Dashboards

Pre-configured dashboards:

  • PostgreSQL metrics
  • Hasura query performance
  • Nginx traffic
  • Application errors

Troubleshooting

Issue: Apps can't authenticate

Symptoms: Login fails, 401 errors

Solutions:

  1. Check redirect URIs

    # Verify in .backend/.env
    echo $AUTH_REDIRECT_URL
  2. Verify cookie domain

    // Check in browser console
    document.cookie
  3. Check CORS configuration

    curl -I https://auth.example.com/v1/auth

Issue: Cross-app data leakage

Symptoms: User sees data from other apps

Solutions:

  1. Verify RLS policies

    SELECT * FROM pg_policies WHERE tablename = 'messages';
  2. Check Hasura permissions

    # Hasura Console β†’ Data β†’ Table β†’ Permissions
  3. Verify JWT app context

    // Decode JWT and check claims
    const decoded = jwt.decode(token)
    console.log(decoded['x-hasura-app-context'])

Issue: Performance degradation

Symptoms: Slow queries, timeouts

Solutions:

  1. Check database connections

    SELECT count(*) FROM pg_stat_activity;
  2. Enable connection pooling (PgBouncer)

  3. Add database indexes

    CREATE INDEX idx_messages_user_id ON nchat.messages(user_id);

Migration Guide

From Standalone to Shared-Backend

Step 1: Backup existing data

# Backup each standalone database
pg_dump -h localhost -U postgres app1_db > app1_backup.sql
pg_dump -h localhost -U postgres app2_db > app2_backup.sql

Step 2: Create schemas in shared database

CREATE SCHEMA app1;
CREATE SCHEMA app2;

Step 3: Restore data to schemas

# Restore with schema prefix
psql -h localhost -U postgres shared_backend < app1_backup.sql

Step 4: Update app configurations

Change environment variables to point to shared backend.

Step 5: Test thoroughly

Verify data isolation and authentication.

Next Steps

  1. Setup Monitoring: Monitoring.md
  2. Configure Backups: Backup-Strategy.md
  3. Review Security: Security-Checklist.md

Resources


Need Help?


🎯 Getting Started


✨ Features

Core Features

Communication

Security & Privacy

(See πŸ” Security section below for 2FA, PIN Lock, and security audits.)

Interactive

(Search lives in πŸ“š Reference below.)

Extensibility


πŸ“– Guides

User Guides

Developer Guides

Enterprise

Backend

Deployment


βš™οΈ Configuration


πŸ“‘ API

API Documentation


πŸš€ Deployment


πŸ“š Reference

Architecture

Quick Reference


πŸ” Security


πŸ†˜ Help


ℹ️ About


πŸ”— Links


v1.0.0 β€’ 2026

Clone this wiki locally