9 月 30 日、2025 年現在、Atlas App Services はサポート終了のステータスに達し、 MongoDBによってアクティブにサポートされなくなりました。
Atlas データAPI を使用すると、開発者はHTTPエンドポイントを使用してMongoDB Atlasクラスターを直接操作できます。 このガイドでは、ドライバーベースのカスタムAPIを使用して、 データAPIの機能を複製する方法を説明します。
APIテンプレートはNode.jsと Express で構築されており、Vercel への配置をサポートしています。バックエンドサービスは、 MongooseとMongoDB Node.jsドライバーを使用して、データに対してCRUD (作成、読み取り、更新、削除)と集計操作を実行し、単純なAPIキーベースの認可を使用してアクセスを保護します。
プロジェクトのソースコードは、次の Githubリポジトリで入手できます。 https://github.com/abhishekmongoDB/data-api-alternative
重要
本番環境に対応していない
このテンプレートは、本番環境またはサードパーティのホスティング環境での使用を意図したものではありません。範囲が制限され、基本機能のみが表示されます。特定の要件を満たし、セキュリティのベストプラクティスに従うために、追加の機能強化を実装することを強くお勧めします。
プロジェクト構造
data-api-alternative/ . ├── index.js ├── connection/ │   └── databaseManager.js ├── controllers/ │   └── dbController.js ├── models/ │   └── dynamicModel.js ├── routes/ │   └── api.js ├── utils/ │   └── logging.js ├── .env ├── package.json └── vercel.json 
主なファイルは次のとおりです。
- .env: 認証情報、接続の詳細、プロジェクト設定を保持する構成ファイル。 
- インデックス.js:Expressサーバーのメインエントリ点。 
- route/api.js: 対応するビジネス ロジックにマップされたAPIエンドポイントを公開します。 
- connection/databaseManager.js: MongoDBデータベース接続を管理し、キャッシュします。 
- models/dyntaxModel.js: スキーマレス Atlas データをサポートするMongooseモデルを動的に生成しました。 
- Drivers/dbCluster.js:定義されたCRUDおよび集計操作のビジネス ロジック。 
これらのファイルについては、以下で詳しく説明しています。
はじめる
前提条件
- 有効な接続文字列を持つ配置されたMongoDBクラスターまたはローカルインスタンス。 
- Node.jsとnpm (Node Package Manager)の最新の安定バージョンがインストールされました。 
プロジェクトを設定する
リポジトリをクローンし、依存関係をインストールします。
git clone https://github.com/abhishekmongoDB/data-api-alternative.git cd data-api-alternative npm install 
環境変数を定義する
プロジェクトのルートディレクトリに .envファイルを作成し、次のコードを貼り付けます。
Replace with your deployment credentials MONGO_URI="<MONGO_URI>" MONGO_OPTIONS="<MONGO_OPTIONS>" # Optional Replace with your locally defined secrets for basic proxy auth API_KEY="<API_KEY>" API_SECRET="<API_SECRET>" Project variables PORT=7438 RATE_LIMIT_WINDOW_MS=900000 # 15 minutes in milliseconds RATE_LIMIT_MAX=100 # Maximum requests per window RATE_LIMIT_MESSAGE=Too many requests, please try again later. 
次のプレースホルダーを自分の認証情報に置き換えます。
- MONGO_URIを、配置用の接続文字列に置き換えます。詳細については、「接続文字列の形式」を参照してください。- ローカルインスタンス: - "mongodb://[<user>:<pw>@]localhost"
- Atlas クラスター: - "mongodb+srv://[<user>:<pw>@]<cluster>.<projectId>.mongodb.net"
 
