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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,6 @@ fastlane/test_output
iOSInjectionProject/
*.DS_Store
*.swiftpm

# DocC preview output
.docc-build/
4 changes: 4 additions & 0 deletions .spi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
version: 1
builder:
configs:
- documentation_targets: [Squeal, SquealPostgresNIO]
240 changes: 240 additions & 0 deletions Examples/HummingbirdBlog/Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions Examples/HummingbirdBlog/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// swift-tools-version: 6.4

import PackageDescription

let package = Package(
name: "HummingbirdBlog",
platforms: [.macOS(.v14)],
dependencies: [
// In your own app: .package(url: "https://github.com/s4cha/Squeal", from: "0.2.0")
.package(path: "../.."),
.package(url: "https://github.com/hummingbird-project/hummingbird", from: "2.25.0"),
.package(url: "https://github.com/vapor/postgres-nio", from: "1.21.0"),
],
targets: [
.executableTarget(
name: "App",
dependencies: [
.product(name: "Squeal", package: "Squeal"),
.product(name: "SquealPostgresNIO", package: "Squeal"),
.product(name: "Hummingbird", package: "hummingbird"),
.product(name: "PostgresNIO", package: "postgres-nio"),
]
),
]
)
56 changes: 56 additions & 0 deletions Examples/HummingbirdBlog/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# HummingbirdBlog

A small JSON API built with [Hummingbird](https://github.com/hummingbird-project/hummingbird),
[PostgresNIO](https://github.com/vapor/postgres-nio) and Squeal.

It shows the pieces you need in a real server:

- `Schema.swift`: the tables, described with `@Table`.
- `BlogRepository.swift`: what the routes need from storage, as a protocol.
- `PostgresBlogRepository.swift`: that protocol implemented with Squeal queries.
- `Routes.swift`: Hummingbird routes that only know about the protocol.
- `main.swift`: a `PostgresClient` running alongside the server.

## Run it

You need PostgreSQL running locally.

```bash
createdb hummingbird_blog
psql hummingbird_blog -f schema.sql
swift run
```

The server listens on `http://127.0.0.1:8080`. To use another database, set
`DATABASE_HOST`, `DATABASE_PORT`, `DATABASE_USERNAME`, `DATABASE_PASSWORD` and
`DATABASE_NAME`.

## Try it

```bash
# Create a user
curl -X POST localhost:8080/api/users \
-H 'Content-Type: application/json' \
-d '{"email": "ada@example.com", "name": "Ada"}'

# Create a post (use the id from the previous response)
curl -X POST localhost:8080/api/posts \
-H 'Content-Type: application/json' \
-d '{"authorId": "<user id>", "title": "Hello, world"}'

# Read the feed
curl localhost:8080/api/posts

# Like a post
curl -X POST localhost:8080/api/posts/<post id>/like

# Delete a post
curl -X DELETE localhost:8080/api/posts/<post id>
```

This package depends on Squeal through a relative path, so it always builds against the
checkout it lives in. In your own app, depend on the released package instead:

```swift
.package(url: "https://github.com/s4cha/Squeal", from: "0.2.0"),
```
11 changes: 11 additions & 0 deletions Examples/HummingbirdBlog/Sources/App/BlogRepository.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Foundation

/// What the routes need from storage. Routes depend on this protocol, not on Postgres,
/// so tests can swap in an in-memory fake.
protocol BlogRepository: Sendable {
func createUser(email: String, name: String) async throws -> User?
func createPost(authorId: UUID, title: String) async throws -> UUID
func feed(limit: Int, offset: Int) async throws -> [Post]
func like(postId: UUID) async throws -> Int?
func deletePost(id: UUID) async throws -> Bool
}
26 changes: 26 additions & 0 deletions Examples/HummingbirdBlog/Sources/App/Models.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Foundation
import Hummingbird

struct User: Codable, ResponseEncodable {
let id: UUID
let email: String
let name: String
}

struct Post: Codable, ResponseEncodable {
let id: UUID
let title: String
let authorName: String
let likes: Int
let createdAt: Date
}

struct CreateUserRequest: Decodable {
let email: String
let name: String
}

struct CreatePostRequest: Decodable {
let authorId: UUID
let title: String
}
Loading
Loading