Loading...
Loading...
Expert guidance on Hummingbird 2 web framework. Use when developers mention: (1) Hummingbird, HB, or Hummingbird 2, (2) Swift web server or HTTP server, (3) server-side Swift routing or middleware, (4) building REST APIs in Swift, (5) RequestContext or ChildRequestContext, (6) HummingbirdAuth or authentication middleware, (7) HummingbirdWebSocket, (8) HummingbirdFluent or database integration, (9) ResponseGenerator or EditedResponse.
npx skill4agent add joannis/claude-skills hummingbirdPackage.swiftdependencies: [
.package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.0.0")
].target(
name: "App",
dependencies: [
.product(name: "Hummingbird", package: "hummingbird")
]
)import Hummingbird
@main
struct App {
static func main() async throws {
let router = Router()
router.get("/") { _, _ in
"Hello, World!"
}
router.get("/health") { _, _ -> HTTPResponse.Status in
.ok
}
let app = Application(
router: router,
configuration: .init(address: .hostname("0.0.0.0", port: 8080))
)
try await app.runService()
}
}let router = Router()
// Basic routes
router.get("/users") { request, context in
// Return all users
}
router.post("/users") { request, context in
// Create user
}
router.get("/users/{id}") { request, context in
let id = context.parameters.get("id")!
// Return user by ID
}
router.put("/users/{id}") { request, context in
// Update user
}
router.delete("/users/{id}") { request, context in
// Delete user
}let router = Router()
router.group("api/v1") { api in
api.group("users") { users in
users.get { _, _ in /* list users */ }
users.post { _, _ in /* create user */ }
users.get("{id}") { _, context in /* get user */ }
}
api.group("posts") { posts in
posts.get { _, _ in /* list posts */ }
}
}BasicRequestContext// Using basic context
let router = Router(context: BasicRequestContext.self)
// Custom context for additional properties
struct AppRequestContext: RequestContext {
var coreContext: CoreRequestContextStorage
var requestId: String?
var authenticatedUser: User?
init(source: Source) {
self.coreContext = .init(source: source)
}
}
let router = Router(context: AppRequestContext.self)ChildRequestContextimport HummingbirdAuth
// Parent context with optional authentication
struct AppRequestContext: AuthRequestContext {
var coreContext: CoreRequestContextStorage
var auth: LoginCache
init(source: Source) {
self.coreContext = .init(source: source)
self.auth = .init()
}
}
// Child context guarantees authenticated user
struct AuthenticatedContext: ChildRequestContext {
typealias ParentContext = AppRequestContext
var coreContext: CoreRequestContextStorage
var user: User // Non-optional, guaranteed to exist
init(context: AppRequestContext) throws {
self.coreContext = context.coreContext
self.user = try context.auth.require(User.self)
}
}
// Use in routes
let router = Router(context: AppRequestContext.self)
router.group("api")
.add(middleware: BearerAuthenticator())
.group(context: AuthenticatedContext.self) { protected in
// All routes here have guaranteed user
protected.get("/me") { _, context -> User in
context.user // Non-optional access
}
}references/request-context.mdRequestContextResponseGenerator// Return String
router.get("/hello") { _, _ in
"Hello, World!"
}
// Return HTTP status
router.get("/health") { _, _ -> HTTPResponse.Status in
.ok
}
// Return Codable (auto-encoded to JSON)
router.get("/user") { _, _ -> User in
User(id: 1, name: "Alice")
}
// Return full Response
router.get("/custom") { _, _ -> Response in
Response(
status: .ok,
headers: [.contentType: "text/plain"],
body: .init(byteBuffer: ByteBuffer(string: "Custom response"))
)
}router.get("/users/{id}") { request, context in
let id = context.parameters.get("id")!
return "User ID: \(id)"
}
router.get("/posts/{postId}/comments/{commentId}") { request, context in
let postId = context.parameters.get("postId")!
let commentId = context.parameters.get("commentId")!
return "Post \(postId), Comment \(commentId)"
}router.get("/search") { request, context in
let query = request.uri.queryParameters.get("q") ?? ""
let page = request.uri.queryParameters.get("page").flatMap(Int.init) ?? 1
return "Searching for '\(query)' on page \(page)"
}struct CreateUserRequest: Decodable {
let name: String
let email: String
}
router.post("/users") { request, context in
let input = try await request.decode(as: CreateUserRequest.self, context: context)
// Create user with input.name, input.email
return HTTPResponse.Status.created
}router.get("/protected") { request, context in
guard let auth = request.headers[.authorization] else {
throw HTTPError(.unauthorized)
}
// Process authorization header
return "Authorized"
}ResponseCodablestruct User: ResponseCodable {
let id: Int
let name: String
let email: String
}
router.get("/users/{id}") { request, context -> User in
return User(id: 1, name: "Alice", email: "alice@example.com")
}router.post("/users") { request, context -> EditedResponse<User> in
let user = User(id: 1, name: "Alice", email: "alice@example.com")
return EditedResponse(
status: .created,
headers: [.location: "/users/1"],
response: user
)
}router.get("/users/{id}") { request, context in
guard let user = findUser(id: context.parameters.get("id")!) else {
throw HTTPError(.notFound, message: "User not found")
}
return user
}struct LoggingMiddleware<Context: RequestContext>: RouterMiddleware {
func handle(
_ request: Request,
context: Context,
next: (Request, Context) async throws -> Response
) async throws -> Response {
let start = ContinuousClock.now
let response = try await next(request, context)
let duration = ContinuousClock.now - start
print("\(request.method) \(request.uri.path) - \(response.status) (\(duration))")
return response
}
}
// Apply to router
router.middlewares.add(LoggingMiddleware())
// Apply to route group
router.group("api") { api in
api.middlewares.add(AuthMiddleware())
api.get("/protected") { _, _ in "Secret data" }
}import Hummingbird
// CORS
router.middlewares.add(CORSMiddleware(
allowOrigin: .originBased,
allowHeaders: [.contentType, .authorization],
allowMethods: [.get, .post, .put, .delete]
))
// File serving
router.middlewares.add(FileMiddleware(rootFolder: "public"))
// Metrics (with swift-metrics)
router.middlewares.add(MetricsMiddleware())
// Tracing (with swift-distributed-tracing)
router.middlewares.add(TracingMiddleware())let app = Application(
router: router,
configuration: .init(
address: .hostname("0.0.0.0", port: 8080),
serverName: "MyApp"
)
)
try await app.runService()import Hummingbird
import ServiceLifecycle
@main
struct App {
static func main() async throws {
let router = Router()
// ... configure routes
let app = Application(
router: router,
configuration: .init(address: .hostname("0.0.0.0", port: 8080))
)
let serviceGroup = ServiceGroup(
services: [app],
gracefulShutdownSignals: [.sigterm, .sigint],
logger: Logger(label: "app")
)
try await serviceGroup.run()
}
}.product(name: "HummingbirdAuth", package: "hummingbird-auth")import HummingbirdAuth
// Context with authentication
struct AppRequestContext: AuthRequestContext {
var coreContext: CoreRequestContextStorage
var auth: LoginCache
init(source: Source) {
self.coreContext = .init(source: source)
self.auth = .init()
}
}
// Bearer token authentication
router.group("api")
.add(middleware: BearerAuthenticator())
.get("/me") { request, context -> User in
let user = try context.auth.require(User.self)
return user
}.product(name: "HummingbirdWebSocket", package: "hummingbird-websocket")import HummingbirdWebSocket
router.ws("/chat") { inbound, outbound, context in
for try await message in inbound {
switch message {
case .text(let text):
try await outbound.write(.text("Echo: \(text)"))
case .binary(let data):
try await outbound.write(.binary(data))
}
}
}.product(name: "HummingbirdFluent", package: "hummingbird-fluent")import HummingbirdFluent
import FluentPostgresDriver
// Add Fluent to application
let fluent = Fluent(logger: logger)
fluent.databases.use(.postgres(configuration: postgresConfig), as: .psql)
let app = Application(
router: router,
configuration: .init(address: .hostname("0.0.0.0", port: 8080))
)
app.addServices(fluent).product(name: "HummingbirdHTTP2", package: "hummingbird")
.product(name: "HummingbirdTLS", package: "hummingbird")import HummingbirdTesting
import Testing
@Test func testHealthEndpoint() async throws {
let router = Router()
router.get("/health") { _, _ -> HTTPResponse.Status in .ok }
let app = Application(router: router)
try await app.test(.router) { client in
try await client.execute(uri: "/health", method: .get) { response in
#expect(response.status == .ok)
}
}
}
@Test func testCreateUser() async throws {
let app = buildApplication()
try await app.test(.router) { client in
let user = CreateUserRequest(name: "Alice", email: "alice@example.com")
try await client.execute(
uri: "/users",
method: .post,
headers: [.contentType: "application/json"],
body: JSONEncoder().encodeAsByteBuffer(user, allocator: .init())
) { response in
#expect(response.status == .created)
}
}
}struct AppRequestContext: AuthRequestContext {
var coreContext: CoreRequestContextStorage
var auth: LoginCache
init(source: Source) {
self.coreContext = .init(source: source)
self.auth = .init()
}
}// UserRoutes.swift
func addUserRoutes(to router: Router<AppRequestContext>) {
router.group("users") { users in
users.get { _, _ in /* list */ }
users.post { _, _ in /* create */ }
users.get("{id}") { _, _ in /* get */ }
}
}
// Application setup
let router = Router(context: AppRequestContext.self)
addUserRoutes(to: router)
addPostRoutes(to: router)router.get("/users/{id}") { request, context in
guard let id = context.parameters.get("id"),
let user = try await userService.find(id: id) else {
throw HTTPError(.notFound, message: "User not found")
}
return user
}references/request-context.mdreferences/routing.mdreferences/middleware.mdreferences/testing.md