RORK LABEN
TOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しましたTOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しました
記事一覧/開発ツール
開発ツール/2026-04-20上級

Rork Maxの自動バックエンドAPIを本番レベルで使う — データ永続化・認証・外部サービス連携の実践ガイド

Rork Maxの自動バックエンドAPIを本番環境で活用する完全ガイド。データ永続化の設計から認証フロー、外部APIとの連携、エラーハンドリングまで、実際に動くアプリを作るための実践的な知識を解説します。

Rork Max229バックエンドAPIデータ永続化2認証14外部サービス連携

Rork で作ったアプリが「動いてはいるけど、データが消えてしまう」「ログイン機能をつけたいけどどうすればいいか」「外部のAPIと連携したいけど手順がわからない」という段階にある方に向けて書いています。

Rork Max のバックエンドAPIは、適切に使えばモバイルアプリの本番品質に十分対応できます。ただし、その「適切な使い方」を知らないと、スケールしない設計を作ってしまいます。

Rork のバックエンドアーキテクチャを理解する

まず、Rork が自動生成するバックエンドの仕組みを理解する点が肝心です。

Rork のバックエンドは、Cloudflare Workers 上で動く軽量なHTTPサーバーです。各エンドポイントはサーバーレス関数として動作し、データストアは Cloudflare KV(キーバリューストア)または D1(SQLite互換)を使います。

この設計の特徴は、スケーラビリティが高く、デプロイが簡単なことです。一方で、複雑なリレーショナルデータモデルには向いていない制約もあります。

KV vs D1 の使い分け

Cloudflare KV を使うべきケース:
- セッションデータ・キャッシュ
- ユーザー設定・プリファレンス
- TTL(期限)付きデータ
- 単純なキーバリューの参照が多いデータ

Cloudflare D1 を使うべきケース:
- リレーショナルデータ(ユーザーと投稿の関係など)
- 複雑な検索クエリが必要なデータ
- 集計・分析が必要なデータ
- スキーマが安定しているデータ

データ永続化の実装

D1 を使ったユーザーデータの永続化

Rork のプロンプトで「SQLiteデータベースを使ってユーザーデータを永続化して」と指定することで、D1を使ったバックエンドが生成されます。

生成されたコードを理解・改善するために、基本的なパターンを押さえておきましょう。

// rork-generated backend: src/api/users.ts
 
import { D1Database } from "@cloudflare/workers-types";
 
export interface User {
  id: string;
  email: string;
  display_name: string;
  created_at: string;
  updated_at: string;
}
 
export class UserRepository {
  constructor(private db: D1Database) {}
 
  async createUser(email: string, displayName: string): Promise<User> {
    const id = crypto.randomUUID();
    const now = new Date().toISOString();
 
    await this.db.prepare(`
      INSERT INTO users (id, email, display_name, created_at, updated_at)
      VALUES (?, ?, ?, ?, ?)
    `).bind(id, email, displayName, now, now).run();
 
    return { id, email, display_name: displayName, created_at: now, updated_at: now };
  }
 
  async getUserByEmail(email: string): Promise<User | null> {
    const result = await this.db
      .prepare("SELECT * FROM users WHERE email = ?")
      .bind(email)
      .first<User>();
    return result ?? null;
  }
 
  async updateUser(id: string, updates: Partial<Pick<User, "display_name">>): Promise<User | null> {
    const now = new Date().toISOString();
    
    await this.db.prepare(`
      UPDATE users SET display_name = ?, updated_at = ?
      WHERE id = ?
    `).bind(updates.display_name, now, id).run();
 
    return this.db.prepare("SELECT * FROM users WHERE id = ?")
      .bind(id).first<User>() ?? null;
  }
}

Rork で生成されたコードを改善する際の重要ポイント: Rork が生成するSQLは基本的に正しいですが、インデックスが設定されていないことがあります。検索頻度が高いカラム(emailなど)には必ずインデックスを追加してください。

-- マイグレーションファイルに追加
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
CREATE INDEX IF NOT EXISTS idx_posts_user_id ON posts(user_id);
CREATE INDEX IF NOT EXISTS idx_posts_created_at ON posts(created_at DESC);

認証の実装

JWT認証フロー

Rork に「JWTを使ったログイン機能をつけて」と指示すると、基本的なJWT認証が生成されます。ただし、本番環境ではいくつかの改善が必要です。

// src/api/auth.ts — 本番レベルの認証実装
 
import { SignJWT, jwtVerify } from "jose";
 
const JWT_SECRET = new TextEncoder().encode(process.env.JWT_SECRET);
const TOKEN_EXPIRY = "7d";
 
export async function createSession(userId: string, email: string): Promise<string> {
  return new SignJWT({ userId, email })
    .setProtectedHeader({ alg: "HS256" })
    .setIssuedAt()
    .setExpirationTime(TOKEN_EXPIRY)
    .sign(JWT_SECRET);
}
 
export async function verifySession(token: string): Promise<{ userId: string; email: string } | null> {
  try {
    const { payload } = await jwtVerify(token, JWT_SECRET);
    return { userId: payload.userId as string, email: payload.email as string };
  } catch {
    return null;
  }
}
 
// ミドルウェア: 認証が必要なエンドポイントを保護
export function requireAuth(handler: Function) {
  return async (request: Request, env: any) => {
    const authHeader = request.headers.get("Authorization");
    if (\!authHeader?.startsWith("Bearer ")) {
      return new Response(JSON.stringify({ error: "Unauthorized" }), {
        status: 401,
        headers: { "Content-Type": "application/json" }
      });
    }
 
    const token = authHeader.substring(7);
    const session = await verifySession(token);
    
    if (\!session) {
      return new Response(JSON.stringify({ error: "Invalid or expired token" }), {
        status: 401,
        headers: { "Content-Type": "application/json" }
      });
    }
 
    return handler(request, env, session);
  };
}

