Skip to Content

Server Setup

Set up a Gluonic sync server in minutes with minimal configuration.

Prerequisites

  • Node.js 18+
  • Database (PostgreSQL, MySQL, SQLite, MongoDB, etc.)
  • Prisma (recommended) or another ORM

Installation

# Core server package pnpm add @gluonic/server # Database adapter pnpm add @gluonic/server-prisma # Prisma (available now) # pnpm add @gluonic/server-typeorm # TypeORM (coming soon) # pnpm add @gluonic/server-mongodb # MongoDB (coming soon) # Auth helper (optional but recommended) pnpm add @gluonic/auth-jwt

Basic Setup

Step 1: Define Your Prisma Schema

Note: Gluonic currently supports Prisma. Adapters for TypeORM and MongoDB are coming soon. You can also build a custom adapter for any database.

SyncAction Table (Required)

First, add the SyncAction table to track changes:

prisma/schema.prisma
// Required: SyncAction table tracks changes for delta sync model SyncAction { id BigInt @id @default(autoincrement()) orgId String model String modelId String op String patch String? actorId String clientTxId String? createdAt DateTime @default(now()) @@index([orgId, id]) }

This table is used by Gluonic automatically - you never query it directly.

Your Models

Gluonic needs to know which fields in each model are used for versioning (conflict detection) and ownership (data filtering). You have two options:

Follow naming conventions and Gluonic detects fields automatically:

