Docs Menu
Docs Home
/ /
Atlas App Services
/

Custom HTTPS Endpoints

On this page

  • Structure of an Endpoint
  • Base URL
  • Endpoint Routes
  • HTTP Methods
  • Create a Custom HTTPS Endpoint
  • Authentication
  • Authorization
  • Write an Endpoint Function
  • Access Request Data
  • Return an HTTPS Response
  • Example
  • Call a Custom Endpoint
  • Choose a Response Data Format
  • Authenticate the Request
  • Authorize the Request

You can define custom HTTPS endpoints to create app-specific API routes or webhooks that integrate with external services. A custom endpoint uses a serverless function that you write to handle incoming requests for a specific URL and HTTP method.

Note

Custom HTTPS Endpoints are not supported in private endpoints.

Endpoints use standard, encrypted HTTPS requests, which means that you don't need to install any database drivers or opinionated libraries to call them. Instead, you send requests like this from any HTTP client:

curl -s "https://data.mongodb-api.com/app/myapp-abcde/endpoint/hello" \
-X POST \
-H "Accept: application/json" \
-H "apiKey: TpqAKQgvhZE4r6AOzpVydJ9a3tB1BLMrgDzLlBLbihKNDzSJWTAHMVbsMoIOpnM6" \
-d '{
"name": "Casey"
}'

An endpoint handles one or more HTTP methods sent to a specific URL.

Endpoints in an app share a base URL. The URL uses your App ID to uniquely point to your app.

Globally deployed apps use the following format:

Endpoint Base URL (Global Apps):
https://data.mongodb-api.com/app/<App ID>/endpoint

Endpoints in a locally deployed app use a base URL specific to the app's deployment region (e.g. us-east-1)

Endpoint Base URL (Local Apps)
https://<Region>.aws.data.mongodb-api.com/app/<App ID>/endpoint

Every HTTPS endpoint has a route that serves as a name for the endpoint. An endpoint's route is arbitrary and specific to your app. However, it appears in the endpoint URL path and so should represent the action the route performs.

Route names must begin with a forward slash (/) and may contain additional forward slashes to indicate a nested path.

An endpoint route
/my/nested/route

You call an endpoint by appending its route to your app's base URL and sending an HTTP request.

An endpoint URL
https://data.mongodb-api.com/app/<App ID>/endpoint/my/nested/route

Each endpoint in your app handles one or more HTTP methods for a given route. For example, you might have a single route that accepts POST requests to create a new resource and a GET request to list existing resources.

You can define multiple custom endpoints that serve the same route but handle different request methods. Alternatively, you can define a single endpoint for the route that handles all methods.

Custom endpoints support the following standard HTTP methods:

You can configure the Data API for your app from the App Services UI or by deploying configuration files with App Services CLI:

  1. Click HTTPS Endpoints in the left navigation menu and then click Add An Endpoint.

  2. Define the endpoint Route. Route names must begin with a forward slash (/) and may contain additional forward slashes to indicate a nested path.

  3. Choose an HTTP method for the endpoint from the dropdown. You can choose either a specific method (e.g. GET or POST) or configure the endpoint to accept any HTTP method (i.e. ANY).

  4. Choose a response type, either JSON or EJSON. You can enable Respond With Result to automatically include the endpoint function's return value as the response body.

  5. Write an endpoint function that handles requests for the endpoint. Alternatively, specify an existing function by name.

  6. For additional security, you can configure request authorization.

  7. Save the Data API configuration.

  8. Configure access permissions to allow requests to securely read and write data.

  9. Save and deploy your app.

  1. Pull the latest version of your app.

    appservices pull --remote="<Your App ID>"
  2. Define a configuration object for the custom endpoint.

    http_endpoints/config.json
    [
    {
    "route": "<Endpoint route name>",
    "http_method": "<HTTP method>",
    "function_name": "<Endpoint function name",
    "validation_method": "<Authorization scheme>",
    "respond_result": <boolean>,
    "fetch_custom_user_data": <boolean>,
    "create_user_on_auth": <boolean>,
    "disabled": <boolean>
    }
    ]
  3. Define rules for one or more collections.

    data_sources/mongodb-atlas/<db>/<collection>/rules.json
    {
    "database": "<Database Name>",
    "collection": "<Collection Name>",
    "roles": [<Role>],
    "filters": [<Filter>]
    }
  4. Deploy your app.

    appservices push

Custom endpoints run in the context of a specific user, which allows your app to enforce rules and validate document schemas for each request.

By default, endpoints use Application Authentication, which requires each request to include credentials for one of your application users, like an API key or JWT. You can also configure other custom authentication schemes to fit your application's needs.

