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: store.Post.title, content: store.Post.content) {
requires session.isAuthenticated
creates store.Post
implements {
// 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: store.Post.slug) {
reads store.Post
implements {
// Anyone can read, but we must ensure it exists and is published
targetPost = store.Post.findBy(slug: targetSlug)
if (!targetPost.exists()) {
throws "Post not found"
}
if (targetPost.isPublished == false && session.role != "Admin") {
throws "You do not have permission to view drafts"
}
return targetPost
}
}
// ==========================================
// UPDATE
// ==========================================
function UpdatePostContent(postId: store.Post.id, newContent: store.Post.content) {
requires session.isAuthenticated
updates store.Post
implements {
targetPost = store.Post.find(postId)
// Only the author or an Admin can edit the post
if (targetPost.authorId != session.userId && session.role != "Admin") {
throws "Unauthorized to edit this post"
}
targetPost.content = newContent
return targetPost
}
}
// ==========================================
// DELETE
// ==========================================
function DeletePost(postId: store.Post.id) {
requires session.role == "Admin" // Strict override: Only Admins can delete
deletes store.Post
implements {
store.Post.find(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.find(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") throws "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.