model User { id String @id @default(cuid()) email String @unique name String? // Auto-discovery looks for these field names orgId String // Ownership field (orgId, workspaceId, tenantId, or ownerId) version Int @default(1) // Version field (must be named "version") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model Post { id String @id title String content String // Auto-discovery conventions orgId String // Ownership (private model) version Int @default(1) // Versioning author User @relation(fields: [authorId], references: [id]) authorId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model Tag { id String @id name String version Int @default(1) // Has version but NO orgId = public model }

Auto-discovery rules:

  • Required: Field named version
  • Optional: ONE field named orgId, workspaceId, tenantId, or ownerId
    • With ownership → Private (filtered by org)
    • Without ownership → Public (everyone sees all)
    • Multiple ownership fields → Error (use manual config)

Have existing schema with different field names? See Option B below for manual configuration.

Option B: Manual Configuration

Use any field names you want, then specify them in code:

model Post { id String @id organizationId String // Your custom ownership field rev Int // Your custom version field }
adapter.ts
const database = PrismaAdapter({ prisma, models: { post: { ownership: 'organizationId', // Tell Gluonic which field versioning: 'rev' // Tell Gluonic which field } } })

API Reference: PrismaAdapter - Complete configuration options

When to use:

  • Auto-discovery: Starting fresh (simpler)
  • Manual config: Existing schemas with different field names

Step 2: Create Sync Server

Now create and start your sync server:

server.ts
import { SyncServer } from '@gluonic/server' import { PrismaAdapter } from '@gluonic/server-prisma' import { JWTAuth } from '@gluonic/auth-jwt' import { prisma } from './db' // Create database adapter (auto-discovers models with orgId + version fields) const database = PrismaAdapter({ prisma }) // Create sync server with database and authentication const server = SyncServer({ database, auth: JWTAuth({ secret: process.env.JWT_SECRET }) }) // Start listening on port 3000 await server.listen({ port: 3000 })

How auto-discovery works: The Prisma client instance contains embedded schema metadata (DMMF - Data Model Meta Format). When you run prisma generate, your schema is compiled into the client. PrismaAdapter introspects this metadata at runtime to discover models - no schema file path needed!

Prerequisites:

  • Run prisma generate after defining your schema
  • Create ./db.ts that exports your Prisma client singleton:
// db.ts (Prisma singleton pattern) import { PrismaClient } from '@prisma/client' const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined } export const prisma = globalForPrisma.prisma ?? new PrismaClient() if (process.env.NODE_ENV !== 'production') { globalForPrisma.prisma = prisma }

This prevents multiple instances during development hot reload. See Prisma’s best practices  for more details.

API Reference: SyncServer · PrismaAdapter · JWTAuth

That’s it! Your sync server is now running with:

  • GET /sync/v1/bootstrap - Initial data load
  • GET /sync/v1/delta?since=N - Incremental updates
  • GET /sync/v1/ws - WebSocket real-time sync
  • POST /sync/v1/tx - Mutations (create, update, delete)
  • GET /sync/v1/metrics - Server metrics

Next: Set up the client or continue reading to configure your server.

Configuration

Database

Gluonic works with any database through adapters.

Available now:

Coming soon:

  • TypeORMAdapter - TypeORM (@gluonic/server-typeorm)
  • MongoAdapter - MongoDB (@gluonic/server-mongodb)
  • Or build your own adapter for any database

See Database Adapters guide for detailed configuration.

Authentication

All sync endpoints require authentication to secure your data.

Built-in JWT helper (recommended):

import { JWTAuth } from '@gluonic/auth-jwt' auth: JWTAuth({ secret: process.env.JWT_SECRET, expiresIn: '7d' // Optional: token expiration })

API Reference: JWTAuth - Complete authentication options

For custom authentication (OAuth, sessions, API keys), see the Authentication guide.

Real-Time Sync

Real-time WebSocket updates work out of the box with SyncServer.

Single Server (Default):

  • WebSocket endpoint automatically available at /sync/v1/ws
  • Clients connected to this server receive real-time updates
  • No additional configuration needed

With Broadcaster (Recommended for Production):

Add a broadcaster when you need:

  1. Service isolation - Run Gluonic as a separate service from your main API
  2. Cross-service updates - Your main API can trigger sync updates
  3. Horizontal scaling - Multiple Gluonic pods sync with each other
import { RedisBroadcaster } from '@gluonic/broadcaster-redis' const server = SyncServer({ database, auth: JWTAuth({ secret: process.env.JWT_SECRET }), broadcaster: RedisBroadcaster({ url: 'redis://localhost:6379' }) })

How it works:

  • Your main API writes data → publishes to Redis → all Gluonic pods receive → all WebSocket clients updated
  • Gluonic pod writes data → publishes to Redis → all other pods receive → all WebSocket clients updated

Available broadcasters:

  • RedisBroadcaster from @gluonic/broadcaster-redis (available now)
  • More coming soon: NATSBroadcaster, RabbitMQBroadcaster, etc.

API Reference: RedisBroadcaster - Broadcasting configuration

See Real-Time Sync guide for detailed setup and architecture.

Snapshots (Optional)

Cache bootstrap responses for 10x faster initial sync.

Snapshot adapters handle pre-computing and caching bootstrap payloads:

import { S3Snapshots } from '@gluonic/snapshots-s3' const server = SyncServer({ database, auth: JWTAuth({ secret: process.env.JWT_SECRET }), snapshots: S3Snapshots({ bucket: 'my-app-snapshots', intervalMinutes: 10 }) })

Available snapshot adapters:

  • S3Snapshots from @gluonic/snapshots-s3 (available now)
  • FileSnapshots from @gluonic/snapshots-file (coming soon)
  • RedisSnapshots from @gluonic/snapshots-redis (coming soon)

API Reference: S3Snapshots - Snapshot caching configuration

See Snapshots guide for detailed configuration.

Complete Example

Here’s everything together - a production-ready sync server with all features:

server.ts
import { SyncServer } from '@gluonic/server' import { PrismaAdapter } from '@gluonic/server-prisma' import { JWTAuth } from '@gluonic/auth-jwt' import { RedisBroadcaster } from '@gluonic/broadcaster-redis' import { S3Snapshots } from '@gluonic/snapshots-s3' import { prisma } from './db' // Create database adapter with auto-discovery const database = PrismaAdapter({ prisma }) // Create sync server with all features const server = SyncServer({ database, auth: JWTAuth({ secret: process.env.JWT_SECRET }), // Real-time broadcasting (optional) broadcaster: RedisBroadcaster({ url: process.env.REDIS_URL }), // Snapshot caching (optional) snapshots: S3Snapshots({ bucket: process.env.SNAPSHOT_BUCKET, intervalMinutes: 60 }), // Performance tuning (optional) maxDelta: 5000, retentionDays: 30 }) // Start listening await server.listen({ port: Number(process.env.PORT) || 3000, host: '0.0.0.0' }) console.log('🚀 Sync server running')

Environment Variables

.env
# Database DATABASE_URL="postgresql://user:pass@localhost:5432/myapp" # Authentication JWT_SECRET="your-super-secret-key-change-in-production" # Optional: Redis (for real-time sync) REDIS_URL="redis://localhost:6379" # Optional: Snapshots (for caching) SNAPSHOT_BUCKET="my-app-snapshots" AWS_REGION="us-east-1" # Server PORT=3000 NODE_ENV=production

Verification

Test your server is running:

# Check server is up curl http://localhost:3000/sync/v1/metrics # Expected response: { "bootstrapCount": 0, "deltaCount": 0, "wsConnections": 0 }

Next Steps

Essential:

Enhance your setup:

  • Real-Time Sync - Multi-pod deployment and cross-service broadcasting
  • Snapshots - Performance optimization with cached bootstrap
  • Deployment - Deploy to AWS, Vercel, or self-hosted

Explore:

Advanced

Need more control?

Last updated on