- MONGO_OPTIONSを、接続固有のオプションを指定する任意のクエリ文字列に置き換えます。詳細については、接続文字列オプション を参照してください。
- API_KEYを、 プロキシサーバーへのリクエストを認証するために使用されるローカルに定義されたキーに置き換えます。これは Atlas APIキーではありません。
- API_SECRETを、対応するローカルで定義されたシークレットに置き換えます。
サーバーを初期化
Expressサーバーはindex.jsファイルで初期化されます。サーバーは.envファイルで指定されたポートでリッスンします。
サーバーには次のミドルウェアが含まれています。
- x-api-keyと- x-api-secretヘッダーを使用したAPIキーとシークレットの検証
- JSONリクエストボディを解析します。 
- APIエンドポイントに基づいてコントローラー メソッドへのリクエストをルーティングします。 
- レート制限。レート制限オプションは - .envファイルで指定されます。
- winstonライブラリを使用したログ記録。
/** * This file initializes the Express server, validates locally defined API keys, * applies basic rate limiting, and routes incoming requests to API controller methods. */ const rateLimit = require("express-rate-limit"); const express = require("express"); const apiRoutes = require("./routes/api"); const logger = require("./utils/logging"); require("dotenv").config(); // Load local shared secrets from environment variables const API_KEY = process.env.API_KEY; const API_SECRET = process.env.API_SECRET; const app = express(); // Middleware for rate limiting const limiter = rateLimit({    windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS, 10), // 15 minutes    max: parseInt(process.env.RATE_LIMIT_MAX, 10), // Limit each IP to 100 requests per windowMs    message: { message: process.env.RATE_LIMIT_MESSAGE }, }); // Apply the rate limiter to all requests app.use(limiter); // Middleware for parsing requests app.use(express.json()); // Middleware for basic API key authentication // NOTE: Replace this with your preferred authentication method in production app.use((req, res, next) => {    logger.info({       method: req.method,       url: req.originalUrl,       body: req.body,       headers: req.headers,    });    const apiKey = req.headers["x-api-key"];    const apiSecret = req.headers["x-api-secret"];    if (apiKey === API_KEY && apiSecret === API_SECRET) {       next(); // Authorized    } else {       res.status(403).json({ message: "Forbidden: Invalid API Key or Secret" });    } }); // Middleware for API routing app.use("/api", apiRoutes); // Start the server const PORT = process.env.PORT || 3000; app.listen(PORT, () => {    console.log(`Server is running on port ${PORT}`); }); 
ルートを定義する
api.jsファイルは、基本的なCRUDおよび集計操作のルートを定義します。HTTP POST リクエストは、対応するコントローラー関数にマッピングされます。
 /**  * Defines the routes for all API endpoints and maps them to the corresponding functions in the dbController class.  */  const express = require('express');  const dbController = require('../controllers/dbController');  const router = express.Router();  router.post('/insertOne', dbController.insertOne);  router.post('/insertMany', dbController.insertMany);  router.post('/findOne', dbController.findOne);  router.post('/find', dbController.find);  router.post('/updateOne', dbController.updateOne);  router.post('/deleteOne', dbController.deleteOne);  router.post('/deleteMany', dbController.deleteMany);  router.post('/aggregate', dbController.aggregate);  module.exports = router; 
