· 8 min read · featured

Getting Started with Hummingbird

Hummingbird is a robust, feature-rich and performance-oriented web framework for Swift.

It’s designed for Swift 6, making it the first web framework to do so. In this guide, you’ll learn how to set up a basic Hummingbird project, run it, and create routes to handle requests.

Setting Up

To get started with Hummingbird, you’ll need to set up a new project based on the starter template. This template includes everything you need to create and deploy a basic Hummingbird app.

To create a new project based on the starter template, you can click “Use this template” on the GitHub page or clone the repository using the following command:

curl -L https://raw.githubusercontent.com/hummingbird-project/template/main/scripts/download.sh | bash -s <project-name>

You can also clone the template repository, and run the configure script:

git clone https://github.com/hummingbird-project/template
cd template
./configure <path to new project>

You now have a new Hummingbird project set up, and are ready to start building your application.

Running the Project

To run your Hummingbird project using Swift Package Manager (SwiftPM), navigate to the root directory of your project and run the following command:

swift run

This will download any dependencies and build your project, before starting the server. You can then navigate to http://localhost:8080 in your browser to see your empty Hummingbird app in action.

When using Xcode, you can double-click on- or otherwise open the Package.swift file. This will open the entire folder as a project. From here, you can build and run your project as normal.

Finally, you can use Visual Studio Code to develop and run apps as well. More on that in Developing with Swift in Visual Studio Code

Project Structure

The Hummingbird project template includes only two files:

App.swift containing your Command Line arguments, and run function. The type is @main annotated, making this is the entrypoint for your app.

From here, the app calls into buildApplication, which is located in Application+build.swift.

Once the app is configured in buildApplication, the server is started by calling Application.runService().

Running services starts Hummingbird and all registered dependencies (Services). This kicks off ServiceLifecycle, which manages your app’s lifecycle. For now, services are not important to understand in detail, but you can learn about them in detail here: Introduction to Swift Service Lifecycle

Configuring the App

The buildApplication function is where you configure your Hummingbird Application. This is where you register your routes, middleware, and services.

In essence, an Application just needs an HTTPResponder. This type receives incoming Requests from Hummingbird, and responds with a Response.

You can create your own HTTPResponder by conforming to that protocol. Most commonly, you’ll be using one of the router types. The default router type is simply called Router.

Routers, Routing and Routes

Routers are objects that take incoming requests route them to the appropriate handler. Handlers are simply functions that take a Request and return a Response. They’re the core of your backend logic.

Each of these route handlers is registered to a RouterPath. When an incoming request is received, the uri and method are matched against the registered paths. If a match is found, the handler is called with that request.

There are two default routers in Hummingbird. The basic Router uses a Trie to organise routes. There’s also a separate HummingbirdRouter module can be opted-into, which contains a result-builder style router called RouterBuilder.

Both are roughly equivalent in performance. While the RouterBuilder is faster in small apps, the regular (trie-based) Router scales much better to bigger apps.

You can read up on the result builder routers here.

Context

Before adding routes, a Router is created:

typealias AppRequestContext = BasicRequestContext
let router = Router(context: AppRequestContext.self)
HummingbirdApp.swift:5

Notice that a “context” is provided here. An instance of this context is created for each request that passes through your Hummingbird server. The default one is BasicRequestContext, but you can customise this to your needs. A context must be a concrete type, and can conform to many protocols. Through this system, you can integrate with various different libraries that need to inject or read properties.

Adding a Route

In the template, the first route has already been created. This is a GET /health route, as indicated in the function signature:

router.get("/health") { _, _ -> HTTPResponse.Status in
    return .ok
}
HummingbirdApp.swift:10

This route is a simple health check that returns a 200 OK status code. You can test this route by navigating to http://localhost:8080/health in your browser. Although it returns a status code, the body is empty, meaning you’ll see an empty page.

A route handler has two input parameters: a Request first, and the Context second. Since the AppRequestContext is set to BasicRequestContext, you’ll find that type in here.

Let’s add a new GET route at the / path. This means that visiting your server at http://localhost:8080 you’ll see the response.

import Hummingbird

@main struct App {
    static func main() async throws {
HummingbirdApp.swift
typealias AppRequestContext = BasicRequestContext
let router = Router(context: AppRequestContext.self)
HummingbirdApp.swift:5
router.get("/health") { _, _ -> HTTPResponse.Status in
    return .ok
}
HummingbirdApp.swift:10
router.get("/") { _, _ -> String in
    return "My app works!"
}
HummingbirdApp.swift:16
struct MyResponse: ResponseCodable {
    let message: String
}

router.get("/message") { _, _ -> MyResponse in
    return MyResponse(message: "Hello, world!")
}
HummingbirdApp.swift:22
let app = Application(router: router)
try await app.runService()
HummingbirdApp.swift:32

Rebuild and re-run your app, using Xcode, swift run or your other preferred method. Note that we’ve changed the return type to String to return a body.

You can return any type that conforms to ResponseGenerator. A full list of these types can be found here.

Now, when you navigate to http://localhost:8080, you’ll see the message “My app works!”.

Responses

While returning these simple types is a nice way to get started, you’ll quickly find that you need to return more complex responses. Hummingbird embraces Codable for this, although any other system can be used as well.

Create a type that conforms to ResponseCodable, and return that from your route handler. This type will be encoded to the response body.

struct MyResponse: ResponseCodable {
    let message: String
}

router.get("/message") { _, _ -> MyResponse in
    return MyResponse(message: "Hello, world!")
}
HummingbirdApp.swift:22

This route will return a JSON response with the message “Hello, world!” encoded within!

From here, just run the app!

let app = Application(router: router)
try await app.runService()
HummingbirdApp.swift:32

Conclusion

In this guide, you’ve learned how to set up a new Hummingbird project, run it, and create routes to handle requests. You also learned how to return simple and complex responses. With this foundation, you’re ready to start building your own Hummingbird applications! For more advanced topics, check out the Hummingbird documentation and our other tutorials! Happy coding!

Related posts

Using Hummingbird's Request Contexts

Learn about request contexts in Hummingbird and how to use them.

How to Build a Proxy Server with Hummingbird

Learn how to leverage the flexibility and performance of Hummingbird to build a proxy server.

Working with UDP in SwiftNIO

Create UDP servers and clients using SwiftNIO and structured concurrency

Using WebSockets in Hummingbird

In this article, you will learn about WebSockets and how to use them with the Hummingbird framework in a straightforward, easy-to-follow manner.

Using OpenAPI Generator with Hummingbird

Learn how to use OpenAPI Generator to create Swift APIs with Hummingbird.

What's new in Hummingbird 2?

Discover Hummingbird 2: a Swift-based HTTP server framework, with modern concurrency and customizable request contexts.

Server-Side Swift Conference logo

ServerSide.swift

The talk recordings are live!
See you next year!

Watch the Videos