For examples of how to authenticate requests, see Authenticate Data API Requests.

Application authentication requires users to log in with an authentication provider that you have enabled for your App. Requests can either include an access token granted by the authentication provider or the credentials the user would log in with (e.g. their API key or email and password).

User ID authentication runs all requests as a single, pre-selected application user. This is useful if all requests should have the same permissions regardless of who called the endpoint.

To select the user, specify their User Account ID in the endpoint configuration.

functions/config.json
[
{
...,
"run_as_user_id": "628e47baf4c2ac2796fc8a91"
}
]

Script authentication calls a function to determine which application user a request runs as. You can use this to implement custom authentication and authorization schemes.

The function must return an existing application user's Account ID as a string or { "runAsSystem": true } to run the request as a system user that has full access to MongoDB CRUD and Aggregation APIs and is not subject to any rules, roles, or limited permissions.

To define the function, specify the source code in the endpoint configuration.

functions/config.json
[
{
...,
"run_as_user_id_script_source": "exports = () => {return \"628e47baf4c2ac2796fc8a91\"}"
}
]

System authentication configures an endpoint to run as a system user that requires no credentials, has full access to MongoDB CRUD and Aggregation APIs, and is not subject to any rules, roles, or limited permissions.

functions/config.json
[
{
...,
"run_as_system": true
}
]

Tip

New endpoints that you create in the UI use System authentication by default.

An endpoint can require authenticated users to provide additional authorization information in the request. You define the authorization scheme for each custom endpoint by configuring the endpoint function.

Endpoints natively support a set of built-in authorization schemes that use a secret string to prove that the request is authorized. You can also define a custom authorization scheme that you can use together with or instead of the built-in schemes.

To learn how to configure authorization for a specific function, see Define a Function.

Endpoints support the following built-in authorization schemes:

All authenticated users are authorized to call the endpoint. Authenticated requests do not need to include authorization information.

Authenticated users must prove that they are authorized to call the endpoint by including a specific string as the value of the secret query parameter.

You define the string in a secret and reference the secret by name in the endpoint configuration.

http_endpoints/config.json
[
{
...,
"verification_method": "SECRET_AS_QUERY_PARAM",
"secret_name": "secret_verification_string"
}
]

To learn how to include the secret in a request, see Authorize the Request.

Authenticated users must prove that they are authorized to call the endpoint by including an Endpoint-Signature header that contains a hexadecimal-encoded HMAC SHA-256 hash generated from the request body and a secret string.

You define the string in a secret and reference the secret by name in the endpoint configuration.

To learn how to sign your requests, see Authorize the Request.

You can define a custom authorization expression to determine if an incoming authenticated request is allowed to run. The expression is evaluated for each request and must evaluate to true to allow the request. If the expression evaluates to false, the request is not authorized and fails with an error. Requests that fail authorization are not counted toward your App's billed usage.

Authorization expressions can use variables like %%user to authorize based on the calling user's data or %%request to make decisions based on the specifics of each incoming request.

To define a custom authorization scheme, specify the expression in the endpoint function's configuration:

http_endpoints/config.json
[
{
...,
"can_evaluate": {
"%%request.requestHeaders.x-secret-key": "my-secret"
}
}
]

Every custom endpoint is associated with a function that runs whenever the endpoint receives an incoming request. In the function, you can import libraries from npm, connect to a linked MongoDB Atlas cluster, and call other serverless functions.

To define a new function when creating an endpoint in the App Services UI, navigate to the Function section and select + New Function from the dropdown.

Depending on your workflow, you can also define and edit endpoint handler functions:

Endpoint functions always receive two arguments:

  • A Request object that lets you access incoming request headers, query parameters, and body data.

  • A Response object that you use to configure the HTTPS response sent back to the caller.

For a sample function and an example request and response, see Example.

A custom endpoint Request object represents the HTTP request that called the endpoint. You can access the incoming request's headers, query parameters, and body data.

Endpoint Request Object
{
"headers": { "<Header>": ["<Header Value>"] },
"query": { "<Query Parameter>": "<Parameter Value>" },
"body": <BSON.Binary>
}
Field
Description
query

An object where each field maps a URL query parameter to its value. If a key is used multiple times in the query string, only the first occurence is represented in this object. To work with the full query string, use context.request.rawQueryString.

Example

The following query object represents the query string ?regions=na,eu&currency=USD:

{
"regions": "na&eu",
"currency": "USD"
}
headers

An object where each field maps a request header name to an array of one or more values.

Example

