CRUD API for Users
Full Create-Read-Update-Delete API using JSON serialization. Serve: soma serve 042_crud_api.cell Test: curl -X POST localhost:8080/create?name=Alice&[email protected] curl localhost:8080/get/1 curl localhost:8080/list
Example 042 of the basic Soma programs: a small, commented program that teaches one thing. Every output below was printed by soma 3.2.4 when this page was built.
How it is built
The cell UserCRUD declares 6 signals in its face: create(name: String, email: String) -> String, get(id: String) -> String, list_all() -> String, update(id: String, name: String) -> String, delete(id: String) -> Map and demo() -> String; keeps 2 memory slots: users (Map<String, String>, persistent and consistent) and meta (Map<String, Int>, persistent and consistent). Handlers: create (line 32), get (line 48), list_all (line 56), update (line 65), delete (line 76) and demo (line 85).
The source
042_crud_api.cell
// ============================================================================
// 042 — CRUD API for Users
// ============================================================================
// Full Create-Read-Update-Delete API using JSON serialization.
//
// Serve: soma serve 042_crud_api.cell
// Test: curl -X POST localhost:8080/create?name=Alice&[email protected]
// curl localhost:8080/get/1
// curl localhost:8080/list
//
// Run: soma run 042_crud_api.cell demo
// Expect:
// Created: {"id": 1, "name": "Alice", "email": "[email protected]"}
// Created: {"id": 2, "name": "Bob", "email": "[email protected]"}
// All users: 2
cell UserCRUD {
face {
signal create(name: String, email: String) -> String
signal get(id: String) -> String
signal list_all() -> String
signal update(id: String, name: String) -> String
signal delete(id: String) -> Map
signal demo() -> String
}
memory {
users: Map<String, String> [persistent, consistent]
meta: Map<String, Int> [persistent, consistent]
}
on create(name: String, email: String) {
// Auto-increment ID
let current = meta.get("next_id")
if current == () {
meta.set("next_id", 1)
}
let id = meta.get("next_id")
meta.set("next_id", id + 1)
let id_str = to_string(id)
// Store as JSON
let user = map("id", id, "name", name, "email", email)
users.set(id_str, to_json(user))
return user
}
on get(id: String) {
let raw = users.get(id)
if raw == () {
return response(404, map("error", "user not found"))
}
return from_json(raw)
}
on list_all() {
let keys = users.keys()
let result = list()
for key in keys {
result = list(result, from_json(users.get(key)))
}
return result
}
on update(id: String, name: String) {
let raw = users.get(id)
if raw == () {
return response(404, map("error", "user not found"))
}
let existing = from_json(raw)
let updated = map("id", id, "name", name, "email", existing.email)
users.set(id, to_json(updated))
return updated
}
on delete(id: String) {
let existed = users.delete(id)
if existed == false {
return response(404, map("error", "user not found"))
}
return map("deleted", id)
}
// Self-contained demo
on demo() {
let u1 = create("Alice", "[email protected]")
print("Created: {u1}")
let u2 = create("Bob", "[email protected]")
print("Created: {u2}")
let all = list_all()
let count = all.length
print("All users: {count}")
return "done"
}
}What the toolchain says
$ soma check 042_crud_api.cell
warning: handler 'create' returns a Map (`user`) but its face declares `-> String` — fix the value or the face (an error in edition 2027)
--> 042_crud_api.cell:45:16
|
45 | return user
| ^
warning: handler 'update' returns a Map (`updated`) but its face declares `-> String` — fix the value or the face (an error in edition 2027)
--> 042_crud_api.cell:73:16
|
73 | return updated
| ^
✓ 2 warning(s), no errors.$ soma run 042_crud_api.cell demo
error: create(): the face declares `-> String` but the handler returned Map {"id": 1, "name": "Alice", "email": "ali
--> 042_crud_api.cell:45:16
|
45 | return user
| ^