Quick Start¶
Build and run a REST API backed by a database in 10 minutes.
Prerequisites¶
- Docker (recommended) or Go 1.21+
- A terminal
Step 1: Create Your Service¶
That scaffolds a service that already runs, in the layout that stays maintainable as it grows — one declaration per file, grouped by kind. See Project Structure.
To follow along by hand instead, create the directory and these files yourself:
Why three files, and why .mycel
Mycel reads every .mycel file under the config directory, recursively,
and merges them into one service. The extension is the only thing that
matters — file and directory names are for your benefit, not the runtime's.
These three could equally be one file.
Project Structure covers how to lay this out as a service grows.
config.mycel — service identity¶
connectors.mycel — data sources¶
connector "api" {
type = "rest"
port = 3000
}
connector "db" {
type = "database"
driver = "sqlite"
database = "./data.db"
}
flows.mycel — data flows¶
flow "list_items" {
from {
connector = "api"
operation = "GET /items"
}
to {
connector = "db"
target = "items"
}
}
flow "create_item" {
from {
connector = "api"
operation = "POST /items"
}
to {
connector = "db"
target = "INSERT items"
}
}
flow "get_item" {
from {
connector = "api"
operation = "GET /items/:id"
}
to {
connector = "db"
target = "items WHERE id = :id"
}
}
Step 2: Run Your Service¶
With Docker¶
From source (requires Go 1.21+)¶
You should see:
__ __ _
| \/ |_ _ ___ ___ | |
| |\/| | | | |/ __/ _ \ | |
| | | | |_| | (_| __/ | |
|_| |_|\__, |\___\___| |_|
|___/
Declarative Microservice Framework
INFO Starting service: my-first-api
INFO Loaded 2 connectors
INFO Registered 3 flows
INFO REST server listening on :3000
Step 3: Test Your API¶
Open a new terminal:
# Create an item
curl -X POST http://localhost:3000/items \
-H "Content-Type: application/json" \
-d '{"name": "My first item", "description": "Created with Mycel!"}'
Response:
Response:
You just created a REST API with a database backend without writing any code.
Step 4: Add Data Transformation¶
Add automatic UUIDs and timestamps. Update flows.mycel:
flow "create_item" {
from {
connector = "api"
operation = "POST /items"
}
transform {
id = "uuid()"
name = "input.name"
description = "input.description"
created_at = "now()"
}
to {
connector = "db"
target = "INSERT items"
}
}
Two variables appear here that nothing declared. input is the data that arrived — for a REST source, the request body, path and query parameters, all flat. The field name on the left of each line is what gets written out. See Input and Output for the full picture.
Test it:
curl -X POST http://localhost:3000/items \
-H "Content-Type: application/json" \
-d '{"name": "With UUID", "description": "Auto-generated ID"}'
Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "With UUID",
"description": "Auto-generated ID",
"created_at": "2024-01-15T10:30:00Z"
}
Step 5: Add Input Validation¶
Create types.mycel:
type "item_input" {
name = string({
required = true
min_length = 1
max_length = 100
})
description = string({
required = false
max_length = 500
})
}
Reference it in the flow:
flow "create_item" {
from {
connector = "api"
operation = "POST /items"
}
validate {
input = "item_input"
}
transform {
id = "uuid()"
name = "input.name"
description = "default(input.description, '')"
created_at = "now()"
}
to {
connector = "db"
target = "INSERT items"
}
}
Invalid requests are now rejected:
Response:
What's Next¶
Use a real database¶
connector "db" {
type = "database"
driver = "postgres"
host = env("DB_HOST", "localhost")
port = 5432
database = "myapp"
user = env("DB_USER", "postgres")
password = env("DB_PASSWORD", "")
}
Add environment variables¶
Create a .env file (never commit it):
Mycel loads it automatically on startup.
Deploy with Docker¶
docker run \
-v ./config:/etc/mycel \
-e MYCEL_ENV=production \
-e MYCEL_LOG_FORMAT=json \
-e DB_HOST=db.example.com \
-e DB_PASSWORD=secret \
ghcr.io/matutetandil/mycel
Core Concepts Summary¶
| Concept | What it does |
|---|---|
| connector | Connects to an external system (database, API, queue, cache) |
| flow | Defines how data moves from a source to a target |
| transform | Reshapes data with CEL expressions |
| type | Validates data structure with schema constraints |