Docs Menu

Docs HomeAtlas App Services

Data API Examples

On this page

  • Create, Read, Update, and Delete (CRUD) Operations
  • Find a Single Document
  • Find Multiple Documents
  • Insert a Single Document
  • Insert Multiple Documents
  • Update a Single Document
  • Update Multiple Documents
  • Replace a Single Document
  • Delete a Single Document
  • Delete Multiple Documents
  • Run an Aggregation Pipeline
  • Specify BSON Types in Requests
  • Binary
  • Date
  • Decimal128
  • Double
  • Int32
  • Int64
  • ObjectId
  • Query Examples
  • Find All Documents
  • Find a Document by ID
  • Find By Date
  • Data Access Permissions
  • Define Permissions for Specific API Keys
  • Define Permissions for a Specific Collection

The following examples demonstrate how to send requests to the Atlas Data API.

The following examples demonstrate how to perform CRUD operations using the Atlas Data API.

curl -s "https://data.mongodb-api.com/app/$CLIENT_APP_ID/endpoint/data/v1/action/findOne" \
-X POST \
-H "apiKey: $API_KEY" \
-H 'Content-Type: application/ejson' \
-H "Accept: application/json" \
-d '{
"dataSource": "mongodb-atlas",
"database": "learn-data-api",
"collection": "tasks",
"filter": {
"text": "Do the dishes"
}
}'
curl -s "https://data.mongodb-api.com/app/$CLIENT_APP_ID/endpoint/data/v1/action/find" \
-X POST \
-H "apiKey: $API_KEY" \
-H 'Content-Type: application/ejson' \
-H "Accept: application/json" \
-d '{
"dataSource": "mongodb-atlas",
"database": "learn-data-api",
"collection": "tasks",
"filter": {
"status": "complete"
},
"sort": { "completedAt": 1 },
"limit": 10
}'
curl -s "https://data.mongodb-api.com/app/$CLIENT_APP_ID/endpoint/data/v1/action/insertOne" \
-X POST \
-H "apiKey: $API_KEY" \
-H 'Content-Type: application/ejson' \
-H "Accept: application/json" \
-d '{
"dataSource": "mongodb-atlas",
"database": "learn-data-api",
"collection": "tasks",
"document": {
"status": "open",
"text": "Do the dishes"
}
}'
curl -s "https://data.mongodb-api.com/app/$CLIENT_APP_ID/endpoint/data/v1/action/insertMany" \
-X POST \
-H "apiKey: $API_KEY" \
-H 'Content-Type: application/ejson' \
-H "Accept: application/json" \
-d '{
"dataSource": "mongodb-atlas",
"database": "learn-data-api",
"collection": "tasks",
"documents": [
{
"status": "open",
"text": "Mop the floor"
},
{
"status": "open",
"text": "Clean the windows"
}
]
}'
curl -s "https://data.mongodb-api.com/app/$CLIENT_APP_ID/endpoint/data/v1/action/updateOne" \
-X POST \
-H "apiKey: $API_KEY" \
-H 'Content-Type: application/ejson' \
-H "Accept: application/json" \
-d '{
"dataSource": "mongodb-atlas",
"database": "learn-data-api",
"collection": "tasks",
"filter": {
"_id": { "$oid": "64224f4d089104f1766116a5" }
},
"update": {
"$set": {
"status": "complete",
"completedAt": { "$date": { "$numberLong": "1680105272788" } }
}
}
}'
curl -s "https://data.mongodb-api.com/app/$CLIENT_APP_ID/endpoint/data/v1/action/updateMany" \
-X POST \
-H "apiKey: $API_KEY" \
-H 'Content-Type: application/ejson' \
-H "Accept: application/json" \
-d '{
"dataSource": "mongodb-atlas",
"database": "learn-data-api",
"collection": "tasks",
"filter": {
"status": "open"
},
"update": {
"$set": {
"status": "complete",
"completedAt": { "$date": { "$numberLong": "1680105287069" } }
}
}
}'
curl -s "https://data.mongodb-api.com/app/$CLIENT_APP_ID/endpoint/data/v1/action/replaceOne" \
-X POST \
-H "apiKey: $API_KEY" \
-H 'Content-Type: application/ejson' \
-H "Accept: application/json" \
-d '{
"dataSource": "mongodb-atlas",
"database": "learn-data-api",
"collection": "tasks",
"filter": {
"text": "Clean the windows"
},
"replacement": {
"status": "open",
"text": "Re-clean the windows"
}
}'
curl -s "https://data.mongodb-api.com/app/$CLIENT_APP_ID/endpoint/data/v1/action/deleteOne" \
-X POST \
-H "apiKey: $API_KEY" \
-H 'Content-Type: application/ejson' \
-H "Accept: application/json" \
-d '{
"dataSource": "mongodb-atlas",
"database": "learn-data-api",
"collection": "tasks",
"filter": {
"_id": { "$oid": "64224f3cd79f54ad342dd9b2" }
}
}'
curl -s "https://data.mongodb-api.com/app/$CLIENT_APP_ID/endpoint/data/v1/action/deleteMany" \
-X POST \
-H "apiKey: $API_KEY" \
-H 'Content-Type: application/ejson' \
-H "Accept: application/json" \
-d '{
"dataSource": "mongodb-atlas",
"database": "learn-data-api",
"collection": "tasks",
"filter": {
"status": "complete"
}
}'
curl -s "https://data.mongodb-api.com/app/$CLIENT_APP_ID/endpoint/data/v1/action/aggregate" \
-X POST \
-H "apiKey: $API_KEY" \
-H 'Content-Type: application/ejson' \
-H "Accept: application/json" \
-d '{
"dataSource": "mongodb-atlas",
"database": "learn-data-api",
"collection": "tasks",
"pipeline": [
{
"$match": { "status": "complete" }
},
{
"$group": {
"_id": "$status",
"count": { "$sum": 1 },
"tasks": { "$push": "$text" }
}
},
{
"$sort": { "count": -1 }
}
]
}'

Data API requests can specify BSON types that don't exist in JSON by instead using the EJSON data format in the request body. You can use EJSON to match BSON types in query filters or write BSON types in insert and update operations.

To specify that a request body uses EJSON, set the Content-Type header:

Content-Type: application/ejson

To specify a binary value, use $binary with the value encoded in Base64 and a BSON subtype encoded as a two-character hexadecimal string:

curl -s "https://data.mongodb-api.com/app/$CLIENT_APP_ID/endpoint/data/v1/action/insertOne" \
-X POST \
-H "apiKey: $API_KEY" \
-H 'Content-Type: application/ejson' \
-H "Accept: application/json" \
-d '{
"dataSource": "<cluster name>",
"database": "<database name>",
"collection": "<collection name>",
"document": { "_id": { "$oid":"645404f4ee8583002fc5a77e" },
"data": {
"$binary": {
"base64": "46d989eaf0bde5258029534bc2dc2089",
"subType": "05"
}
}
}
}'

To specify a date, use a $date object. The value depends on which EJSON format you want to use.

Canonical EJSON

The value is a UNIX timestamp in milliseconds as a 64-bit integer:

curl -s https://data.mongodb-api.com/app/$CLIENT_APP_ID/endpoint/data/v1/action/insertOne \
-X POST \
-H "apiKey: $API_KEY" \
-H 'Content-Type: application/ejson' \
-H "Accept: application/json" \
-d '{
"dataSource": "<cluster name>",
"database": "<database name>",
"collection": "<collection name>",
"document": {
"createdAt": { "$date": { "$numberLong": "1638551310749" } }
}
}'

Relaxed EJSON

The value is an ISO 8601 date string with a time component:

curl -s https://data.mongodb-api.com/app/$CLIENT_APP_ID/endpoint/data/v1/action/insertOne \
-X POST \
-H "apiKey: $API_KEY" \
-H 'Content-Type: application/ejson' \
-H "Accept: application/json" \
-d '{
"dataSource": "<cluster name>",
"database": "<database name>",
"collection": "<collection name>",
"document": {
"createdAt": { "$date": "2021-12-03T17:08:30.749Z" }
}
}'

To specify a 128-bit decimal, use $numberDecimal with the decimal value as a string:

curl -s https://data.mongodb-api.com/app/$CLIENT_APP_ID/endpoint/data/v1/action/insertOne \
-X POST \
-H "apiKey: $API_KEY" \
-H 'Content-Type: application/ejson' \
-H "Accept: application/json" \
-d '{
"dataSource": "<cluster name>",
"database": "<database name>",
"collection": "<collection name>",
"document": {
"accountBalance": { "$numberDecimal": "128452.420523" }
}
}'

To specify a 64-bit signed floating point value (commonly referred to as a "double"), use a canonical $numberDouble with the integer value as a string:

curl -s https://data.mongodb-api.com/app/$CLIENT_APP_ID/endpoint/data/v1/action/insertOne \
-X POST \
-H "apiKey: $API_KEY" \
-H 'Content-Type: application/ejson' \
-H "Accept: application/json" \
-d '{
"dataSource": "<cluster name>",
"database": "<database name>",
"collection": "<collection name>",
"document": {
"temperatureCelsius": { "$numberDouble": "23.847" }
}
}'

Relaxed EJSON

An EJSON value that contains a raw JSON number with a decimal point is automatically cast to a $numberDouble object:

curl -s https://data.mongodb-api.com/app/$CLIENT_APP_ID/endpoint/data/v1/action/insertOne \
-X POST \
-H "apiKey: $API_KEY" \
-H 'Content-Type: application/ejson' \
-H "Accept: application/json" \
-d '{
"dataSource": "<cluster name>",
"database": "<database name>",
"collection": "<collection name>",
"document": {
"temperatureCelsius": 23.847
}
}'

To specify a 32-bit signed integer value, use a canonical $numberInt object with the integer value as a string:

curl -s https://data.mongodb-api.com/app/$CLIENT_APP_ID/endpoint/data/v1/action/insertOne \
-X POST \
-H "apiKey: $API_KEY" \
-H 'Content-Type: application/ejson' \
-H "Accept: application/json" \
-d '{
"dataSource": "<cluster name>",
"database": "<database name>",
"collection": "<collection name>",
"document": {
"coins": { "$numberInt": "2147483647" }
}
}'

Relaxed EJSON

An EJSON value that contains a raw JSON number without a decimal point is automatically cast to a $numberInt object:

curl -s https://data.mongodb-api.com/app/$CLIENT_APP_ID/endpoint/data/v1/action/insertOne \
-X POST \
-H "apiKey: $API_KEY" \
-H 'Content-Type: application/ejson' \
-H "Accept: application/json" \
-d '{
"dataSource": "<cluster name>",
"database": "<database name>",
"collection": "<collection name>",
"document": {
"coins": 2147483647
}
}'

To specify a 64-bit signed integer value, use $numberLong with the integer value as a string:

curl -s https://data.mongodb-api.com/app/$CLIENT_APP_ID/endpoint/data/v1/action/insertOne \
-X POST \
-H "apiKey: $API_KEY" \
-H 'Content-Type: application/ejson' \
-H "Accept: application/json" \
-d '{
"dataSource": "<cluster name>",
"database": "<database name>",
"collection": "<collection name>",
"document": {
"population": { "$numberLong": "8047923148" }
}
}'

To specify an ObjectId value, use $oid with the ID as a byte string:

curl -s https://data.mongodb-api.com/app/$CLIENT_APP_ID/endpoint/data/v1/action/insertOne \
-X POST \
-H "apiKey: $API_KEY" \
-H 'Content-Type: application/ejson' \
-H "Accept: application/json" \
-d '{
"dataSource": "<cluster name>",
"database": "<database name>",
"collection": "<collection name>",
"document": {
"_id": { "$oid": "61f02ea3af3561e283d06b91" }
}
}'

The code snippets in this section demonstrate common patterns you can use in your read and write operations.

To match all documents in a collection, use an empty query object:

curl --request POST \
'https://data.mongodb-api.com/app/<App ID>/endpoint/data/v1/action/find' \
--header 'Content-Type: application/ejson' \
--header 'apiKey: <API Key>' \
--data-raw '{
"dataSource": "<cluster name>",
"database": "<database name>",
"collection": "<collection name>",
"filter": {}
}'

MongoDB stores each document with a unique identifier in the _id field. For most apps, this is a BSON ObjectID value. You can match any ObjectID field, including a document using an EJSON $oid object:

curl --request POST \
'https://data.mongodb-api.com/app/<App ID>/endpoint/data/v1/action/find' \
--header 'Content-Type: application/ejson' \
--header 'apiKey: <API Key>' \
--data-raw '{
"dataSource": "<cluster name>",
"database": "<database name>",
"collection": "<collection name>",
"filter": {
"_id": { "$oid": "630e51b3f4cd7d9e606caab6" }
}
}'

You can match documents on a specific date by matching an EJSON $date object with a field that contains a date value:

curl --request POST \
'https://data.mongodb-api.com/app/<App ID>/endpoint/data/v1/action/find' \
--header 'Content-Type: application/ejson' \
--header 'apiKey: <API Key>' \
--data-raw '{
"dataSource": "<cluster name>",
"database": "<database name>",
"collection": "<collection name>",
"filter": {
"createdAt": { "$date": "2022-08-30T17:52:05.033Z" }
}
}'

You can query a range of dates with $gt, $gte, $lt, and $lte:

curl --request POST \
'https://data.mongodb-api.com/app/<App ID>/endpoint/data/v1/action/find' \
--header 'Content-Type: application/ejson' \
--header 'apiKey: <API Key>' \
--data-raw '{
"dataSource": "<cluster name>",
"database": "<database name>",
"collection": "<collection name>",
"filter": {
"createdAt": {
"$gte": { "$date": "2022-01-01T00:00:00.000Z" },
"$lt": { "$date": "2023-01-01T00:00:00.000Z" }
}
}
}'

You can secure Data API requests using your app's built-in role-based permissions. The examples in this section demonstrate how to set up common permissions schemes. You can mix and match these and more complex schemes to meet your API's needs. For more examples and information on how permissions work, see Role-based Permissions.

You can define multiple roles with different data access permissions and assign specific API keys to each role. For example, you can create a read-only role that allows users to read all data but not insert, delete, or modify data.

You map each role to an API key in the role's apply_when expression.

Each API key corresponds to a separate user account with a unique account ID. You can access the account ID and other data of the user making a request with the %%user expansion.

To associate a role with a single API key, set the apply_when expression to match the API key's account ID:

apply_when - One user per role
{
"database": "<database>",
"collection": "<collection>",
"roles": [
{
"name": "SpecificUser",
"apply_when": {
"%%user.id": "61f9a5e69cd3c0199dc1bb88"
},
"insert": true,
"delete": true,
"read": true,
"write": true
}
],
"filters": []
}

To associate a role with multiple API keys, set the apply_when expression to match an array of API key account IDs:

apply_when - Multiple users per role
{
"database": "<database>",
"collection": "<collection>",
"roles": [
{
"name": "MultipleUsers",
"apply_when": {
"%%user.id": {
"$in": [
"61f9a5e69cd3c0199dc1bb88",
"61f9a5e69cd3c0199dc1bb89"
]
}
},
"insert": true,
"delete": true,
"read": true,
"write": true
}
],
"filters": []
}

You can also use a custom system to determine if a user has a role. For example, you can write a function that looks up a user's roles from an Atlas cluster.

functions/hasRole.js
exports = async function hasRole({
userId, // The user account ID, e.g. "60d0c1bdee9d3c23b677f929"
roleName, // The name of a role the user might have, e.g. "admin"
}) {
// 1. Get a reference to a custom user data collection
const users = context.services.get("mongodb-atlas").db("app").collection("users")
// 2. Query the user's document and make sure it exists
const user = await users.findOne({ userId })
if(!user) {
console.error(`User.id ${userId} not found in custom user data collection`)
return false
}
// 3. Decide if the user has the role we're checking
return user.roles.includes(roleName)
};

And then call that function from the role's apply_when expression with the %function operator:

apply_when - Custom function
{
"database": "<database>",
"collection": "<collection>",
"roles": [
{
"name": "SomeCustomRole",
"apply_when": {
"%%true": {
"%function": {
"name": "hasRole",
"arguments": [{
"userId": "%%user.id",
"roleName": "SomeCustomRole"
}]
}
}
},
"insert": true,
"delete": true,
"read": true,
"write": true
}
],
"filters": []
}

This example uses a custom user data collection, but you can use any external service to determine if a user has a role. The function code and arguments you pass are up to you.

You can define roles that control data access permissions for a specific collection in your cluster. For example, you can create a read-only role that allows users to read all data in a collection but not insert, delete, or modify any data.

To define roles for a specific collection, update the collection's configuration file with the collection-specific role configurations:

/data_sources/<data source>/<database>/<collection>/rules.json
{
"database": "<database>",
"collection": "<collection>",
"roles": [
{
"name": "IsOwner",
"apply_when": { "owner_id": "%%user.id" },
"insert": false,
"delete": false,
"read": true,
"write": true
}
],
"filters": []
}

If no collection roles match, then the request is also evaluated against the data source's default roles. If you don't want to apply default roles, remove any roles and filters from the default rule configuration file.

/data_sources/<data source>/default_rule.json
{
"roles": [],
"filters": []
}
← Authenticate Data API Requests