Back to OSS
swift-api-contract
Type-safe API contract definitions with Swift macros
Swift
swift-macroapitype-safe
APIContract
English | 日本語
Swiftマクロを活用した型安全なAPIコントラクト定義ライブラリ。クライアントとサーバー間のAPI定義を共通化し、コンパイル時の型チェックを実現します。
特徴
- 型安全なAPI定義: コンパイル時にエンドポイントの入出力型をチェック
- Swiftマクロ:
@Endpoint、@APIGroupマクロによる宣言的なAPI定義 - 自動コード生成: パスパラメータ、クエリパラメータ、ボディのエンコード処理を自動生成
- グループ化: 関連するエンドポイントを論理的にグループ化
- Async/Await対応: モダンな非同期処理との統合
クイックスタート
import APIContract
// APIグループの定義
@APIGroup(path: "/v1/users", auth: .required)
enum UsersAPI {
// GETエンドポイント(一覧取得)
@Endpoint(.get)
struct List {
@QueryParam var limit: Int?
@QueryParam var offset: Int?
typealias Output = [User]
}
// GETエンドポイント(単一取得)
@Endpoint(.get, path: ":userId")
struct Get {
@PathParam var userId: String
typealias Output = User
}
// POSTエンドポイント(作成)
@Endpoint(.post)
struct Create {
@Body var body: CreateUserRequest
typealias Output = User
}
}
リクエストの実行
// APIExecutorプロトコルを実装したクライアントを使用
let client: APIExecutor = MyAPIClient(baseURL: "https://api.example.com")
// エンドポイントを作成
let endpoint = UsersAPI.Get(userId: "123")
// 実行(型安全なレスポンス)
let user: User = try await endpoint.execute(using: client)
インストール
Swift Package Manager
Package.swift に以下を追加:
dependencies: [
.package(url: "https://github.com/no-problem-dev/swift-api-contract.git", from: "1.0.0")
]
ターゲットに追加:
.target(
name: "YourTarget",
dependencies: [
.product(name: "APIContract", package: "swift-api-contract")
]
)
マクロ一覧
@APIGroup
関連するエンドポイントをグループ化します。
@APIGroup(path: "/v1/users", auth: .required)
enum UsersAPI {
// エンドポイント定義...
}
| パラメータ | 型 | 説明 |
|---|---|---|
path |
String |
グループの基本パス |
auth |
AuthRequirement |
認証要件(.none / .required) |
@Endpoint
エンドポイントを定義します。
@Endpoint(.get, path: ":id")
struct GetUser {
@PathParam var id: String
typealias Output = User
}
| パラメータ | 型 | 説明 |
|---|---|---|
method |
APIMethod |
HTTPメソッド |
path |
String? |
サブパス(オプション) |
@PathParam
パスパラメータをマークします。
@PathParam var userId: String
@QueryParam
クエリパラメータをマークします。カスタムパラメータ名も指定可能。
@QueryParam var limit: Int?
@QueryParam("page_size") var pageSize: Int?
@Body
リクエストボディをマークします。
@Body var body: CreateUserRequest
@APIServices
複数のAPIサービスを一括登録します。
@APIServices
struct AppServices {
let users: UsersService
let posts: PostsService
}
// 生成されるメソッド:
// func registerAll<R: Routes>(_ routes: R) { ... }
// 使用例:
services.registerAll(server.routes)
HTTPメソッド
| メソッド | 用途 |
|---|---|
.get |
リソースの取得 |
.post |
リソースの作成 |
.put |
リソースの完全更新 |
.patch |
リソースの部分更新 |
.delete |
リソースの削除 |
.head |
ヘッダーのみ取得 |
.options |
許可メソッドの確認 |
型サポート
パラメータで使用可能な型
StringInt,Int8,Int16,Int32,Int64UInt,UInt8,UInt16,UInt32,UInt64Double,FloatBoolDate(ISO8601形式で自動変換)RawRepresentableな型(enumなど)- 上記のOptional型
特殊な型
| 型 | 説明 |
|---|---|
EmptyInput |
パラメータなしのエンドポイント用 |
EmptyOutput |
レスポンスボディなしのエンドポイント用 |
APIExecutorの実装(クライアント)
struct MyAPIClient: APIExecutor {
let baseURL: String
let session: URLSession
func execute<E: APIContract>(_ endpoint: E) async throws -> E.Output
where E.Output: Decodable {
let request = try endpoint.urlRequest(baseURL: baseURL)
let (data, _) = try await session.data(for: request)
return try JSONDecoder().decode(E.Output.self, from: data)
}
func execute<E: APIContract>(_ endpoint: E) async throws
where E.Output == EmptyOutput {
let request = try endpoint.urlRequest(baseURL: baseURL)
_ = try await session.data(for: request)
}
}
APIServiceの実装(サーバー)
@APIGroupマクロは対応するServiceプロトコルを自動生成します。
// @APIGroup(path: "/v1/users", auth: .required)
// enum UsersAPI { ... }
// ↓ 自動生成
// protocol UsersAPIService: APIService where Group == UsersAPI { ... }
struct UsersService: UsersAPIService {
func handle(_ input: UsersAPI.List, context: ServiceContext) async throws -> [User] {
// 実装
}
func handle(_ input: UsersAPI.Get, context: ServiceContext) async throws -> User {
// 実装
}
func handle(_ input: UsersAPI.Create, context: ServiceContext) async throws -> User {
// 実装
}
}
依存関係
| パッケージ | 用途 | 必須 |
|---|---|---|
| swift-syntax | マクロ実装 | ✅ |
ドキュメント
詳細なAPIドキュメントは GitHub Pages で確認できます。
ライセンス
MIT License - 詳細は LICENSE を参照してください。