Back4App

BaaSParse ServerローコードGraphQLリアルタイムMongoDB

BaaSプラットフォーム

Back4App

概要

Back4Appは、Parse Serverをベースとしたローコード開発プラットフォームです。直感的なビジュアルエディターと強力なバックエンド機能を組み合わせ、開発者が迅速にスケーラブルなアプリケーションを構築できる環境を提供します。GraphQLとRESTの両方をサポートし、リアルタイム機能とクラウド関数により、モダンなアプリケーション開発のニーズに応答します。

詳細

Back4Appは、Facebookが開発したParse Serverの継承者として、オープンソースの Parse プラットフォームを基盤に構築されています。MongoDB を使用したデータベースシステムにより、高い柔軟性とスケーラビリティを実現し、スキーマレスなデータ構造で複雑なアプリケーションに対応できます。

特徴的な機能として、ビジュアルデータベースエディター、リアルタイムクエリ(Live Query)、包括的なユーザー管理、プッシュ通知、ファイルストレージなどを提供。また、GraphQLの完全サポートにより、効率的なデータフェッチングとAPI設計が可能です。

メリット・デメリット

メリット

  • ローコード開発: ビジュアルエディターにより、コード記述を最小限に抑えた開発が可能
  • Parse Server基盤: 実績のあるオープンソース技術により、安定性と拡張性を確保
  • GraphQL完全サポート: 効率的なデータフェッチングと柔軟なAPI設計
  • リアルタイム機能: Live Query による即座のデータ同期
  • 充実したドキュメント: 豊富なチュートリアルと実例で学習コストを軽減

デメリット

  • Parse依存: Parse Server の制約やアーキテクチャに縛られる可能性
  • MongoDB限定: データベースエンジンの選択肢が限られる
  • コスト構造: 大規模運用時の料金体系が複雑になる場合がある
  • カスタマイズ制限: 深いカスタマイズには限界がある

参考ページ

コード例

1. 基本的なセットアップとデータ操作

// Parse SDK初期化
Parse.initialize("YOUR_APP_ID", "YOUR_JAVASCRIPT_KEY");
Parse.serverURL = 'https://parseapi.back4app.com/';

// オブジェクトの作成
const GameScore = Parse.Object.extend("GameScore");
const gameScore = new GameScore();

gameScore.set("score", 1337);
gameScore.set("playerName", "山田太郎");
gameScore.set("cheatMode", false);

try {
  const result = await gameScore.save();
  console.log('オブジェクトが保存されました:', result.id);
} catch (error) {
  console.error('保存エラー:', error);
}

2. クエリとデータ取得

// クエリの作成と実行
const GameScore = Parse.Object.extend("GameScore");
const query = new Parse.Query(GameScore);

// 条件指定
query.equalTo("playerName", "山田太郎");
query.greaterThan("score", 1000);
query.limit(10);
query.descending("score");

try {
  const results = await query.find();
  console.log(`${results.length}件の結果が見つかりました`);
  
  results.forEach(gameScore => {
    console.log(`${gameScore.get('playerName')}: ${gameScore.get('score')}`);
  });
} catch (error) {
  console.error('クエリエラー:', error);
}

3. ユーザー認証とアカウント管理

// ユーザー登録
const user = new Parse.User();
user.set("username", "taro_yamada");
user.set("password", "secure-password");
user.set("email", "[email protected]");
user.set("phone", "090-1234-5678");

try {
  const newUser = await user.signUp();
  console.log('ユーザー登録成功:', newUser.id);
} catch (error) {
  console.error('登録エラー:', error.message);
}

// ユーザーログイン
try {
  const user = await Parse.User.logIn("taro_yamada", "secure-password");
  console.log('ログイン成功:', user.get('username'));
} catch (error) {
  console.error('ログインエラー:', error.message);
}

// 現在のユーザー取得
const currentUser = Parse.User.current();
if (currentUser) {
  console.log('現在のユーザー:', currentUser.get('username'));
}

4. リアルタイム機能(Live Query)

// Live Queryの設定
const GameScore = Parse.Object.extend("GameScore");
const query = new Parse.Query(GameScore);
query.greaterThan("score", 1000);

// サブスクリプションの作成
const subscription = await query.subscribe();

// イベントリスナーの設定
subscription.on('open', () => {
  console.log('Live Queryに接続しました');
});

