prax-orm 0.11.0

A next-generation, type-safe ORM for Rust inspired by Prisma
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
---
import DocsLayout from '../../layouts/DocsLayout.astro';
import CodeBlock from '../../components/CodeBlock.astro';

const basicServerGroup = `// Define a server group for your database cluster
serverGroup MainCluster {
    server primary {
        url = env("PRIMARY_DATABASE_URL")
        role = "primary"
    }
}`;

const readReplicaSetup = `// Read replica configuration with primary and replicas
serverGroup ProductionCluster {
    @@strategy(ReadReplica)
    @@loadBalance(RoundRobin)

    server primary {
        url = env("PRIMARY_DATABASE_URL")
        role = "primary"
        weight = 1
    }

    server replica1 {
        url = env("REPLICA1_DATABASE_URL")
        role = "replica"
        weight = 2
        region = "us-east-1"
    }

    server replica2 {
        url = env("REPLICA2_DATABASE_URL")
        role = "replica"
        weight = 2
        region = "us-west-2"
    }
}`;

const multiRegionSetup = `// Multi-region deployment for geographic distribution
serverGroup GlobalCluster {
    @@strategy(MultiRegion)
    @@loadBalance(Nearest)

    server usEast {
        url = env("US_EAST_DATABASE_URL")
        role = "primary"
        region = "us-east-1"
        priority = 1
    }

    server euWest {
        url = env("EU_WEST_DATABASE_URL")
        role = "replica"
        region = "eu-west-1"
        priority = 2
    }

    server apSouth {
        url = env("AP_SOUTH_DATABASE_URL")
        role = "replica"
        region = "ap-southeast-1"
        priority = 3
    }
}`;

const highAvailability = `// High availability configuration with automatic failover
serverGroup HACluster {
    @@strategy(HighAvailability)

    server primary {
        url = env("PRIMARY_URL")
        role = "primary"
        priority = 1
        healthCheck = "/health"
        maxConnections = 100
    }

    server standby1 {
        url = env("STANDBY1_URL")
        role = "replica"
        priority = 2
        healthCheck = "/health"
    }

    server standby2 {
        url = env("STANDBY2_URL")
        role = "replica"
        priority = 3
        healthCheck = "/health"
    }
}`;

const shardingSetup = `// Sharding configuration for horizontal scaling
serverGroup ShardedCluster {
    @@strategy(Sharding)

    server shard1 {
        url = env("SHARD1_URL")
        role = "shard"
        shardKey = "user_id"
        shardRange = "0-999999"
    }

    server shard2 {
        url = env("SHARD2_URL")
        role = "shard"
        shardKey = "user_id"
        shardRange = "1000000-1999999"
    }

    server shard3 {
        url = env("SHARD3_URL")
        role = "shard"
        shardKey = "user_id"
        shardRange = "2000000-2999999"
    }
}`;

const analyticsSetup = `// Separate analytics server for reporting
serverGroup DataCluster {
    @@strategy(Custom)

    server primary {
        url = env("PRIMARY_DATABASE_URL")
        role = "primary"
    }

    server analytics {
        url = env("ANALYTICS_DATABASE_URL")
        role = "analytics"
        readOnly = true
    }

    server archive {
        url = env("ARCHIVE_DATABASE_URL")
        role = "archive"
        readOnly = true
    }
}`;

const weightedLoadBalancing = `// Weighted load balancing for uneven server capacity
serverGroup WeightedCluster {
    @@strategy(ReadReplica)
    @@loadBalance(Weighted)

    server primary {
        url = env("PRIMARY_URL")
        role = "primary"
        weight = 1    // Receives 1/6 of reads
    }

    server powerful {
        url = env("POWERFUL_REPLICA_URL")
        role = "replica"
        weight = 3    // Receives 3/6 of reads (50%)
    }

    server standard {
        url = env("STANDARD_REPLICA_URL")
        role = "replica"
        weight = 2    // Receives 2/6 of reads (~33%)
    }
}`;