{
"Content-Type": ["application/json"],
"Accept": ["application/json"],
"X-CustomHeader": ["some-value", "some-other-value"]
}
body

A BSON.Binary object that contains the request body. If the request did not include a body, this value is undefined.

To access data in the request body, you need to serialize the binary:

// Convert the request body to a JSON string
const serialized = request.body.text();
// Parse the string into a usable object
const body = JSON.parse(serialized);

A custom endpoint Response object lets you configure the HTTPS response sent back to the caller. You can set the status code, customize headers, and include data in the response body.

Method
Description
setStatusCode(code)
- code: number

Set the HTTP response status code.

Example

response.setStatusCode(201);
setBody(body)
- body: string | BSON.Binary

Set the HTTP response body.

If body is a string, the endpoint automatically encodes it as a BSON.Binary.

Example

response.setBody(
"{'message': 'Hello, World!'}"
);
setHeader(name, value)
- name: string
- value: string

Set the HTTP response header specified by name to the value passed in the value argument. This overrides any other values that may have already been assigned to that header.

Example

response.setHeader(
"Content-Type",
"application/json"
);
addHeader(name, value)
- name: string
- value: string

Set the HTTP response header specified by name to the value passed in the value argument. Unlike setHeader, this does not override other values that have already been assigned to the header.

Example

response.addHeader(
"Cache-Control",
"max-age=600"
);
response.addHeader(
"Cache-Control",
"min-fresh=60"
)

Consider an endpoint function that parses the body of an incoming POST request, stores the parsed body in a MongoDB collection, and then responds to the caller:

exports = async function MyCustomEndpoint(request, response) {
try {
// 1. Parse data from the incoming request
if (request.body === undefined) {
throw new Error(`Request body was not defined.`);
}
const body = JSON.parse(request.body.text());
// 2. Handle the request
const { insertedId } = await context.services
.get("mongodb-atlas")
.db("myDb")
.collection("myCollection")
.insertOne({ date: new Date(), requestBody: body });
// 3. Configure the response
response.setStatusCode(201);
// tip: You can also use EJSON.stringify instead of JSON.stringify.
response.setBody(
JSON.stringify({
insertedId,
message: "Successfully saved the request body",
})
);
} catch (error) {
response.setStatusCode(400);
response.setBody(error.message);
}
};

The function receives the following POST request:

curl -s "https://data.mongodb-api.com/app/myapp-abcde/endpoint/custom" \
-X POST \
-H "Accept: application/json" \
-H "apiKey: TpqAKQgvhZE4r6AOzpVydJ9a3tB1BLMrgDzLlBLbihKNDzSJWTAHMVbsMoIOpnM6" \
-d '{
"type": "event",
"date": "2024-01-01T00:00:00.000Z",
"name": "New Year Begins",
"comment": "Happy New Year!"
}'
{
"message": "Successfully saved the request body",
"insertedId": "639a521bbdec9b85ba94014b"
}

After the function verifies that the body of the incoming request is defined, it stores the parsed body as a new document in a collection named myCollection. The resulting output displays the configured response, which includes a custom message and the insertedId.

You can call a custom endpoint from any standard HTTPS client.

curl -s "https://data.mongodb-api.com/app/myapp-abcde/endpoint/hello" \
-X POST \
-H "Accept: application/json" \
-H "apiKey: TpqAKQgvhZE4r6AOzpVydJ9a3tB1BLMrgDzLlBLbihKNDzSJWTAHMVbsMoIOpnM6" \
-d '{
"name": "Casey"
}'

HTTP/1.1 or greater is required when making requests.

A request can include an Accept header to request a specific data format for the response body, either JSON or EJSON. If a request does not include a valid Accept header, the response uses the default data format specified in the endpoint configuration.

curl -s "https://data.mongodb-api.com/app/myapp-abcde/endpoint/hello/latest" \
-X GET \
-H "Accept: application/ejson" \
-H "apiKey: TpqAKQgvhZE4r6AOzpVydJ9a3tB1BLMrgDzLlBLbihKNDzSJWTAHMVbsMoIOpnM6"
{
"greeting": "Hello, Leafie!",
"date": { "$date": { "$numberLong": "1654589430998" } }
}
curl -s "https://data.mongodb-api.com/app/myapp-abcde/endpoint/hello/latest" \
-X GET \
-H "Accept: application/json" \
-H "apiKey: TpqAKQgvhZE4r6AOzpVydJ9a3tB1BLMrgDzLlBLbihKNDzSJWTAHMVbsMoIOpnM6"
{
"greeting": "Hello, Leafie!",
"date": "2022-06-07T08:10:30.998Z"
}

