URL Parameters
Learn how to capture and use dynamic URL parameters in Fastro applications
URL parameters allow you to capture dynamic segments from the request URL.
Fastro automatically parses these parameters and makes them available through
the req.params
object.
Basic Usage
import fastro, { HttpRequest } from "https://fastro.deno.dev/mod.ts";
const f = new fastro();
// Single parameter
f.get("/:user", (req: HttpRequest) => {
const { user } = req.params || {};
return Response.json({ user });
});
await f.serve();
You can access with: http://localhost:8000/agus
This will return: {"user": "agus"}
Multiple Parameters
Capture multiple dynamic segments in a single route:
f.get("/users/:id/posts/:postId", (req: HttpRequest) => {
const { id, postId } = req.params || {};
return Response.json({
userId: id,
postId: postId,
});
});
Access with: http://localhost:8000/users/123/posts/456
Returns: {"userId": "123", "postId": "456"}
Optional Parameters
Use the ?
modifier to make parameters optional:
f.get("/profile/:username/:tab?", (req: HttpRequest) => {
const { username, tab } = req.params || {};
return Response.json({
username,
tab: tab || "overview",
});
});
Both URLs work:
http://localhost:8000/profile/john
→{"username": "john", "tab": "overview"}
http://localhost:8000/profile/john/settings
→{"username": "john", "tab": "settings"}
Wildcard Parameters
Capture remaining path segments with *
:
f.get("/files/*", (req: HttpRequest) => {
const wildcard = req.params?.["*"];
return Response.json({
path: wildcard,
segments: wildcard?.split("/") || [],
});
});
Access with: http://localhost:8000/files/documents/reports/2024/summary.pdf
Returns:
{
"path": "documents/reports/2024/summary.pdf",
"segments": ["documents", "reports", "2024", "summary.pdf"]
}
Parameter Validation
Always validate parameters for production applications:
f.get("/api/users/:id", (req: HttpRequest) => {
const id = req.params?.id;
// Validate required parameter
if (!id) {
return new Response("User ID is required", { status: 400 });
}
// Validate numeric ID
if (isNaN(Number(id))) {
return new Response("Invalid user ID format", { status: 400 });
}
const userId = Number(id);
// Business logic validation
if (userId < 1) {
return new Response("User ID must be positive", { status: 400 });
}
return Response.json({
userId,
message: `Fetching user ${userId}`,
});
});
Real-world Examples
Blog Application
// Blog post by slug
f.get("/blog/:slug", (req: HttpRequest) => {
const { slug } = req.params || {};
// Fetch post from database using slug
return Response.json({ post: `Content for ${slug}` });
});
// Blog post with category
f.get("/blog/:category/:slug", (req: HttpRequest) => {
const { category, slug } = req.params || {};
return Response.json({ category, slug });
});
API Endpoints
// RESTful API patterns
f.get("/api/v1/users/:id", (req: HttpRequest) => {
const { id } = req.params || {};
return Response.json({ action: "get", userId: id });
});
f.put("/api/v1/users/:id", (req: HttpRequest) => {
const { id } = req.params || {};
return Response.json({ action: "update", userId: id });
});
f.delete("/api/v1/users/:id", (req: HttpRequest) => {
const { id } = req.params || {};
return Response.json({ action: "delete", userId: id });
});
File Serving
// Serve files with nested paths
f.get("/static/*", (req: HttpRequest) => {
const filePath = req.params?.["*"];
if (!filePath) {
return new Response("File path required", { status: 400 });
}
// Security: prevent directory traversal
if (filePath.includes("..")) {
return new Response("Invalid path", { status: 403 });
}
return Response.json({
message: `Serving file: ${filePath}`,
});
});
Best Practices
1. Always Check for Parameter Existence
// Good
const { id } = req.params || {};
if (!id) {
return new Response("ID required", { status: 400 });
}
// Avoid
const id = req.params.id; // Can throw if params is undefined
2. Use Descriptive Parameter Names
// Good
f.get("/users/:userId/orders/:orderId", handler);
// Avoid
f.get("/users/:id1/orders/:id2", handler);
3. Validate Parameter Types
// For numeric IDs
const id = Number(req.params?.id);
if (isNaN(id)) {
return new Response("Invalid ID", { status: 400 });
}
// For UUIDs
const uuid = req.params?.uuid;
const uuidRegex =
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
if (!uuid || !uuidRegex.test(uuid)) {
return new Response("Invalid UUID", { status: 400 });
}
4. Handle Missing Parameters Gracefully
f.get("/search/:query?", (req: HttpRequest) => {
const { query } = req.params || {};
if (!query) {
return Response.json({
results: [],
message: "No search query provided",
});
}
// Perform search with query
return Response.json({
results: [`Results for: ${query}`],
});
});
5. Consider URL Encoding
f.get("/search/:query", (req: HttpRequest) => {
const { query } = req.params || {};
if (query) {
const decodedQuery = decodeURIComponent(query);
return Response.json({
original: query,
decoded: decodedQuery,
});
}
return new Response("Query required", { status: 400 });
});
URL parameters are a powerful feature for creating dynamic, RESTful APIs. Remember to always validate and sanitize parameter values before using them in your application logic.