MongoDB に接続する
databaseManager.jsファイルは、mongoose ライブラリを使用してMongoDBデータベース接続を処理します。接続の詳細は .envファイルに保存されます。
使用可能なオプションの完全なリストについては、 MongoDB Node.jsドライバーのドキュメントの接続オプションを参照してください。
const mongoose = require("mongoose"); require("dotenv").config(); const connections = {}; // Cache for database connections /** * Manages MongoDB database connections. * @param {string} database - The database name. * @returns {mongoose.Connection} - Mongoose connection instance. */ const getDatabaseConnection = (database) => {    const baseURI = process.env.MONGO_URI;    const options = process.env.MONGO_OPTIONS || "";    if (!baseURI) {       throw new Error("MONGO_URI is not defined in .env file");    }    // If connection does not exist, create it    if (!connections[database]) {       connections[database] = mongoose.createConnection(          `${baseURI}/${database}${options}`       );       // Handle connection errors       connections[database].on("error", (err) => {          console.error(`MongoDB connection error for ${database}:`, err);       });       connections[database].once("open", () => {          console.log(`Connected to MongoDB database: ${database}`);       });    }    return connections[database]; }; module.exports = getDatabaseConnection; 
モデルを動的に生成する
dynamicModel.js ユーティリティファイルは、指定されたデータベースとコレクションのMongooseモデルを動的に生成します。
また、次の処理も行います。
- あらゆるフィールドを受け入れることができる柔軟なスキーマを使用してモデルを定義します。 
- 冗長なモデル定義を回避するためにモデルをキャッシュします。 
 // Import the mongoose library for MongoDB object modeling  const mongoose = require("mongoose");  // Import the function to get a database connection  const getDatabaseConnection = require("../connection/databaseManager");  // Initialize an empty object to cache models  // This helps in reusing models and avoiding redundant model creation  const modelsCache = {}; // Cache for models (database.collection -> Model)  /**  * Creates and retrieves a dynamic model for a given database and collection.  * This function ensures that the same model is reused if it has already been created.  *  * @param {string} database - The name of the database.  * @param {string} collection - The name of the collection.  * @returns {mongoose.Model} - The Mongoose model instance for the specified collection.  */  const getModel = (database, collection) => {    // Create a unique key for the model based on the database and collection names    const modelKey = `${database}.${collection}`;    // Check if the model already exists in the cache    // If it does, return the cached model    if (modelsCache[modelKey]) {       return modelsCache[modelKey];    }    // Get the database connection for the specified database    const dbConnection = getDatabaseConnection(database);    // Define a flexible schema with no predefined structure    // This allows the schema to accept any fields    const schema = new mongoose.Schema({}, { strict: false });    // Create the model using the database connection, collection name, and schema    // Cache the model for future use    const model = dbConnection.model(collection, schema, collection);    modelsCache[modelKey] = model;    // Return the newly created model    return model;  };  // Export the getModel function as a module  module.exports = getModel; 
ドライバーを実装する
dbController.jsファイルは、 CRUD操作と集計を処理するためのコントローラー ロジックを実装しています。各メソッドは、動的に生成されたMongooseモデルを使用してMongoDBとやりとりします。
API は現在、次の操作をサポートしています。
- 1 つを挿入 
- 多数挿入 
- 1件を特定 
- findMany 
- 更新 1 
- 多数更新 
- deleteOne 
- deleteMany 
- 集計 
 /**  * Contains the logic for all CRUD and aggregation operations.  * Each method interacts with the database and handles errors gracefully.  */  const getModel = require("../models/dynamicModel");  class DbController {    async insertOne(req, res) {       const { database, collection, document } = req.body;       try {          const Model = getModel(database, collection);          const result = await Model.insertOne(document);          if (!result) {             return res             .status(400)             .json({ success: false, message: "Insertion failed" });          }          res.status(201).json({ success: true, result });       } catch (error) {          res.status(500).json({ success: false, error: error.message });       }    }    async insertMany(req, res) {       const { database, collection, documents } = req.body;       try {          const Model = getModel(database, collection);          const result = await Model.insertMany(documents);          if (!result || result.length === 0) {             return res             .status(400)             .json({ success: false, message: "Insertion failed" });          }          res.status(201).json({ success: true, result });       } catch (error) {          res.status(500).json({ success: false, error: error.message });       }    }    async findOne(req, res) {       const { database, collection, filter, projection } = req.body;       try {          const Model = getModel(database, collection);          const result = await Model.findOne(filter, projection);          if (!result) {             return res             .status(404)             .json({ success: false, message: "No record found" });          }          res.status(200).json({ success: true, result });       } catch (error) {          res.status(500).json({ success: false, error: error.message });       }    }    async find(req, res) {       const { database, collection, filter, projection, sort, limit } = req.body;       try {          const Model = getModel(database, collection);          const result = await Model.find(filter, projection).sort(sort).limit(limit);          if (!result || result.length === 0) {             return res             .status(404)             .json({ success: false, message: "No records found" });          }          res.status(200).json({ success: true, result });       } catch (error) {       r  es.status(500).json({ success: false, error: error.message });       }    }    async updateOne(req, res) {       const { database, collection, filter, update, upsert } = req.body;       try {          const Model = getModel(database, collection);          const result = await Model.updateOne(filter, update, { upsert });          if (result.matchedCount === 0) {             return res             .status(404)             .json({ success: false, message: "No records updated" });          }          res.status(200).json({ success: true, result });       } catch (error) {          res.status(500).json({ success: false, error: error.message });       }    }    async updateMany(req, res) {       const { database, collection, filter, update } = req.body;       try {          const Model = getModel(database, collection);          const result = await Model.updateMany(filter, update);          if (result.matchedCount === 0) {             return res             .status(404)             .json({ success: false, message: "No records updated" });          }          res.status(200).json({ success: true, result });       } catch (error) {          res.status(500).json({ success: false, error: error.message });       }    }    async deleteOne(req, res) {       const { database, collection, filter } = req.body;       try {          const Model = getModel(database, collection);          const result = await Model.deleteOne(filter);          if (result.deletedCount === 0) {             return res             .status(404)             .json({ success: false, message: "No records deleted" });          }          res.status(200).json({ success: true, result });       } catch (error) {          res.status(500).json({ success: false, error: error.message });       }    }    async deleteMany(req, res) {       const { database, collection, filter } = req.body;       try {       const Model = getModel(database, collection);       const result = await Model.deleteMany(filter);       if (result.deletedCount === 0) {             return res             .status(404).json({ success: false, message: "No records deleted" });          }          res.status(200).json({ success: true, result });       } catch (error) {          res.status(500).json({ success: false, error: error.message });       }    }    async aggregate(req, res) {       const { database, collection, pipeline } = req.body;       try {          const Model = getModel(database, collection);          const result = await Model.aggregate(pipeline);          if (!result || result.length === 0) {             return res             .status(404)             .json({ success: false, message: "No aggregation results found" });          }          res.status(200).json({ success: true, result });       } catch (error) {          res.status(500).json({ success: false, error: error.message });       }    }  }  module.exports = new DbController(); 