subscription.on('create', (gameScore) => {
  console.log('新しいスコアが追加されました:', gameScore.get('score'));
  // UIの更新
  updateScoreBoard(gameScore);
});

subscription.on('update', (gameScore) => {
  console.log('スコアが更新されました:', gameScore.get('score'));
  // UIの更新
  updateScoreBoard(gameScore);
});

subscription.on('delete', (gameScore) => {
  console.log('スコアが削除されました');
  // UIからの削除
  removeFromScoreBoard(gameScore.id);
});

// サブスクリプションの解除
// subscription.unsubscribe();

5. クラウド関数の定義と実行

// クラウド関数の実行(クライアント側)
const params = { 
  playerName: "山田太郎",
  newScore: 1500 
};

try {
  const result = await Parse.Cloud.run("updateHighScore", params);
  console.log('関数実行結果:', result);
} catch (error) {
  console.error('関数実行エラー:', error);
}

// -------

// クラウド関数の定義(サーバー側 - main.js)
Parse.Cloud.define("updateHighScore", async (request) => {
  const { playerName, newScore } = request.params;
  
  // 現在のハイスコアを取得
  const GameScore = Parse.Object.extend("GameScore");
  const query = new Parse.Query(GameScore);
  query.equalTo("playerName", playerName);
  query.descending("score");
  query.limit(1);
  
  const currentHigh = await query.first();
  
  if (!currentHigh || newScore > currentHigh.get("score")) {
    const gameScore = new GameScore();
    gameScore.set("playerName", playerName);
    gameScore.set("score", newScore);
    gameScore.set("isHighScore", true);
    
    await gameScore.save();
    return { success: true, newHighScore: newScore };
  }
  
  return { success: false, message: "スコアが更新されませんでした" };
});

6. React.jsアプリケーション統合例

import React, { useState, useEffect } from 'react';
import Parse from 'parse';

// Parse初期化
Parse.initialize("YOUR_APP_ID", "YOUR_JAVASCRIPT_KEY");
Parse.serverURL = 'https://parseapi.back4app.com/';

function GameApp() {
  const [user, setUser] = useState(null);
  const [scores, setScores] = useState([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    // 現在のユーザーを確認
    const currentUser = Parse.User.current();
    setUser(currentUser);
    
    // スコア一覧を取得
    if (currentUser) {
      fetchScores();
    }
  }, []);

  const fetchScores = async () => {
    setLoading(true);
    const GameScore = Parse.Object.extend("GameScore");
    const query = new Parse.Query(GameScore);
    query.limit(10);
    query.descending("score");
    
    try {
      const results = await query.find();
      setScores(results);
    } catch (error) {
      console.error('スコア取得エラー:', error);
    } finally {
      setLoading(false);
    }
  };

  const addScore = async (score) => {
    const GameScore = Parse.Object.extend("GameScore");
    const gameScore = new GameScore();
    
    gameScore.set("score", score);
    gameScore.set("playerName", user.get('username'));
    
    try {
      await gameScore.save();
      fetchScores(); // スコア一覧を再取得
    } catch (error) {
      console.error('スコア保存エラー:', error);
    }
  };

  const login = async (username, password) => {
    try {
      const loggedInUser = await Parse.User.logIn(username, password);
      setUser(loggedInUser);
      fetchScores();
    } catch (error) {
      console.error('ログインエラー:', error);
    }
  };

  const logout = async () => {
    try {
      await Parse.User.logOut();
      setUser(null);
      setScores([]);
    } catch (error) {
      console.error('ログアウトエラー:', error);
    }
  };

  return (
    <div>
      {user ? (
        <div>
          <h1>ゲームアプリ - {user.get('username')}</h1>
          <button onClick={logout}>ログアウト</button>
          
          <div>
            <h2>スコア一覧</h2>
            {loading ? (
              <p>読み込み中...</p>
            ) : (
              <ul>
                {scores.map(score => (
                  <li key={score.id}>
                    {score.get('playerName')}: {score.get('score')}点
                  </li>
                ))}
              </ul>
            )}
            <button onClick={() => addScore(Math.floor(Math.random() * 2000))}>
              ランダムスコアを追加
            </button>
          </div>
        </div>
      ) : (
        <LoginForm onLogin={login} />
      )}
    </div>
  );
}