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
---
import DocsLayout from '../../layouts/DocsLayout.astro';
import CodeBlock from '../../components/CodeBlock.astro';

const basicEnum = `// Basic enum definition
enum Role {
    USER
    ADMIN
    MODERATOR
}

// Using enums in models
model User {
    id    Int    @id @auto
    role  Role   @default(USER)
}`;

const enumWithValues = `// Enum with custom database values
enum Status {
    ACTIVE      @map("active")
    INACTIVE    @map("inactive")
    PENDING     @map("pending_review")
    SUSPENDED   @map("account_suspended")
}

// Map the entire enum to a different type name
enum OrderStatus {
    NEW
    PROCESSING
    SHIPPED
    DELIVERED
    CANCELLED
    REFUNDED

    @@map("order_status_enum")
}`;

const documentedEnum = `/// User subscription tier
/// Determines access levels and pricing
enum SubscriptionTier {
    /// Free tier with limited features
    FREE

    /// Basic paid tier
    /// @since 1.0.0
    BASIC

    /// Professional tier with all features
    /// @since 1.0.0
    PRO

    /// Enterprise tier with custom features
    /// @since 2.0.0
    ENTERPRISE
}`;

const enumWithModel = `// Complete example with multiple enums
enum UserStatus {
    ACTIVE
    INACTIVE
    SUSPENDED
    DELETED
}

enum NotificationType {
    EMAIL
    SMS
    PUSH
    IN_APP
}

enum Priority {
    LOW
    MEDIUM
    HIGH
    URGENT
}

model Notification {
    id        Int              @id @auto
    userId    Int
    user      User             @relation(fields: [userId], references: [id])
    type      NotificationType
    priority  Priority         @default(MEDIUM)
    title     String
    message   String
    read      Boolean          @default(false)
    createdAt DateTime         @default(now())

    @@index([userId, read])
    @@index([type, priority])
}`;

const enumArrays = `// Using enum arrays
enum Tag {
    FEATURED
    NEW
    SALE
    POPULAR
    LIMITED
}

enum Category {
    ELECTRONICS
    CLOTHING
    HOME
    SPORTS
    BOOKS
}

model Product {
    id         Int        @id @auto
    name       String
    tags       Tag[]      // Array of enum values
    categories Category[] // Multiple categories
    mainTag    Tag?       // Single optional enum

    @@index([tags], type: GIN)  // PostgreSQL GIN index for arrays
}`;

const enumBestPractices = `// ✅ Good: Descriptive enum names
enum PaymentStatus {
    PENDING_CONFIRMATION
    PROCESSING_PAYMENT
    PAYMENT_COMPLETED
    PAYMENT_FAILED
    REFUND_INITIATED
    REFUND_COMPLETED
}

// ✅ Good: Consistent naming convention (SCREAMING_SNAKE_CASE)
enum HttpMethod {
    GET
    POST
    PUT
    PATCH
    DELETE
    OPTIONS
    HEAD
}

// ✅ Good: Group related values
enum PermissionLevel {
    // Read permissions
    READ_OWN
    READ_TEAM
    READ_ALL

    // Write permissions
    WRITE_OWN
    WRITE_TEAM
    WRITE_ALL

    // Admin permissions
    ADMIN_TEAM
    ADMIN_ALL
}

// ❌ Avoid: Single-letter or unclear names
// enum S { A B C }  // Bad!

// ❌ Avoid: Mixing conventions
// enum Status { Active INACTIVE pending }  // Bad!`;

const enumInQueries = `// Generated Rust code usage
use prax::generated::{User, Role, user};

// Filter by enum value
let admins = client
    .user()
    .find_many()
    .where(user::role::equals(Role::ADMIN))
    .exec()
    .await?;

// Filter by multiple enum values
let privileged = client
    .user()
    .find_many()
    .where(user::role::in_(vec![Role::ADMIN, Role::MODERATOR]))
    .exec()
    .await?;

// Update with enum
let user = client
    .user()
    .update()
    .where(user::id::equals(1))
    .data(data! { role: Role::ADMIN })
    .exec()
    .await?;

// Create with enum default
let user = client
    .user()
    .create(data! {
        email: "user@example.com",
        // role defaults to USER
    })
    .exec()
    .await?;`;

const databaseMapping = `// PostgreSQL: Creates native ENUM type
// CREATE TYPE "Role" AS ENUM ('USER', 'ADMIN', 'MODERATOR');
enum Role {
    USER
    ADMIN
    MODERATOR
}

// MySQL: Uses ENUM column type
// ENUM('USER', 'ADMIN', 'MODERATOR')
enum Role {
    USER
    ADMIN
    MODERATOR
}

// SQLite: Uses CHECK constraint
// CHECK(role IN ('USER', 'ADMIN', 'MODERATOR'))
enum Role {
    USER
    ADMIN
    MODERATOR
}

// Custom database name
enum Role {
    USER
    ADMIN
    MODERATOR

    @@map("user_role")  // PostgreSQL type name: user_role
}`;
---