APIリクエストの例
次の例は、sample_mflix.usersサンプルデータセットでAPIを使用する方法を示しています。sample_mflixAtlas クラスターで使用可能な データセットがまだない場合は、「 Atlas へのデータのロード 」を参照してください。
サーバーを起動する
次のコマンドを実行して、 Expressサーバーを起動します。プロジェクトファイルへの変更を保存するたびに、サーバーは自動的に再読み込みされます。
npm run dev 
リクエストの送信
を実行中サーバーでは、次の cURL コマンドを使用してリクエストを送信できます。
これらの例コマンドを実行中前に、<YOUR_API_KEY> と <YOUR_API_SECRET> のプレースホルダーを認証情報で更新していることを確認してください。
ドキュメントの挿入
次の例は、1 つまたは複数のドキュメントを挿入する方法を示しています。
新しいユーザードキュメントを usersコレクションに挿入します 。
curl -X POST http://localhost:7438/api/insertOne \      -H "Content-Type: application/json" \      -H "x-api-key: <YOUR_API_KEY>" \      -H "x-api-secret: <YOUR_API_SECRET>" \      -d '{             "database": "sample_mflix",             "collection": "users",             "document": {                 "name": "Marcus Bell",                 "email": "marcus.bell@example.com",                 "password": "lucky13" }          }' 
複数のユーザー ドキュメントを usersコレクションに挿入します 。
curl -X POST http://localhost:7438/api/insertMany \      -H "Content-Type: application/json" \      -H "x-api-key: <YOUR_API_KEY>" \      -H "x-api-secret: <YOUR_API_SECRET>" \      -d '{             "database": "sample_mflix",             "collection": "users",             "documents": [                { "name": "Marvin Diaz",                  "email": "marvin.diaz@example.com",                  "password": "123unicorn" },                { "name": "Delores Lambert",                  "email": "delores.lambert@example.com",                  "password": "cats&dogs" },                { "name": "Gregor Ulrich",                  "email": "gregor.ulrich@example.com",                  "password": "securePass123" }             ]          }' 
ドキュメントの検索
次の例は、1 つまたは複数のドキュメントを検索する方法を示しています。
名前でユーザーを検索し、メールアドレスのみを返します。
curl -X POST http://localhost:7438/api/findOne \      -H "Content-Type: application/json" \      -H "x-api-key: <YOUR_API_KEY>" \      -H "x-api-secret: <YOUR_API_SECRET>" \      -d '{             "database": "sample_mflix",             "collection": "users",             "filter": { "name": "Marvin Diaz" },             "projection": { "email": 1, "_id": 0 }          }' 
指定されたドメインで終わるメールアドレスを持つすべてのユーザーを検索します。次に、結果を名前で並べ替え、最初の 10 の名前とメールのみを返します。
curl -X POST http://localhost:7438/api/find \      -H "Content-Type: application/json" \      -H "x-api-key: <YOUR_API_KEY>" \      -H "x-api-secret: <YOUR_API_SECRET>" \      -d '{              "database": "sample_mflix",              "collection": "users",              "filter": { "email": { "$regex": "example\\.com$" } },              "projection": { "name": 1, "email": 1, "_id": 0 },              "sort": { "name": 1 },              "limit": 10          }' 
Update Documents
次の例は、1 つまたは複数のドキュメントの更新を示しています。
指定されたメールメールを持つユーザーのメールアドレスを更新します。
curl -X POST http://localhost:7438/api/updateOne \      -H "Content-Type: application/json" \      -H "x-api-key: <YOUR_API_KEY>" \      -H "x-api-secret: <YOUR_API_SECRET>" \      -d '{              "database": "sample_mflix",              "collection": "users",              "filter": { "email": "marvin.diaz@example.com" },              "update": { "$set": { "password": "456pegasus" } },              "upsert": false          }' 
指定されたドメインを持つすべてのユーザーのメールアドレスを更新します。
curl -X POST http://localhost:7438/api/updateMany \      -H "Content-Type: application/json" \      -H "x-api-key: <YOUR_API_KEY>" \      -H "x-api-secret: <YOUR_API_SECRET>" \      -d '{          "database": "sample_mflix",          "collection": "users",          "filter": { "email": { "$regex": "@example\\.com$" } },          "update": {              "$set": {                  "email": {                      "$replaceAll": {                          "input": "$email",                          "find": "@example.com",                          "replacement": "@example.org"                      }                  }              }          },          "upsert": false          }' 
削除
次の例は、1 つまたは複数のドキュメントの削除を示しています。
指定された名前のドキュメントを削除します。
curl -X POST http://localhost:7438/api/deleteOne \      -H "Content-Type: application/json" \      -H "x-api-key: <YOUR_API_KEY>" \      -H "x-api-secret: <YOUR_API_SECRET>" \      -d '{              "database": "sample_mflix",              "collection": "users",              "filter": { "name": "Delores Lambert" }          }' 
名前が「M」で始まるすべてのドキュメントを削除します。
curl -X POST http://localhost:7438/api/deleteMany \      -H "Content-Type: application/json" \      -H "x-api-key: <YOUR_API_KEY>" \      -H "x-api-secret: <YOUR_API_SECRET>" \      -d '{              "database": "sample_mflix",              "collection": "users",              "filter": { "name": { "$regex": "^M" } }          }' 
ドキュメントの集計
次の例では、ユーザーをメールごとにグループ化し、発生回数をカウントする集計操作を示しています。
curl -X POST http://localhost:7438/api/aggregate \      -H "Content-Type: application/json" \      -H "x-api-key: <YOUR_API_KEY>" \      -H "x-api-secret: <YOUR_API_SECRET>" \      -d '{              "database": "sample_mflix",              "collection": "users",              "pipeline": [              { "$group": { "_id": "$email", "count": { "$sum": 1 } } }              ]          }' 
次のステップ
このガイドでは、廃止された Atlas データAPIの代替としてカスタム ドライバーベースのAPIを実装する方法について説明します。 提供されるアプリは範囲が限定されており、特定のニーズと要件に合わせて調整および拡張できるテンプレートとしてのみ目的とされています。
配置前に、本番環境に対応するセキュリティと信頼性機能(認証、ログ記録、エラー処理、入力検証など)を追加することを強くおすすめします。
追加機能
テンプレートアプリを拡張するために、次の推奨機能を使用します。
- ログの記録: 可視性とデバッグ性を向上させるために構造化ログを実装します。 
- エラー追跡: エラー追跡ツールと統合して、 API の健全性を監視します。 
- レート制限の強化: より強力なリクエスト制限でAPI の不正使用を防ぎます。 
- セキュリティ: 一般的な脆弱性に対してAPIを強化します。特にオンプレミスでホストされている場合は、機密データと秘密が適切に保護されていることを確認してください。