API Routes in Apps
Each app can expose API endpoints by placing
route.grovefiles inside itsapi/directory. API routes follow the same file-based routing conventions as pages but use Grove's route block syntax instead of HTML templates.
Prerequisites: File-Based Routing, Routes. What you'll learn: How API routes fit into the app directory structure, how they differ from page routes, and how to organize them.
Directory Convention
API routes live under apps/<app>/api/. Each route.grove file's directory path determines its URL:
apps/main/
api/
todo/
route.grove # /api/todo
[id]/
route.grove # /api/todo/:id
organizations/
route.grove # /api/organizations
[id]/
route.grove # /api/organizations/:id
API Routes vs Page Routes
Page routes and API routes serve different purposes and use different file types:
| Aspect | Page routes | API routes |
|---|---|---|
| File | page.html / index.html | route.grove |
| Location | App root directories | api/ subdirectory |
| Language | JQT templates | Grove |
| Response | Rendered HTML (or JSON via content negotiation) | JSON |
| Data loading | page.jq alongside the template | data blocks inside the route |
| Layouts | Wrapped by layout.html | No layout wrapping |
Route Block Syntax
Each route.grove file contains a single route { } block. The URL path comes from the file's location -- there is no inline path string:
route {
GET {
data result {
query todo.list_todos {
limit: input.query.limit
}
}
}
POST {
data result {
invoke todo.create_todo {
title: input.body.title
}
}
}
}
Dynamic Segments
Directory names with brackets capture path parameters, just like page routes:
api/todo/[id]/route.grove
Access parameters via input.params:
route {
GET {
data result {
fetch todo { id: input.params.id }
}
}
}
Example: Task Tracker Routes
A task tracker app might organize its API routes as:
apps/main/api/
todo/
route.grove # GET /api/todo (list), POST /api/todo (create)
[id]/
route.grove # GET /api/todo/:id (fetch), POST /api/todo/:id (update, delete)
Collection route at api/todo/route.grove:
route {
GET {
data result {
query todo.list_todos {
limit: input.query.limit,
offset: input.query.offset
}
}
}
POST {
data result {
invoke todo.create_todo {
title: input.body.title
}
}
}
}
Record route at api/todo/[id]/route.grove:
route {
GET {
data result {
fetch todo { id: input.params.id }
}
}
POST update {
data result {
invoke todo.update_todo {
id: input.params.id,
title: input.body.title
}
}
}
POST delete {
data result {
invoke todo.delete_todo { id: input.params.id }
}
}
}
See Also
- Routes -- Full route syntax reference
- Activities -- Data block operations used inside routes
- File-Based Routing -- Page routing conventions
- Data Loaders -- Alternative lightweight data endpoints via
page.jq