const fullExample = `// Complete schema with server groups and models
serverGroup Production {
    @@strategy(ReadReplica)
    @@loadBalance(RoundRobin)

    server primary {
        url = env("DATABASE_URL")
        role = "primary"
        maxConnections = 50
    }

    server replica {
        url = env("REPLICA_URL")
        role = "replica"
        weight = 2
    }
}

model User {
    id        Int      @id @auto
    email     String   @unique
    name      String?
    posts     Post[]
    createdAt DateTime @default(now())
}

model Post {
    id        Int      @id @auto
    title     String
    content   String?
    author    User     @relation(fields: [authorId], references: [id])
    authorId  Int      @map("author_id")
    createdAt DateTime @default(now())
}`;

const rustUsage = `// Server groups are configured at runtime
use prax::{PraxClient, ServerGroupConfig};

let config = ServerGroupConfig::from_schema("Production")
    .with_read_write_splitting(true)
    .with_health_check_interval(Duration::from_secs(30));

let client = PraxClient::with_server_group(config).await?;

// Writes automatically go to primary
let user = client
    .user()
    .create()
    .data(data! {
        email: "user@example.com",
        name: "John"
    })
    .exec()
    .await?;

// Reads can be distributed to replicas
let users = client
    .user()
    .find_many()
    .exec()
    .await?;

// Force read from primary
let user = client
    .user()
    .find_unique()
    .where(user::id::equals(1))
    .use_primary()  // Force primary server
    .exec()
    .await?;`;
---