If an endpoint is configured to use Application Authentication then you must include a valid user access token or login credentials with every request.

In general, bearer authentication with an access token has higher throughput and is more secure than credential headers. Use an access token instead of credential headers when possible. The token lets you run multiple requests without re-authenticating the user. It also lets you send requests from a web browser that enforces CORS.

To use an access token, first authenticate the user through an App Services authentication provider. Then, get the access token returned from App Services and include it in the request's Authorization header using a Bearer token scheme. For more information on how to acquire and use an access token, see Bearer Token Authentication.

curl -X GET \
-H 'Authorization: Bearer <AccessToken>' \
-H 'Content-Type: application/json' \
https://data.mongodb-api.com/app/myapp-abcde/endpoint/hello

Alternatively, you can include valid login credentials for the user in the request headers.

curl -s "https://data.mongodb-api.com/app/myapp-abcde/endpoint/hello" \
-X POST \
-H "Accept: application/json" \
-H "email: bob@example" \
-H "password: Pa55w0rd!" \
-d '{ "name": "Bob" }'
curl -s "https://data.mongodb-api.com/app/myapp-abcde/endpoint/hello" \
-X POST \
-H "Accept: application/json" \
-H "apiKey: TpqAKQgvhZE4r6AOzpVydJ9a3tB1BLMrgDzLlBLbihKNDzSJWTAHMVbsMoIOpnM6" \
-d '{ "name": "Alice" }'
curl -s "https://data.mongodb-api.com/app/myapp-abcde/endpoint/hello" \
-X POST \
-H "Accept: application/json" \
-H "jwtTokenString: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0ZXN0LWN1c3RvbS1lbmRwb2ludHMtZWhtenQiLCJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZXhwIjoyMTQ1OTE2ODAwfQ.pIMvnXWrcDvmPzmE33ZPrwkBAFSwy-GxW8sP-qLtYiw" \
-d '{ "name": "Carlos" }'

Important

Don't Use API Keys in User-Facing Clients

If you're authenticating from a browser or another user-facing client application, avoid using an API key to log in. Instead, use another authentication provider that takes user-provided credentials. Never store API keys or other sensitive credentials locally.

Depending on the endpoint configuration, your requests may need to include additional authorization information.

All authenticated users are authorized to call the endpoint. Authenticated requests do not need to include authorization information.

Authenticated users must prove that they are authorized to call the endpoint by including the endpoint's secret string as the value of the secret query parameter.

Example

The following curl request uses secret query parameter validation with the secret string "Super5ecr3tPa55w0rd":

curl -s "https://data.mongodb-api.com/app/myapp-abcde/endpoint/passwordRequired?secret=Super5ecr3tPa55w0rd" \
-X GET \
-H "Accept: application/json" \
-H "apiKey: TpqAKQgvhZE4r6AOzpVydJ9a3tB1BLMrgDzLlBLbihKNDzSJWTAHMVbsMoIOpnM6" \
-d '{ "data": "VGhpcyBpcyBzb21lIGRhdGEgdGhhdCB3YXMgZW5jb2RlZCBhcyBhIEJhc2U2NCBBU0NJSSBzdHJpbmc=" }'

Authenticated users must prove that they are authorized to call the endpoint by including an Endpoint-Signature header that contains a hexadecimal-encoded HMAC SHA-256 hash generated from the request body and the endpoint's secret string.

Endpoint-Signature: sha256=<hex encoded hash>

You could use the following function to generate the payload signature:

/**
* Generate an HMAC request signature.
* @param {string} secret - The secret validation string, e.g. "12345"
* @param {object} body - The endpoint request body e.g. { "message": "MESSAGE" }
* @returns {string} The HMAC SHA-256 request signature in hex format.
*/
exports = function signEndpointRequest(secret, body) {
const payload = EJSON.stringify(body);
return utils.crypto.hmac(payload, secret, "sha256", "hex");
};

Example

The following curl request includes a payload signature header signed with the secret value Super5ecr3tPa55w0rd:

curl -s "https://data.mongodb-api.com/app/myapp-abcde/endpoint/sendMessage" \
-X POST \
-H "Accept: application/json" \
-H "apiKey: TpqAKQgvhZE4r6AOzpVydJ9a3tB1BLMrgDzLlBLbihKNDzSJWTAHMVbsMoIOpnM6" \
-H "Endpoint-Signature: sha256=d4f0537db4e230d7a6028a6f7c3bb1b57c9d16f39176d78697e559ac333e0b36" \
-d '{ "message": "Hello!" }'

Back

Data API Endpoints

Next

Authenticate Data API Requests