RequestSpec: Organize HTTP requests with SwiftUI-like syntax

Hi folks, I'd like to share RequestSpec with you. It's a lightweight library that provides a declarative API for building HTTP requests, designed to make your networking code more maintainable, organized, and testable. It's built on top of URLRequest and designed to be fully interoperable with existing libraries and codebases such as Alamofire and URLSession.

Getting Started

You can implement and manage a request from a single struct with a clean syntax.

Here's a usage example:

import RequestSpec

struct GetUserRequest: RequestSpec {
    let userId: Int
    let authToken: String?
    let includeFields: [String]

    var body: Get<User> {
        Get("users", "\(userId)")
            .headers {
                ContentType("application/json")
                Accept("application/json")

                if let token = authToken {
                    Authorization("Bearer \(token)")
                    logger.info("Adding Authorization header")
                }
            }
            .queryItems {
                Item("page", value: "1")

                for field in includeFields {
                    Item("fields", value: field)
                }
            }
            .body {
                User(id: userId, name: "John Doe")
            }
    }
}

RequestSpec also provides its own lightweight NetworkService protocol, which can be used to send requests and easily build network services:

protocol UserServiceProtocol: NetworkService {
    func getUser(userId: Int) async throws -> User
}

final class UserService: UserServiceProtocol {
    var baseURL: URL = URL(string: "https://api.example.com")!

    func getUser(userId: Int) async throws -> User {
        let request = GetUserRequest(userId: userId)
        let response = try await send(request)
        return response.body
    }
}

For quick prototyping or one-off requests, you can also use requests directly without defining a RequestSpec struct:


let request = Get<User>("users", "\(userId)")
            .headers {
                Authorization("Bearer \(token)")
                Accept("application/json")
            }

let response = try await service.send(request)
print(response.body.name)

Examples

There are three example projects to help you get started:

Key Features

  • Easy organization - Define your requests in a single struct with a clean syntax
  • Full interoperability - Works seamlessly with URLSession, Alamofire, or any library that accepts URLRequest
  • Zero dependencies - Built entirely on Foundation, no external dependencies required
  • Easy testing - Pure value types that can be inspected, tested, and debugged (includes cURL generation)
  • Built-in networking - Optional NetworkService protocol with async/await support for common use cases

Installing & Source Code

You can find more information in the GitHub repository. Don't forget to give it a star if you find it useful!

Documentation

RequestSpec is lightweight, so you'll find most of the documentation in the readme and on the Swift Package Index page. Additionally, the code is well-documented with examples thanks to Docc. You can also check out the tests folder to see more usage patterns.

Current Status of The Project

I've intentionally kept it lightweight with a focused API surface, making it easy to integrate into existing projects without disrupting your current networking layer. Whether you're building a new API client from scratch or want to gradually adopt a more declarative approach in your existing codebase, RequestSpec can fit your workflow.

I'm actively maintaining the project and welcome feedback, bug reports, and contributions. If you give it a try, I'd love to hear about your experience!