Part 2: Routing and Data¶
Verified status as of March 28, 2026. Runtime note: FastFN auto-installs function-local dependencies from
requirements.txt/package.json; host runtimes are required infastfn dev --native, whilefastfn devdepends on a running Docker daemon.
Quick View¶
- Complexity: Intermediate
- Typical time: 25-35 minutes
- Outcome: dynamic path/query/body handling with explicit validation errors
1. Path params and catch-all¶
Create dynamic route files under functions/.
File: functions/tasks/[id].js
File: functions/reports/[...slug].js
File: functions/tasks/[id].py
File: functions/reports/[...slug].py
File: functions/tasks/[id].rs
Validation curls (same for all runtimes):
2. Query params with defaults¶
File: functions/tasks/search.js
File: functions/tasks/search.py
File: functions/tasks/search.rs
File: functions/tasks/search.php
Runtime curls:
3. JSON body parsing and validation¶
File: functions/tasks/post.js
exports.handler = async (event) => {
let payload;
try { payload = JSON.parse(event.body || "{}"); }
catch { return { status: 400, body: { error: "invalid JSON body" } }; }
if (!payload.title || typeof payload.title !== "string") {
return { status: 422, body: { error: "title must be a non-empty string" } };
}
return { status: 201, body: { id: 3, title: payload.title } };
};
File: functions/tasks/post.py
import json
def handler(event):
try:
payload = json.loads(event.get("body") or "{}")
except Exception:
return {"status": 400, "body": {"error": "invalid JSON body"}}
if not payload.get("title"):
return {"status": 422, "body": {"error": "title must be a non-empty string"}}
return {"status": 201, "body": {"id": 3, "title": payload["title"]}}
File: functions/tasks/post.rs
use serde_json::{json, Value};
pub fn handler(event: Value) -> Value {
let payload = event.get("body").and_then(|b| b.as_str()).unwrap_or("{}");
let parsed: Value = serde_json::from_str(payload).unwrap_or(json!({"_error": "invalid"}));
if parsed.get("_error").is_some() {
return json!({"status": 400, "body": {"error": "invalid JSON body"}});
}
if parsed.get("title").and_then(|x| x.as_str()).unwrap_or("").is_empty() {
return json!({"status": 422, "body": {"error": "title must be a non-empty string"}});
}
json!({"status": 201, "body": {"id": 3, "title": parsed["title"]}})
}
File: functions/tasks/post.php
<?php
function handler(array $event): array {
$raw = $event['body'] ?? '{}';
$payload = json_decode($raw, true);
if (!is_array($payload)) return ['status' => 400, 'body' => ['error' => 'invalid JSON body']];
if (empty($payload['title']) || !is_string($payload['title'])) {
return ['status' => 422, 'body' => ['error' => 'title must be a non-empty string']];
}
return ['status' => 201, 'body' => ['id' => 3, 'title' => $payload['title']]];
}
Runtime curls:
Flow diagram¶
flowchart LR
A["Route path"] --> B["Path params"]
B --> C["Query parsing"]
C --> D["Body parsing"]
D --> E["Validation result"]
E --> F["HTTP response"]
Troubleshooting¶
- wrong handler not invoked: verify filename prefixes and folder names
- params missing: verify
[id]or[...slug]pattern - body parse errors: confirm
Content-Type: application/jsonand valid JSON syntax
Next step¶
Go to Part 3: Configuration and Secrets