モバイルアプリ側の認証管理

RorkのSwiftコードでJWTを安全に保存・使用する方法です。

// RorkGeneratedApp/Services/AuthService.swift
 
import Security
import Foundation
 
class AuthService: ObservableObject {
    @Published var isAuthenticated = false
    @Published var currentUser: User?
    
    private let keychainKey = "app.authToken"
    
    // Keychainにトークンを保存(UserDefaultsより安全)
    func saveToken(_ token: String) {
        let data = token.data(using: .utf8)\!
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: keychainKey,
            kSecValueData as String: data,
            kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
        ]
        SecItemDelete(query as CFDictionary)
        SecItemAdd(query as CFDictionary, nil)
    }
    
    func getToken() -> String? {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: keychainKey,
            kSecReturnData as String: true,
            kSecMatchLimit as String: kSecMatchLimitOne
        ]
        var result: AnyObject?
        SecItemCopyMatching(query as CFDictionary, &result)
        guard let data = result as? Data else { return nil }
        return String(data: data, encoding: .utf8)
    }
    
    func logout() {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: keychainKey
        ]
        SecItemDelete(query as CFDictionary)
        DispatchQueue.main.async {
            self.isAuthenticated = false
            self.currentUser = nil
        }
    }
}

外部APIとの連携

Stripe決済の統合

アプリ内課金に Stripe を使う場合の実装パターンです。

// src/api/payments.ts
 
export async function createPaymentIntent(
  amount: number,
  currency: string,
  userId: string,
  env: { STRIPE_SECRET_KEY: string }
): Promise<{ clientSecret: string }> {
  const response = await fetch("https://api.stripe.com/v1/payment_intents", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${env.STRIPE_SECRET_KEY}`,
      "Content-Type": "application/x-www-form-urlencoded",
    },
    body: new URLSearchParams({
      amount: String(amount),
      currency,
      metadata: JSON.stringify({ userId }),
    }),
  });
 
  if (\!response.ok) {
    const error = await response.json();
    throw new Error(`Stripe error: ${error.error.message}`);
  }
 
  const intent = await response.json();
  return { clientSecret: intent.client_secret };
}

重要: Stripe のシークレットキーは Cloudflare Workers の環境変数に設定し、コードに直接書かないでください。Rork のプロジェクト設定から環境変数を追加できます。

エラーハンドリングと信頼性

本番アプリで最も軽視されがちなのがエラーハンドリングです。

// 統一エラーハンドリング
export class AppError extends Error {
  constructor(
    message: string,
    public statusCode: number = 500,
    public code: string = "INTERNAL_ERROR"
  ) {
    super(message);
    this.name = "AppError";
  }
}
 
export function createErrorResponse(error: unknown): Response {
  if (error instanceof AppError) {
    return new Response(
      JSON.stringify({ error: error.message, code: error.code }),
      { status: error.statusCode, headers: { "Content-Type": "application/json" } }
    );
  }
  
  console.error("Unexpected error:", error);
  return new Response(
    JSON.stringify({ error: "Internal server error", code: "INTERNAL_ERROR" }),
    { status: 500, headers: { "Content-Type": "application/json" } }
  );
}

Rork の限界を超えたときの移行戦略

Rork のバックエンドが向いていないケースもあります。月間アクティブユーザーが5万人を超えてきた、複雑なリレーショナルデータが必要になってきた、リアルタイム機能(WebSocket)が必要になった — こういった段階では、段階的な移行を検討します。

移行先としては Supabase(認証・リアルタイム機能が強い)や PlanetScale + Hono(スケーラブルなSQLが必要な場合)が現実的な選択肢です。Rorkで作ったAPIの設計をそのままに、バックエンドだけを差し替えることは技術的に可能です。

最初から「Rorkで作って、スケールしたら移行する」という戦略は現実的です。むしろ最初からオーバーエンジニアリングするより、Rorkで素早く検証してユーザーを獲得してから移行を検討する方が賢明な場合が多いです。

あなたのアプリが解決しようとしている問題とユーザー規模感を踏まえて、今必要な「ちょうどいいバックエンド」を選んでください。

シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

開発ツール2026-07-18
AR で置いたものが翌朝には消えている — ARWorldMap による配置の永続化とリローカライズ設計
Rork Max が生成する AR アプリは、置いた 3D オブジェクトがアプリ再起動で消えます。ARWorldMap の保存タイミング、カスタムアンカーの符号化、リローカライズ待ちの見せ方、成立しない時の逃げ道までを設計として整理しました。
開発ツール2026-07-17
半年ぶりのアップデートで署名が切れていた — Rork アプリの証明書とプロファイルを機械的に見張る
Apple の署名アセットは静かに期限を迎えます。App Store Connect API で証明書とプロファイルの残日数を機械的に棚卸しし、Cloudflare Workers の定期実行で先回りする設計をまとめました。
開発ツール2026-07-17
Rork の App Store 提出が「輸出コンプライアンス」で毎回止まるのを設定1行で終わらせる
Rork と Rork Max のビルドを App Store Connect に上げるたびに出る Missing Compliance 警告。免除の判断基準と、app.json / Info.plist で恒久的に消す設定、消えない場合の原因を整理しました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →