Docs Menu
Docs Home
/ /
Desuso de la API de datos y los puntos finales HTTPS

Guía: Implementar una alternativa de Express.js a la API de datos Atlas

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.

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.

Clonar el repositorio e instalar las dependencias:

git clone https://github.com/abhishekmongoDB/data-api-alternative.git
cd data-api-alternative
npm install

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_URI Reemplace 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_OPTIONS Reemplace 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_KEY Reemplazar 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_SECRET Reemplazar con el secreto definido localmente correspondiente.

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-key y x-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.

index.js
/**
* 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}`);
});

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.

rutas/api.js
/**
* 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;

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.

conexión/databaseManager.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;

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.

modelos/dynamicModel.js
// 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;

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

controladores/dbController.js
/**
* 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();

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.

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

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.

Los siguientes ejemplos demuestran cómo insertar uno o varios documentos.

Insertar un nuevo documento de usuario en la colección users:

Inserte uno
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:

Insertar muchos
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" }
]
}'

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:

Encontrar uno
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:

Buscar
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
}'

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:

Actualiza uno
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:

Actualiza muchos
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
}'

Los siguientes ejemplos demuestran cómo eliminar uno o varios documentos.

Eliminar documento con el nombre especificado:

deleteOne
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":

Borrar muchos
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" } }
}'

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:

Agregado
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 } } }
]
}'

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.

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.

Volver

Desuso de la API de datos y los puntos finales HTTPS

En esta página