Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions Hummingbird.docc/HummingbirdRouter/RouterBuilderGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
@PageImage(purpose: icon, source: "logo")
}

Building your router using a result builder.
Building your router using a result builder.

## Overview

Expand Down Expand Up @@ -59,7 +59,7 @@ Get("health") { _,_ in

## Route middleware

Routes can be initialised with their own result builder as long as they end with a route ``/HummingbirdRouter/Handle`` function that returns the response. This allows us to apply middleware to individual routes.
Routes can be initialised with their own result builder as long as they end with a route ``/HummingbirdRouter/Handle`` function that returns the response. This allows us to apply middleware to individual routes.

```swift
Post("login") {
Expand All @@ -70,10 +70,10 @@ Post("login") {
}
```

If you are not adding the handler inline you can add the function reference without the ``/HummingbirdRouter/Handle``.
If you are not adding the handler inline you can add the function reference without the ``/HummingbirdRouter/Handle``.

```swift
@Sendable func processLogin(request: Request, context: MyContext) async throws -> Response {
func processLogin(request: Request, context: MyContext) async throws -> Response {
// process login
}
RouterBuilder(context: BasicRouterRequestContext.self) {
Expand Down Expand Up @@ -133,8 +133,8 @@ let router = RouterBuilder(context: BasicRouterRequestContext.self) {

### Differences from trie router

There is one subtle difference between the result builder based `RouterBuilder` and the more traditional trie based `Router` that comes with `Hummingbird` and this is related to how middleware are processed in groups.
There is one subtle difference between the result builder based `RouterBuilder` and the more traditional trie based `Router` that comes with `Hummingbird` and this is related to how middleware are processed in groups.

With the trie based `Router` a request is matched against an endpoint and then only runs the middleware applied to that endpoint.
With the trie based `Router` a request is matched against an endpoint and then only runs the middleware applied to that endpoint.

With the result builder a request is processed by each element of the router result builder until it hits a route that matches its URI and method. If it hits a ``/HummingbirdRouter/RouteGroup`` and this matches the current request uri path component then the request (with matched URI path components dropped) will be processed by the children of the `RouteGroup` including its middleware. The request path matching and middleware processing is done at the same time which means middleware only needs its parent `RouteGroup` paths to be matched for it to run.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ struct TodoController<Repository: TodoRepository> {
}

/// Get todo endpoint
@Sendable func get(request: Request, context: some RequestContext) async throws -> Todo? {
func get(request: Request, context: some RequestContext) async throws -> Todo? {
let id = try context.parameters.require("id", as: UUID.self)
return try await self.repository.get(id: id)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct TodoController<Repository: TodoRepository> {
}

/// Get todo endpoint
@Sendable func get(request: Request, context: some RequestContext) async throws -> Todo? {
func get(request: Request, context: some RequestContext) async throws -> Todo? {
let id = try context.parameters.require("id", as: UUID.self)
return try await self.repository.get(id: id)
}
Expand All @@ -23,7 +23,7 @@ struct TodoController<Repository: TodoRepository> {
let order: Int?
}
/// Create todo endpoint
@Sendable func create(request: Request, context: some RequestContext) async throws -> Todo {
func create(request: Request, context: some RequestContext) async throws -> Todo {
let request = try await request.decode(as: CreateRequest.self, context: context)
return try await self.repository.create(title: request.title, order: request.order, urlPrefix: "http://localhost:8080/todos/")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct TodoController<Repository: TodoRepository> {
}

/// Get todo endpoint
@Sendable func get(request: Request, context: some RequestContext) async throws -> Todo? {
func get(request: Request, context: some RequestContext) async throws -> Todo? {
let id = try context.parameters.require("id", as: UUID.self)
return try await self.repository.get(id: id)
}
Expand All @@ -23,7 +23,7 @@ struct TodoController<Repository: TodoRepository> {
let order: Int?
}
/// Create todo endpoint
@Sendable func create(request: Request, context: some RequestContext) async throws -> EditedResponse<Todo> {
func create(request: Request, context: some RequestContext) async throws -> EditedResponse<Todo> {
let request = try await request.decode(as: CreateRequest.self, context: context)
let todo = try await self.repository.create(title: request.title, order: request.order, urlPrefix: "http://localhost:8080/todos/")
return EditedResponse(status: .created, response: todo)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ struct TodoController<Repository: TodoRepository> {
}

/// Get todo endpoint
@Sendable func get(request: Request, context: some RequestContext) async throws -> Todo? {
func get(request: Request, context: some RequestContext) async throws -> Todo? {
let id = try context.parameters.require("id", as: UUID.self)
return try await self.repository.get(id: id)
}

/// Get list of todos endpoint
@Sendable func list(request: Request, context: some RequestContext) async throws -> [Todo] {
func list(request: Request, context: some RequestContext) async throws -> [Todo] {
return try await self.repository.list()
}

Expand All @@ -29,7 +29,7 @@ struct TodoController<Repository: TodoRepository> {
let order: Int?
}
/// Create todo endpoint
@Sendable func create(request: Request, context: some RequestContext) async throws -> EditedResponse<Todo> {
func create(request: Request, context: some RequestContext) async throws -> EditedResponse<Todo> {
let request = try await request.decode(as: CreateRequest.self, context: context)
let todo = try await self.repository.create(title: request.title, order: request.order, urlPrefix: "http://localhost:8080/todos/")
return EditedResponse(status: .created, response: todo)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ struct TodoController<Repository: TodoRepository> {
let completed: Bool?
}
/// Update todo endpoint
@Sendable func update(request: Request, context: some RequestContext) async throws -> Todo? {
func update(request: Request, context: some RequestContext) async throws -> Todo? {
let id = try context.parameters.require("id", as: UUID.self)
let request = try await request.decode(as: UpdateRequest.self, context: context)
guard let todo = try await self.repository.update(
id: id,
title: request.title,
order: request.order,
id: id,
title: request.title,
order: request.order,
completed: request.completed
) else {
throw HTTPError(.badRequest)
Expand All @@ -35,13 +35,13 @@ struct TodoController<Repository: TodoRepository> {
}

/// Get todo endpoint
@Sendable func get(request: Request, context: some RequestContext) async throws -> Todo? {
func get(request: Request, context: some RequestContext) async throws -> Todo? {
let id = try context.parameters.require("id", as: UUID.self)
return try await self.repository.get(id: id)
}

/// Get list of todos endpoint
@Sendable func list(request: Request, context: some RequestContext) async throws -> [Todo] {
func list(request: Request, context: some RequestContext) async throws -> [Todo] {
return try await self.repository.list()
}

Expand All @@ -50,7 +50,7 @@ struct TodoController<Repository: TodoRepository> {
let order: Int?
}
/// Create todo endpoint
@Sendable func create(request: Request, context: some RequestContext) async throws -> EditedResponse<Todo> {
func create(request: Request, context: some RequestContext) async throws -> EditedResponse<Todo> {
let request = try await request.decode(as: CreateRequest.self, context: context)
let todo = try await self.repository.create(title: request.title, order: request.order, urlPrefix: "http://localhost:8080/todos/")
return EditedResponse(status: .created, response: todo)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ struct TodoController<Repository: TodoRepository> {
}

/// Delete todo endpoint
@Sendable func delete(request: Request, context: some RequestContext) async throws -> HTTPResponse.Status {
func delete(request: Request, context: some RequestContext) async throws -> HTTPResponse.Status {
let id = try context.parameters.require("id", as: UUID.self)
if try await self.repository.delete(id: id) {
return .ok
Expand All @@ -31,13 +31,13 @@ struct TodoController<Repository: TodoRepository> {
let completed: Bool?
}
/// Update todo endpoint
@Sendable func update(request: Request, context: some RequestContext) async throws -> Todo? {
func update(request: Request, context: some RequestContext) async throws -> Todo? {
let id = try context.parameters.require("id", as: UUID.self)
let request = try await request.decode(as: UpdateRequest.self, context: context)
guard let todo = try await self.repository.update(
id: id,
title: request.title,
order: request.order,
id: id,
title: request.title,
order: request.order,
completed: request.completed
) else {
throw HTTPError(.badRequest)
Expand All @@ -46,13 +46,13 @@ struct TodoController<Repository: TodoRepository> {
}

/// Get todo endpoint
@Sendable func get(request: Request, context: some RequestContext) async throws -> Todo? {
func get(request: Request, context: some RequestContext) async throws -> Todo? {
let id = try context.parameters.require("id", as: UUID.self)
return try await self.repository.get(id: id)
}

/// Get list of todos endpoint
@Sendable func list(request: Request, context: some RequestContext) async throws -> [Todo] {
func list(request: Request, context: some RequestContext) async throws -> [Todo] {
return try await self.repository.list()
}

Expand All @@ -61,7 +61,7 @@ struct TodoController<Repository: TodoRepository> {
let order: Int?
}
/// Create todo endpoint
@Sendable func create(request: Request, context: some RequestContext) async throws -> EditedResponse<Todo> {
func create(request: Request, context: some RequestContext) async throws -> EditedResponse<Todo> {
let request = try await request.decode(as: CreateRequest.self, context: context)
let todo = try await self.repository.create(title: request.title, order: request.order, urlPrefix: "http://localhost:8080/todos/")
return EditedResponse(status: .created, response: todo)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ struct TodoController<Repository: TodoRepository> {
}

/// Delete all todos endpoint
@Sendable func deleteAll(request: Request, context: some RequestContext) async throws -> HTTPResponse.Status {
func deleteAll(request: Request, context: some RequestContext) async throws -> HTTPResponse.Status {
try await self.repository.deleteAll()
return .ok
}

/// Delete todo endpoint
@Sendable func delete(request: Request, context: some RequestContext) async throws -> HTTPResponse.Status {
func delete(request: Request, context: some RequestContext) async throws -> HTTPResponse.Status {
let id = try context.parameters.require("id", as: UUID.self)
if try await self.repository.delete(id: id) {
return .ok
Expand All @@ -38,13 +38,13 @@ struct TodoController<Repository: TodoRepository> {
let completed: Bool?
}
/// Update todo endpoint
@Sendable func update(request: Request, context: some RequestContext) async throws -> Todo? {
func update(request: Request, context: some RequestContext) async throws -> Todo? {
let id = try context.parameters.require("id", as: UUID.self)
let request = try await request.decode(as: UpdateRequest.self, context: context)
guard let todo = try await self.repository.update(
id: id,
title: request.title,
order: request.order,
id: id,
title: request.title,
order: request.order,
completed: request.completed
) else {
throw HTTPError(.badRequest)
Expand All @@ -53,13 +53,13 @@ struct TodoController<Repository: TodoRepository> {
}

/// Get todo endpoint
@Sendable func get(request: Request, context: some RequestContext) async throws -> Todo? {
func get(request: Request, context: some RequestContext) async throws -> Todo? {
let id = try context.parameters.require("id", as: UUID.self)
return try await self.repository.get(id: id)
}

/// Get list of todos endpoint
@Sendable func list(request: Request, context: some RequestContext) async throws -> [Todo] {
func list(request: Request, context: some RequestContext) async throws -> [Todo] {
return try await self.repository.list()
}

Expand All @@ -68,7 +68,7 @@ struct TodoController<Repository: TodoRepository> {
let order: Int?
}
/// Create todo endpoint
@Sendable func create(request: Request, context: some RequestContext) async throws -> EditedResponse<Todo> {
func create(request: Request, context: some RequestContext) async throws -> EditedResponse<Todo> {
let request = try await request.decode(as: CreateRequest.self, context: context)
let todo = try await self.repository.create(title: request.title, order: request.order, urlPrefix: "http://localhost:8080/todos/")
return EditedResponse(status: .created, response: todo)
Expand Down