Server
The Grove server (
grove-server) is an HTTP server that exposes your Grove actions as API endpoints, handles webhook routing, and serves query results. It supports three deployment modes -- module, app, and project -- depending on whether you are serving a single module or an entire project.
Prerequisites: CLI Reference for command-line basics, Database Backends for connection configuration. What you'll learn: How to start and configure the Grove server, how actions map to HTTP endpoints, and how route declarations are handled.
Hosted vs self-run
For Manzano-hosted deployments, you do not run the server yourself — manzano deploy packages your project and Manzano runs grove-server on your behalf. See the CLI deploy section.
The rest of this page covers the self-run path: running grove-server directly against your own database and infrastructure. This is the right path for local integration testing, CI environments, and on-premise or self-hosted deployments.
For rapid local iteration without a full server setup, prefer manzano dev — it starts a development server with an in-memory database and hot reloading.
Starting the Server
grove-server --module <dir> --listen 127.0.0.1:3000
Deployment Modes
Module Mode
Serves a single module. All actions are exposed under the module's namespace.
grove-server --module ./order --listen 127.0.0.1:3000
Actions are available at:
POST /api/order:create_order
POST /api/order:cancel_order
GET /api/order:get_order?order_id=ord-001
App Mode
Serves multiple modules from a parent directory. Each module is automatically discovered and mounted.
grove-server --app ./my-project --listen 127.0.0.1:3000
Given a project structure:
my-project/
order/
actions.grove
customer/
actions.grove
Actions are available at:
POST /api/order:create_order
POST /api/customer:create_customer
Project Mode
Serves an entire project defined by a grove.toml configuration file. This is the recommended mode for production deployments.
grove-server --project ./my-project --listen 127.0.0.1:3000
Project mode reads grove.toml for module discovery, database configuration, and other settings. See Configuration Reference.
Listen Configuration
The --listen flag specifies the address and port the server binds to.
# Listen on localhost only
grove-server --module ./order --listen 127.0.0.1:3000
# Listen on all interfaces
grove-server --module ./order --listen 0.0.0.0:3000
# Listen on a specific port
grove-server --module ./order --listen 127.0.0.1:8080
Environment Variable
The listen address can also be set via the GROVE_LISTEN environment variable:
export GROVE_LISTEN="0.0.0.0:3000"
grove-server --module ./order
The --listen flag takes precedence over the environment variable.
Action Endpoints
Every action in a module is automatically exposed as an HTTP endpoint.
Endpoint Format
POST /api/<module>:<action>
- The module name is derived from the directory name.
- The action name is the identifier from the
actiondeclaration. - All action endpoints use the
POSTmethod by default.
Request Format
Action parameters are passed as a JSON object in the request body:
curl -X POST http://localhost:3000/api/order:create_order \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cust-001",
"items": [
{"product_id": "prod-001", "quantity": 2, "price": 19.99}
]
}'
Response Format
Successful responses return the action's return value as JSON:
{
"ok": true,
"data": {
"order_id": "ord-abc123"
}
}
Error responses include the error message:
{
"ok": false,
"error": "Order total must be positive"
}
HTTP Status Codes
| Scenario | Status Code |
|---|---|
| Action succeeds | 200 |
Validation failure (fail statement) | 400 |
| Authorization failure | 401 |
| Action not found | 404 |
| Method not allowed (non-POST to action endpoint) | 405 |
| Internal error | 500 |
Route Handling
route declarations provide custom HTTP method and path mappings for actions.
route POST "/orders" => create_order
route GET "/orders/:id" => get_order
route DELETE "/orders/:id" => cancel_order
route PUT "/orders/:id/ship" => ship_order
Path Parameters
Path parameters (:param) are extracted from the URL and merged into the action's request parameters:
route GET "/orders/:order_id" => get_order
// Equivalent to calling get_order with { "order_id": "<value-from-url>" }
curl http://localhost:3000/orders/ord-001
# Calls get_order(order_id: "ord-001")
Route Priority
Routes are matched in declaration order. More specific routes should appear before less specific ones:
route GET "/orders/pending" => list_pending_orders // matches first
route GET "/orders/:id" => get_order // matches second
Route vs. Action Endpoints
When a route declaration exists, both the route path and the default action endpoint are available:
route POST "/orders" => create_order
This makes the action available at both:
POST /orders(from the route)POST /api/order:create_order(default action endpoint)
Query Endpoints
Query declarations are exposed as GET endpoints:
GET /api/<module>:<query>?<params>
curl "http://localhost:3000/api/order:get_order?order_id=ord-001"
Query parameters are passed as URL query string parameters and are automatically type-coerced.
Webhook Endpoints
Webhook declarations register endpoints at their configured paths:
webhook stripe_events {
provider: "stripe"
secret: credentials.stripe_webhook_secret
path: "/webhooks/stripe"
handler: handle_stripe_event
}
This registers POST /webhooks/stripe. See Webhook Security for signature verification details.
Server Flags
| Flag | Description |
|---|---|
--module <dir> | Serve a single module (module mode) |
--app <dir> | Serve all modules in a directory (app mode) |
--project <dir> | Serve a project defined by grove.toml (project mode) |
--listen <addr:port> | Address and port to bind to |
--backend <backend> | Database backend: sqlite, postgres, mysql, tidb |
--db <connection> | Database connection string |
--cors-origin <origin> | Allowed CORS origin (default: none) |
--read-timeout <ms> | Request read timeout in milliseconds (default: 30000) |
--write-timeout <ms> | Response write timeout in milliseconds (default: 30000) |
--max-body-size <bytes> | Maximum request body size (default: 1048576, 1 MB) |
--workers <n> | Number of worker threads (default: number of CPU cores) |
--log-level <level> | Log level: error, warn, info, debug, trace |
Health Check
The server exposes a health check endpoint at:
GET /health
Response:
{
"status": "ok",
"version": "0.1.0",
"uptime_seconds": 3600
}
Graceful Shutdown
The server handles SIGTERM and SIGINT signals for graceful shutdown:
- Stop accepting new connections.
- Wait for in-flight requests to complete (up to 30 seconds).
- Shut down worker threads.
- Close database connections.
- Exit with code 0.
See Also
- CLI Reference -- command-line tools for development
- Database Backends -- database connection details
- Configuration Reference --
grove.tomlserver settings - Authorization Hooks -- securing action endpoints
- Webhook Security -- inbound webhook verification