Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.prism.byescaleira.com/llms.txt

Use this file to discover all available pages before exploring further.

Routing

Prism’s router matches incoming requests to handlers using URL patterns. It supports path parameters, wildcards, route groups, and nested prefixes.

Basic Routes

Register routes with the HTTP method and a URL pattern:
Basic Routes
await server.get("/")          { _ in .text("Home") }
await server.get("/about")     { _ in .text("About") }
await server.post("/contact")  { _ in .json(["sent": true]) }
You can also use the generic route method:
await server.route(.PATCH, "/users/:id") { request in
    .json(["patched": true])
}

Path Parameters

Capture dynamic URL segments with :paramName:
Path Parameters
await server.get("/users/:id") { request in
    let userId = request.parameters["id"]!
    return .json(["user": userId])
}

// Multiple parameters
await server.get("/teams/:teamId/members/:memberId") { request in
    let teamId = request.parameters["teamId"]!
    let memberId = request.parameters["memberId"]!
    return .json(["team": teamId, "member": memberId])
}
Parameters are always strings. Parse them in your handler: Int(request.parameters["id"]!) for integers.

Wildcards

The * wildcard matches any remaining path:
Wildcard Routes
// Matches /files/photo.jpg, /files/docs/report.pdf, etc.
await server.get("/files/*") { request in
    let filePath = request.path.replacingOccurrences(of: "/files/", with: "")
    return .text("Serving: \(filePath)")
}

Route Groups

Group related routes under a shared prefix. Groups can have their own middleware:
Route Groups
await server.group("/api") { api in
    api.get("/status") { _ in .json(["status": "ok"]) }

    // Nested group with auth
    api.group("/admin", middlewares: [authMiddleware]) { admin in
        admin.get("/users")  { _ in .json(["users": []]) }
        admin.post("/users") { _ in .json(["created": true]) }
        admin.delete("/users/:id") { request in
            .text("Deleted \(request.parameters["id"]!)")
        }
    }
}
This produces:
  • GET /api/status β€” no auth required
  • GET /api/admin/users β€” auth required
  • POST /api/admin/users β€” auth required
  • DELETE /api/admin/users/:id β€” auth required

REST Resource Pattern

A common pattern for building RESTful APIs:
REST Resource
await server.group("/api/v1") { api in
    // Products resource
    api.get("/products") { request in
        let page = Int(request.queryParameters["page"] ?? "1") ?? 1
        let products = try db.query(
            "SELECT * FROM products LIMIT 20 OFFSET ?",
            parameters: [.integer((page - 1) * 20)]
        )
        return .json(["products": products.map { $0.columns }, "page": page])
    }

    api.get("/products/:id") { request in
        guard let row = try db.queryFirst(
            "SELECT * FROM products WHERE id = ?",
            parameters: [.text(request.parameters["id"]!)]
        ) else {
            throw PrismAppError.notFound("Product not found")
        }
        return .json(row.columns)
    }

    api.post("/products") { request in
        guard let body = request.body,
              let json = try? JSONSerialization.jsonObject(with: body) as? [String: Any],
              let name = json["name"] as? String else {
            throw PrismAppError.badRequest("Missing product name")
        }
        try db.execute(
            "INSERT INTO products (name) VALUES (?)",
            parameters: [.text(name)]
        )
        return .json(["name": name], status: .created)
    }

    api.delete("/products/:id") { request in
        try db.execute(
            "DELETE FROM products WHERE id = ?",
            parameters: [.text(request.parameters["id"]!)]
        )
        return PrismHTTPResponse(status: .noContent)
    }
}

Route Priority

Routes are matched in registration order. The first match wins:
// This matches first for "/users/me"
await server.get("/users/me") { _ in .json(["user": "current"]) }

// This matches "/users/123", "/users/abc", etc.
await server.get("/users/:id") { request in
    .json(["user": request.parameters["id"]!])
}
Register specific routes before parameterized ones. /users/me should come before /users/:id, otherwise :id will capture β€œme” as a parameter.

Query Parameters

Query parameters are automatically parsed from the URL:
Query Parameters
// GET /search?q=swift&page=2
await server.get("/search") { request in
    let query = request.queryParameters["q"] ?? ""
    let page = Int(request.queryParameters["page"] ?? "1") ?? 1
    return .json(["query": query, "page": page])
}

Request

Learn how to extract data from incoming requests.

Middleware

Add cross-cutting concerns to route groups.