A partir del 30 de septiembre de 2025, Atlas App Services alcanzó el final de su vida útil y MongoDB ya no lo respalda activamente.
La API de Datos de Atlas permitió a los desarrolladores interactuar directamente con sus clústeres de MongoDB Atlas mediante puntos finales HTTP. Esta guía muestra cómo usar una API personalizada basada en controladores para replicar la funcionalidad de la API de Datos.
La plantilla API está construida con Node.js y Expresary admite la implementación en Vercel. El servicio backend utiliza el controlador Node.js de MongoDB con Mongoose para realizar operaciones CRUD (Crear, Leer, Actualizar y Eliminar) y de agregación en sus datos, y protege el acceso mediante una sencilla autorización basada en clave API.
El código fuente del proyecto está disponible en el siguiente repositorio de GitHub: https://github.com/abhishekmongoDB/data-api-alternative
Importante
No apto para producción
Esta plantilla no está diseñada para usarse en entornos de producción ni de alojamiento de terceros. Su alcance es limitado y solo muestra las funciones básicas. Recomendamos encarecidamente implementar mejoras adicionales para satisfacer sus necesidades específicas y seguir las mejores prácticas de seguridad.
Estructura del proyecto
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
Los archivos principales son:
.env: El archivo de configuración que contiene credenciales, detalles de conexión y configuraciones del proyecto.
index.js: El punto de entrada principal para el servidor Express.
routes/api.js: expone los puntos finales de API asignados a su lógica empresarial correspondiente.
connection/databaseManager.js: administra y almacena en caché las conexiones de la base de datos MongoDB.
models/dynamicModel.js: Modelos Mongoose generados dinámicamente que admiten datos Atlas sin esquema.
controladores/dbController.js: La lógica empresarial para las operaciones CRUD y de agregación definidas.
Estos archivos se describen con más detalle a continuación.
Empezar
Requisitos previos
Un clúster MongoDB implementado o una instancia local con una cadena de conexión válida.
La última versión estable de Node.js y npm (Node Package Manager) instalada.
Configurar el proyecto
Clonar el repositorio e instalar las dependencias:
git clone https://github.com/abhishekmongoDB/data-api-alternative.git cd data-api-alternative npm install
Definir variables de entorno
Crear una .env archivo en el directorio raíz del proyecto y pegue el siguiente código:
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.
Reemplace los siguientes marcadores de posición con sus credenciales:
MONGO_URIReemplace con la cadena de conexión de su implementación. Para obtener más información, consulte Formatos de cadena de conexión.Instancia local:
"mongodb://[<user>:<pw>@]localhost"Clúster Atlas:
"mongodb+srv://[<user>:<pw>@]<cluster>.<projectId>.mongodb.net"
MONGO_OPTIONSReemplace con la cadena de consulta opcional que especifica las opciones específicas de la conexión. Para más información, consulte Opciones de la cadena de conexión.API_KEYReemplazar con una clave definida localmente que se utiliza para autenticar las solicitudes a su servidor proxy. Esta no es una clave API de Atlas.API_SECRETReemplazar con el secreto definido localmente correspondiente.
Inicializar el servidor
El servidor Express se inicializa en el archivo index.js. El servidor escucha en el puerto especificado en el archivo .env.
El servidor incluye middleware para lo siguiente:
Validación de clave API y secreto mediante encabezados
x-api-keyyx-api-secret.Análisis de cuerpos de solicitudes JSON.
Enrutar solicitudes a los métodos del controlador según el punto final de la API.
Limitación de velocidad. Las opciones de límite de velocidad se especifican en el archivo
.env.Registro utilizando la biblioteca
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}`); });
Definir las rutas
El archivo api.js define rutas para operaciones CRUD y de agregación básicas. Las solicitudes HTTP POST se asignan a sus funciones de controlador correspondientes.
/** * 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;
Conéctese a MongoDB
El archivo databaseManager.js gestiona las conexiones a la base de datos MongoDB mediante la biblioteca mongoose. Los detalles de la conexión se almacenan en el archivo .env.
Para obtener una lista exhaustiva de las opciones disponibles, consulte Opciones de conexión en la documentación del controlador 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;
Generar modelos dinámicamente
El archivo de utilidad dynamicModel.js genera modelos Mongoose dinámicamente para la base de datos y la colección especificadas.
También hace lo siguiente:
Define modelos utilizando un esquema flexible que puede aceptar cualquier campo.
Guarda en caché modelos para evitar definiciones redundantes de modelos.
// 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;
Implementar el controlador
El archivo dbController.js implementa la lógica del controlador para gestionar las operaciones CRUD y las agregaciones. Cada método interactúa con MongoDB mediante los modelos Mongoose generados dinámicamente.
Actualmente la API admite las siguientes operaciones:
Inserte uno
Insertar muchos
Encontrar uno
Encuentra muchos
Actualiza uno
Actualiza muchos
deleteOne
Borrar muchos
Agregado
/** * 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();
Solicitudes de API de ejemplo
Las siguientes solicitudes de ejemplo demuestran el uso de la API con el conjunto de datos de ejemplo sample_mflix.users. Si aún no tiene el conjunto de sample_mflix datos disponible en su clúster de Atlas, consulte Cargar datos en Atlas.
Nota
Colección Cartero Disponible
Las API también están disponibles como una colección Postman en el repositorio del proyecto.
Iniciar el servidor
Ejecute el siguiente comando para iniciar el servidor Express. El servidor se recarga automáticamente cada vez que se guardan cambios en un archivo de proyecto.
npm run dev
Enviar solicitudes
Con el servidor en ejecución, puede enviar solicitudes utilizando los siguientes comandos cURL.
Antes de ejecutar estos comandos de ejemplo, asegúrese de actualizar los marcadores de posición <YOUR_API_KEY> y <YOUR_API_SECRET> con sus credenciales.
Insertar documentos
Los siguientes ejemplos demuestran cómo insertar uno o varios documentos.
Insertar un nuevo documento de usuario en la colección 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" } }'
Insertar varios documentos de usuario en la colección 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" } ] }'
Buscar documentos
Los siguientes ejemplos demuestran cómo encontrar uno o varios documentos.
Busque un usuario por nombre y devuelva solo la dirección de correo electrónico:
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 } }'
Busca todos los usuarios cuyas direcciones de correo electrónico terminen en el dominio especificado. Luego, ordena los resultados por nombre y devuelve solo el nombre y el correo electrónico del primer 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
Los siguientes ejemplos demuestran cómo actualizar uno o varios documentos.
Actualizar la dirección de correo electrónico de un usuario con el correo electrónico especificado:
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 }'
Actualice la dirección de correo electrónico de todos los usuarios con el dominio especificado:
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 }'
Borrar
Los siguientes ejemplos demuestran cómo eliminar uno o varios documentos.
Eliminar documento con el nombre especificado:
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" } }'
Eliminar todos los documentos cuyos nombres comiencen con "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" } } }'
Documentos agregados
El siguiente ejemplo demuestra una operación de agregación que agrupa a los usuarios por correo electrónico y cuenta la cantidad de ocurrencias:
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 } } } ] }'
Próximos pasos
Esta guía demostró cómo implementar una API personalizada basada en controladores como alternativa a la obsoleta API de datos Atlas. La aplicación proporcionada tiene un alcance limitado y está pensada únicamente como una plantilla adaptable y ampliable para satisfacer sus necesidades y requisitos específicos.
Recomendamos encarecidamente añadir funcionalidades de seguridad y fiabilidad listas para producción—como autenticación, registro, manejo de errores y validación de entradas—antes de la implementación.
Características adicionales
Las siguientes son características recomendadas para mejorar la aplicación de plantilla:
Registro mejorado: implemente un registro estructurado para una mejor observabilidad y depuración.
Seguimiento de errores: integre con herramientas de seguimiento de errores para monitorear el estado de la API.
Limitación de velocidad mejorada: proteja su API contra abusos con límites de solicitud más sólidos.
Seguridad: Refuerce la API contra vulnerabilidades comunes. Asegúrese de que los datos confidenciales y secretos estén debidamente protegidos antes de la implementación, especialmente si se alojan fuera de las instalaciones.