<DocsLayout title="Server Groups - Prax ORM">
  <article class="max-w-4xl mx-auto px-6 py-12">
    <header class="mb-12">
      <h1 class="text-4xl font-bold mb-4">Server Groups</h1>
      <p class="text-xl text-muted">
        Define multi-server database configurations for read replicas, geographic distribution, high availability, and horizontal scaling.
      </p>
    </header>

    <div class="space-y-12">
      <!-- Introduction -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">What are Server Groups?</h2>
        <p class="text-muted mb-4">
          Server groups allow you to organize multiple database servers for advanced deployment scenarios.
          They enable read replicas, multi-region deployments, high availability configurations, and sharding
          strategies directly in your Prax schema.
        </p>
        <div class="grid md:grid-cols-2 gap-4 mb-6">
          <div class="p-4 rounded-xl bg-success-500/10 border border-success-500/30">
            <h4 class="font-semibold text-success-400 mb-2">Use Cases</h4>
            <ul class="text-muted text-sm space-y-1">
              <li>• Read replica load balancing</li>
              <li>• Geographic distribution (multi-region)</li>
              <li>• High availability with automatic failover</li>
              <li>• Horizontal scaling with sharding</li>
              <li>• Separating analytics from production</li>
            </ul>
          </div>
          <div class="p-4 rounded-xl bg-info-500/10 border border-info-500/30">
            <h4 class="font-semibold text-info-400 mb-2">Strategies</h4>
            <ul class="text-muted text-sm space-y-1">
              <li>• <strong>ReadReplica</strong> - Primary + read replicas</li>
              <li>• <strong>MultiRegion</strong> - Geographic distribution</li>
              <li>• <strong>HighAvailability</strong> - Auto failover</li>
              <li>• <strong>Sharding</strong> - Horizontal partitioning</li>
              <li>• <strong>Custom</strong> - User-defined strategies</li>
            </ul>
          </div>
        </div>
      </section>

      <!-- Basic Server Group -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Basic Server Group</h2>
        <p class="text-muted mb-4">
          Define a server group with the <code class="px-2 py-1 bg-surface-elevated rounded">serverGroup</code> keyword.
          Each server within the group is defined with a name and properties like URL, role, and weight.
        </p>
        <CodeBlock code={basicServerGroup} lang="prax" filename="prax/schema.prax" />
      </section>

      <!-- Read Replica Setup -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Read Replica Configuration</h2>
        <p class="text-muted mb-4">
          The most common use case is distributing read queries across replicas while sending writes to the primary.
          Use the <code class="px-2 py-1 bg-surface-elevated rounded">&#64;&#64;strategy(ReadReplica)</code> attribute
          and configure load balancing.
        </p>
        <CodeBlock code={readReplicaSetup} lang="prax" filename="prax/schema.prax" />
        <div class="mt-4 p-4 rounded-xl bg-info-500/10 border border-info-500/30">
          <p class="text-info-400 text-sm">
            <strong>Note:</strong> The <code class="px-1 bg-surface-elevated rounded">weight</code> property determines
            the proportion of read traffic each server receives. Higher weights receive more traffic.
          </p>
        </div>
      </section>

      <!-- Multi-Region Setup -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Multi-Region Deployment</h2>
        <p class="text-muted mb-4">
          For globally distributed applications, use the <code class="px-2 py-1 bg-surface-elevated rounded">MultiRegion</code>
          strategy with <code class="px-2 py-1 bg-surface-elevated rounded">Nearest</code> load balancing to route queries
          to the geographically closest server.
        </p>
        <CodeBlock code={multiRegionSetup} lang="prax" filename="prax/schema.prax" />
      </section>

      <!-- High Availability -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">High Availability</h2>
        <p class="text-muted mb-4">
          Configure automatic failover with health checks and priority-based server selection.
          The <code class="px-2 py-1 bg-surface-elevated rounded">priority</code> property determines failover order.
        </p>
        <CodeBlock code={highAvailability} lang="prax" filename="prax/schema.prax" />
      </section>

      <!-- Sharding -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Sharding (Horizontal Scaling)</h2>
        <p class="text-muted mb-4">
          For very large datasets, shard your data across multiple servers. Each shard handles a
          specific range of data based on the shard key.
        </p>
        <CodeBlock code={shardingSetup} lang="prax" filename="prax/schema.prax" />
        <div class="mt-4 p-4 rounded-xl bg-warning-500/10 border border-warning-500/30">
          <p class="text-warning-400 text-sm">
            <strong>Caution:</strong> Sharding adds complexity to your application. Cross-shard queries
            and transactions require special handling. Consider read replicas or vertical scaling first.
          </p>
        </div>
      </section>

      <!-- Analytics Server -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Separating Analytics</h2>
        <p class="text-muted mb-4">
          Route heavy analytical queries to dedicated servers to avoid impacting production traffic.
          Use the <code class="px-2 py-1 bg-surface-elevated rounded">analytics</code> role for reporting servers.
        </p>
        <CodeBlock code={analyticsSetup} lang="prax" filename="prax/schema.prax" />
      </section>

      <!-- Weighted Load Balancing -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Weighted Load Balancing</h2>
        <p class="text-muted mb-4">
          When servers have different capacities, use weighted load balancing to distribute traffic
          proportionally. Higher-capacity servers can handle more requests.
        </p>
        <CodeBlock code={weightedLoadBalancing} lang="prax" filename="prax/schema.prax" />
      </section>

      <!-- Server Properties Reference -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Server Properties Reference</h2>
        <div class="overflow-x-auto">
          <table class="w-full text-sm">
            <thead>
              <tr class="border-b border-border">
                <th class="text-left py-3 px-4 font-semibold">Property</th>
                <th class="text-left py-3 px-4 font-semibold">Type</th>
                <th class="text-left py-3 px-4 font-semibold">Description</th>
              </tr>
            </thead>
            <tbody class="text-muted">
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">url</code></td>
                <td class="py-3 px-4">String</td>
                <td class="py-3 px-4">Database connection URL (required)</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">role</code></td>
                <td class="py-3 px-4">String</td>
                <td class="py-3 px-4">Server role: primary, replica, analytics, archive, shard</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">weight</code></td>
                <td class="py-3 px-4">Int</td>
                <td class="py-3 px-4">Load balancing weight (higher = more traffic)</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">priority</code></td>
                <td class="py-3 px-4">Int</td>
                <td class="py-3 px-4">Failover priority (lower = higher priority)</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">region</code></td>
                <td class="py-3 px-4">String</td>
                <td class="py-3 px-4">Geographic region identifier</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">healthCheck</code></td>
                <td class="py-3 px-4">String</td>
                <td class="py-3 px-4">Health check endpoint path</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">maxConnections</code></td>
                <td class="py-3 px-4">Int</td>
                <td class="py-3 px-4">Maximum connection pool size</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">readOnly</code></td>
                <td class="py-3 px-4">Boolean</td>
                <td class="py-3 px-4">Mark server as read-only</td>
              </tr>
            </tbody>
          </table>
        </div>
      </section>

      <!-- Group Attributes Reference -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Group Attributes Reference</h2>
        <div class="overflow-x-auto">
          <table class="w-full text-sm">
            <thead>
              <tr class="border-b border-border">
                <th class="text-left py-3 px-4 font-semibold">Attribute</th>
                <th class="text-left py-3 px-4 font-semibold">Values</th>
                <th class="text-left py-3 px-4 font-semibold">Description</th>
              </tr>
            </thead>
            <tbody class="text-muted">
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">&#64;&#64;strategy()</code></td>
                <td class="py-3 px-4">ReadReplica, MultiRegion, HighAvailability, Sharding, Custom</td>
                <td class="py-3 px-4">Server group strategy</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">&#64;&#64;loadBalance()</code></td>
                <td class="py-3 px-4">RoundRobin, Random, LeastConnections, Weighted, Nearest, Sticky</td>
                <td class="py-3 px-4">Load balancing algorithm</td>
              </tr>
            </tbody>
          </table>
        </div>
      </section>

      <!-- Full Example -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Complete Example</h2>
        <p class="text-muted mb-4">
          Here's a complete schema with server groups and models:
        </p>
        <CodeBlock code={fullExample} lang="prax" filename="prax/schema.prax" />
      </section>

      <!-- Runtime Usage -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Runtime Usage</h2>
        <p class="text-muted mb-4">
          Server groups are configured at runtime. Prax automatically routes queries based on the configured strategy.
        </p>
        <CodeBlock code={rustUsage} lang="rust" filename="src/main.rs" />
      </section>

      <!-- Best Practices -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Best Practices</h2>
        <div class="grid gap-4">
          <div class="p-4 rounded-xl bg-surface border border-border">
            <h4 class="font-semibold mb-2 text-primary-400">Use Environment Variables for URLs</h4>
            <p class="text-muted text-sm">
              Always use <code class="px-1 bg-surface-elevated rounded">env("VAR_NAME")</code> for database URLs.
              Never hardcode connection strings in the schema.
            </p>
          </div>
          <div class="p-4 rounded-xl bg-surface border border-border">
            <h4 class="font-semibold mb-2 text-primary-400">Configure Health Checks</h4>
            <p class="text-muted text-sm">
              For high availability setups, configure health check endpoints to enable automatic
              failover when a server becomes unavailable.
            </p>
          </div>
          <div class="p-4 rounded-xl bg-surface border border-border">
            <h4 class="font-semibold mb-2 text-primary-400">Start Simple</h4>
            <p class="text-muted text-sm">
              Begin with a single server or simple read replica setup. Add complexity (sharding,
              multi-region) only when needed based on actual load patterns.
            </p>
          </div>
          <div class="p-4 rounded-xl bg-surface border border-border">
            <h4 class="font-semibold mb-2 text-primary-400">Consider Replication Lag</h4>
            <p class="text-muted text-sm">
              Be aware that replicas may have slight replication lag. For operations requiring
              immediate consistency, use <code class="px-1 bg-surface-elevated rounded">.use_primary()</code>.
            </p>
          </div>
        </div>
      </section>
    </div>
  </article>
</DocsLayout>