Cookbook: Simple CRUD Web App ​
In traditional frameworks, building a CRUD (Create, Read, Update, Delete) API is a tedious rite of passage. You have to set up a router, define an ORM schema, write controller functions, wire up validation middleware, and write boilerplate tests.
In Carotene, CRUD is almost entirely declarative. You define the data shape, lock down the security boundaries, and use the Generative Operator (@) to handle the annoying data-formatting tasks.
Let's build a fully secure, tested headless CMS for a company blog in a single file.
1. The Architecture Blueprint ​
We start by defining our domain, our data shape, and the physical database store.
domain Content {
// 1. The Data Shape
model Post {
id: UUID
title: String
slug: String
content: String
authorId: UUID
isPublished: Boolean
createdAt: Date
}
// 2. The Physical Storage
store Post {
model: Post
}
}2. The Backend Endpoints ​
Next, we define the backend block containing our four CRUD operations. Notice how we use the Zero-Trust verbs (creates, reads, updates, deletes) to strictly bound what each endpoint is allowed to do.
backend CMSApi {
// ==========================================
// CREATE
// ==========================================
function CreatePost(title: String, content: String) {
creates store.Post
requires session.isAuthenticated
// Use the Generative Operator to handle string manipulation
slug = @("Convert the ${title} into a URL-friendly lowercase slug, replacing spaces with hyphens and removing special characters.")
newPost = store.Post {
id: Crypto.generateUUID()
title: title
slug: slug
content: content
authorId: session.userId
isPublished: false
createdAt: Time.now()
}
return newPost
}
// ==========================================
// READ
// ==========================================
function GetPost(targetSlug: String) {
reads store.Post
// Anyone can read, but we must ensure it exists and is published
targetPost = store.Post(slug: targetSlug)
if (!targetPost.exists()) {
rejects with "Post not found"
}
if (targetPost.isPublished == false && session.role != "Admin") {
rejects with "You do not have permission to view drafts"
}
return targetPost
}
// ==========================================
// UPDATE
// ==========================================
function UpdatePostContent(postId: UUID, newContent: String) {
updates store.Post
requires session.isAuthenticated
targetPost = store.Post(postId)
// Only the author or an Admin can edit the post
if (targetPost.authorId != session.userId && session.role != "Admin") {
rejects with "Unauthorized to edit this post"
}
targetPost.content = newContent
return targetPost
}
// ==========================================
// DELETE
// ==========================================
function DeletePost(postId: UUID) {
deletes store.Post
requires session.role == "Admin" // Strict override: Only Admins can delete
store.Post(postId).delete()
return true
}
}3. The Proof (Business Logic Tests) ​
Because we used the Generative Operator to handle the URL slug generation in CreatePost, we need to write a test to mathematically guarantee the AI generates the string formatting code correctly.
We will also test our RBAC (Role-Based Access Control) to ensure our security logic holds up in the Vapor Sandbox.
// Test 1: AI Slug Generation
test "Generates a clean URL slug on creation" {
// 1. Mock an active user session
given mock session { userId: "user_123", isAuthenticated: true }
// 2. Trigger the action
result = CreatePost("Hello World! Welcome to 2026.", "This is the content.")
// 3. Assert the AI correctly synthesized the slugification logic
asserts result.slug == "hello-world-welcome-to-2026"
asserts store.Post(result.id).exists()
}
// Test 2: Security Validation
test "Blocks anonymous users from reading unpublished drafts" {
// 1. Mock an unauthenticated session
given mock session { isAuthenticated: false, role: "Guest" }
// 2. Mock a draft post in the database
given mock store.Post {
id: "post_1",
slug: "secret-draft",
isPublished: false
}
// 3. Assert the endpoint physically rejects the request
asserts GetPost("secret-draft") fails with "You do not have permission to view drafts"
}4. Compile and Deploy ​
With exactly 85 lines of highly readable text, you have architected a production-ready microservice.
When you run carrot build:
- The CLI provisions the database schema for
Post. - It generates a perfectly typed REST/tRPC router for
CMSApi. - It prompts the AI to write the Regex/string-manipulation code for the
slug. - It spins up the Sandbox, runs the tests, verifies the slug generation, and ensures the security rules return
403errors when violated.
The output is zero-dependency, highly optimized TypeScript or Go code resting in your .generated/ folder, ready to be deployed.