<DocsLayout title="Enums - 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">Enums</h1>
      <p class="text-xl text-muted">
        Define type-safe enumerated values for your database fields with compile-time checking.
      </p>
    </header>

    <div class="space-y-12">
      <!-- Introduction -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">What are Enums?</h2>
        <p class="text-muted mb-4">
          Enums (enumerations) define a fixed set of allowed values for a field. They provide type safety
          at both the database level and in your Rust code, preventing invalid values from being stored.
        </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">Benefits</h4>
            <ul class="text-muted text-sm space-y-1">
              <li>• Type-safe in Rust code</li>
              <li>• Database-level validation</li>
              <li>• Self-documenting schema</li>
              <li>• Compile-time error checking</li>
              <li>• IDE autocompletion</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">Use Cases</h4>
            <ul class="text-muted text-sm space-y-1">
              <li>• User roles and permissions</li>
              <li>• Order/payment status</li>
              <li>• Content visibility</li>
              <li>• Notification types</li>
              <li>• Category classifications</li>
            </ul>
          </div>
        </div>
      </section>

      <!-- Basic Definition -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Basic Definition</h2>
        <p class="text-muted mb-4">
          Define an enum with the <code class="px-2 py-1 bg-surface-elevated rounded">enum</code> keyword
          followed by the name and values in curly braces. Values use <code class="px-2 py-1 bg-surface-elevated rounded">SCREAMING_SNAKE_CASE</code> by convention.
        </p>
        <CodeBlock code={basicEnum} lang="prax" filename="prax/schema.prax" />
      </section>

      <!-- Custom Values -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Custom Database Values</h2>
        <p class="text-muted mb-4">
          Use <code class="px-2 py-1 bg-surface-elevated rounded">&#64;map()</code> to store different values
          in the database than the enum variant name. This is useful when integrating with existing databases
          or when you need human-readable database values.
        </p>
        <CodeBlock code={enumWithValues} lang="prax" filename="prax/schema.prax" />
      </section>

      <!-- Documentation -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Documented Enums</h2>
        <p class="text-muted mb-4">
          Add documentation comments to your enums and their values using triple-slash comments.
          This documentation is preserved in generated code and API schemas.
        </p>
        <CodeBlock code={documentedEnum} lang="prax" filename="prax/schema.prax" />
      </section>

      <!-- Complete Example -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Complete Example</h2>
        <p class="text-muted mb-4">
          Here's a realistic example showing multiple enums used together in a notification system:
        </p>
        <CodeBlock code={enumWithModel} lang="prax" filename="prax/schema.prax" />
      </section>

      <!-- Enum Arrays -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Enum Arrays</h2>
        <p class="text-muted mb-4">
          Fields can hold arrays of enum values, allowing multiple selections. Use GIN indexes
          in PostgreSQL for efficient querying of enum arrays.
        </p>
        <CodeBlock code={enumArrays} lang="prax" filename="prax/schema.prax" />
      </section>

      <!-- Database Mapping -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Database Representation</h2>
        <p class="text-muted mb-4">
          Prax handles enum storage differently based on the database provider:
        </p>
        <CodeBlock code={databaseMapping} lang="prax" />
        <div class="mt-4 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">Database</th>
                <th class="text-left py-3 px-4 font-semibold">Implementation</th>
                <th class="text-left py-3 px-4 font-semibold">Notes</th>
              </tr>
            </thead>
            <tbody class="text-muted">
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">PostgreSQL</code></td>
                <td class="py-3 px-4">Native ENUM type</td>
                <td class="py-3 px-4">Best performance, type-safe at DB level</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">MySQL</code></td>
                <td class="py-3 px-4">ENUM column type</td>
                <td class="py-3 px-4">Compact storage, limited to 65,535 values</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">SQLite</code></td>
                <td class="py-3 px-4">TEXT with CHECK constraint</td>
                <td class="py-3 px-4">Validation at insert/update time</td>
              </tr>
            </tbody>
          </table>
        </div>
      </section>

      <!-- Using in Queries -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Using Enums in Queries</h2>
        <p class="text-muted mb-4">
          Prax generates type-safe Rust enums that you can use directly in your queries:
        </p>
        <CodeBlock code={enumInQueries} lang="rust" filename="src/main.rs" />
      </section>

      <!-- Best Practices -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Best Practices</h2>
        <CodeBlock code={enumBestPractices} lang="prax" filename="prax/schema.prax" />

        <div class="mt-6 grid gap-4">
          <div class="p-4 rounded-xl bg-surface border border-border">
            <h4 class="font-semibold mb-2 text-primary-400">Naming Conventions</h4>
            <p class="text-muted text-sm">
              Use <code class="px-1 bg-surface-elevated rounded">PascalCase</code> for enum names and
              <code class="px-1 bg-surface-elevated rounded">SCREAMING_SNAKE_CASE</code> for values.
              Be descriptive and consistent across your schema.
            </p>
          </div>
          <div class="p-4 rounded-xl bg-surface border border-border">
            <h4 class="font-semibold mb-2 text-primary-400">Adding New Values</h4>
            <p class="text-muted text-sm">
              When adding new enum values, always add them at the end. Removing or reordering values
              may cause issues with existing data. Consider using soft-deprecation with documentation
              instead of removing values.
            </p>
          </div>
          <div class="p-4 rounded-xl bg-surface border border-border">
            <h4 class="font-semibold mb-2 text-primary-400">Default Values</h4>
            <p class="text-muted text-sm">
              Always consider whether a field should have a default enum value. This makes the API
              easier to use and reduces required fields during record creation.
            </p>
          </div>
        </div>
      </section>
    </div>
  </article>
</DocsLayout>