Firebase vs Supabase
Googleの包括的モバイルプラットフォームFirebaseと、オープンソースのPostgreSQL代替Supabaseが対決。Backend-as-a-Service選定の際に何を優先すべきか?
リソースベースでステートレス、普遍的なHTTP標準
クエリ言語 — クライアントが必要なデータを正確に指定できる
| カテゴリー | REST API | GraphQL |
|---|---|---|
| パフォーマンス | 8/10 | 8/10 |
| 学習のしやすさ | 9/10 | 6/10 |
| エコシステム | 10/10 | 8/10 |
| コミュニティ | 10/10 | 8/10 |
| 求人市場 | 10/10 | 8/10 |
| 将来性 | 8/10 | 9/10 |
// Swift - REST APIクライアント
import Foundation
enum HTTPMethod: String {
case GET, POST, PUT, DELETE, PATCH
}
struct APIClient {
private let baseURL = URL(string: "https://api.example.com")!
private let session: URLSession
init(session: URLSession = .shared) {
self.session = session
}
func request<T: Decodable>(
path: String,
method: HTTPMethod = .GET,
body: Encodable? = nil
) async throws -> T {
var url = baseURL.appendingPathComponent(path)
var request = URLRequest(url: url)
request.httpMethod = method.rawValue
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer \(AuthManager.shared.token)", forHTTPHeaderField: "Authorization")
if let body {
request.httpBody = try JSONEncoder().encode(body)
}
let (data, response) = try await session.data(for: request)
guard let http = response as? HTTPURLResponse else {
throw APIError.invalidResponse
}
switch http.statusCode {
case 200...299:
return try JSONDecoder().decode(T.self, from: data)
case 401:
throw APIError.unauthorized
case 404:
throw APIError.notFound
default:
throw APIError.serverError(http.statusCode)
}
}
}
// 使用例
let client = APIClient()
let user: User = try await client.request(path: "/users/123")
let posts: [Post] = try await client.request(path: "/users/123/posts")// Swift - GraphQL Apolloクライアント
import Apollo
import Foundation
// GraphQLクエリ定義(コードから生成)
// query GetUserWithPosts($id: ID!) {
// user(id: $id) {
// id
// name
// email
// posts(limit: 5) {
// id
// title
// excerpt
// publishedAt
// }
// }
// }
class GraphQLService {
private lazy var apollo = ApolloClient(url: URL(string: "https://api.example.com/graphql")!)
func fetchUserWithPosts(id: String) async throws -> UserWithPostsQuery.Data.User {
try await withCheckedThrowingContinuation { continuation in
apollo.fetch(query: UserWithPostsQuery(id: id)) { result in
switch result {
case .success(let graphQLResult):
if let errors = graphQLResult.errors {
continuation.resume(throwing: GraphQLError(errors))
} else if let user = graphQLResult.data?.user {
continuation.resume(returning: user)
} else {
continuation.resume(throwing: APIError.notFound)
}
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
func createPost(title: String, content: String) async throws -> CreatePostMutation.Data.CreatePost {
try await withCheckedThrowingContinuation { continuation in
apollo.perform(mutation: CreatePostMutation(title: title, content: content)) { result in
switch result {
case .success(let graphQLResult):
if let post = graphQLResult.data?.createPost {
continuation.resume(returning: post)
} else {
continuation.resume(throwing: APIError.invalidResponse)
}
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
}小〜中規模のAPIやパブリックな連携にはREST — シンプルで汎用的、キャッシュとの相性も良好です。複雑なデータ要件、複数クライアント対応、迅速なプロダクトイテレーションが必要な場合はGraphQLが有力な選択肢です。2025年時点では多くの企業がハイブリッドアプローチを採用しています:安定した用途にはREST、柔軟性が求められるクエリにはGraphQL、という使い分けです。
無料相談を受けるいいえ。両者は異なるユースケースに対応しています。RESTはシンプルで汎用的な選択肢であり続け、GraphQLは複雑なプロダクトニーズに対する強力な代替手段です。