Перевод материала подготовлен в рамках курса "NoSQL".
Приглашаем также всех желающих на двухдневный интенсив «MongoDB Map-Reduce Framework».
— Темы 1 дня: CRUD-операции; фильтрация по полям;sort,skip,limit; запросы по поддокументам.
— Темы 2 дня: концепция map-reduce; концепцияpipeline; структура и синтаксис агрегации; стадия$match; стадия$group; стадия$lookup.

Многие наши клиенты предоставляют своим командам разработчиков доступ к MongoDB как к сервису. Разработчики могут запросить экземпляр базы данных MongoDB и получить строку для подключения и учетные данные за считаные минуты. Переходя на использование MongoDB Atlas, наши клиенты заинтересованы и в том, чтобы дать своим разработчикам соответствующий уровень обслуживания.
В Atlas есть очень мощный уровень управления для предоставления кластеров. Однако в крупных организациях, где работают тысячи разработчиков, не всегда целесообразно предоставлять такому количеству людей прямой доступ к этому интерфейсу. Цель этой статьи — показать, как можно использовать API-интерфейсы Atlas для предоставления доступа к MongoDB как к сервису, когда MongoDB работает под управлением Atlas.
В частности, мы продемонстрируем способ создания интерфейса, предлагающего разработчикам набор вариантов для создания экземпляра базы данных MongoDB. Чтобы упростить себе задачу, рассмотрим, как предоставить разработчикам набор вариантов памяти и хранилища для настройки кластера. Прочие параметры, например выбор провайдера облачных услуг и региона, мы рассматривать не будем. Мы также расскажем о том, как добавлять метки к кластерам Atlas, так как эта функция не поддерживается в пользовательском интерфейсе Atlas. Для примера мы добавили метку для описания кластера.

Архитектура
Хотя API-интерфейсы Atlas можно вызывать непосредственно из клиентского интерфейса, мы решили использовать трехуровневую архитектуру. Ее преимущества заключаются в следующем:
- возможность ограничивать доступную функциональность по мере необходимости; 
- возможность упростить API-интерфейсы, доступные разработчикам клиентских частей приложений; 
- возможность тонкой настройки защиты конечных точек API. 
- Мы могли бы воспользоваться другими функциями серверной части, например триггерами, интеграцией с Twilio и т. д. 
Конечно же, для размещения среднего уровня мы выбрали Realm.

Реализация
Серверная часть

API Atlas
API-интерфейсы Atlas обернуты в набор функций Realm.

По большей части все они вызывают API Atlas следующим образом (здесь мы взяли для примера getOneCluster):
/*
* Gets information about the requested cluster. If clusterName is empty, all clusters will be fetched.
* See https://docs.atlas.mongodb.com/reference/api/clusters-get-one
*
*/
exports = async function(username, password, projectID, clusterName) 
{
	const arg = {
		scheme: 'https',
		host: 'cloud.mongodb.com',
		path: 'api/atlas/v1.0/groups/' + projectID +'/clusters/' + 
clusterName,
		username: username,
		password: password,
		headers: {'Content-Type': ['application/json'], 
'Accept-Encoding': ['bzip, deflate']},
		digestAuth:true
	};
	// The response body is a BSON.Binary object. Parse it and return.
	response = await context.http.get(arg);
	return EJSON.parse(response.body.text());
};Исходный код каждой функции размещен на GitHub.
API MiniAtlas
Следующий шаг — представление функций как конечных точек, которые может использовать клиентская часть. В качестве альтернативы мы могли бы вызывать функции с помощью Realm Web SDK, но мы решили придерживаться протокола REST; он более знаком нашим веб-разработчикам.
Используя функционал сторонних сервисов, мы разработали следующие 6 конечных точек:
| API | Тип метода | Конечная точка | 
| GET | /getClusters | |
| POST | /getClusters | |
| GET | /getClusterState?clusterName:cn | |
| PATCH | /modifyCluster | |
| POST | /pauseCluster | |
| DELETE | /deleteCluster?clusterName:cn | 

Далее представлен исходный код конечной точки getClusters (примечание: имя пользователя и пароль извлекаются из констант Value и Secret):
/*
* GET getClusters
*
* Query Parameters
*
* None
*
* Response - Currently all values documented at https://docs.atlas.mongodb.com/reference/api/clusters-get-all/
*
*/
exports = async function(payload, response) {
	var results = [];
	const username = context.values.get("username");
	const password = context.values.get("apiKey");
	projectID = context.values.get("projectID");
	// Sending an empty clusterName will return all clusters.
	var clusterName = '';
	response = await context.functions.execute("getOneCluster", username, password, 
	projectID, clusterName);
	results = response.results;
	return results;
};Исходный код каждого веб-хука размещен на GitHub.
При сохранении веб-хука генерируется URL-адрес, который служит конечной точкой для API:

Защита конечных точек API
Только аутентифицированные пользователи могут выполнять функции, представленные конечными точками API. При вызове API необходимо передавать заголовок авторизации с действительным идентификатором пользователя. Конечная точка пропустит этот идентификатор через следующую функцию:
exports = function(payload) {
	const headers = context.request.requestHeaders
	const { Authorization } = headers
	const user_id = Authorization.toString().replace(/^Bearer/, '')
	return user_id
};MongoDB Realm имеет несколько встроенных провайдеров аутентификации, включая доступ для анонимных пользователей, доступ по комбинации электронная почта / пароль, доступ по ключам API, а также аутентификацию OAuth 2.0 через Facebook, Google и Apple ID.
Для этого примера мы решили использовать сервис Google OAuth — прежде всего потому, что он уже интегрирован с провайдером единого входа, который мы используем в нашей компании.

Выбор провайдера не важен. Независимо от того, какие провайдеры включены, будет сгенерирован связанный идентификатор пользователя, который можно использовать для аутентификации при доступе к API.
Клиентская часть

Клиентская часть реализована на JQuery и размещена в Realm.
Аутентификация
С помощью MongoDB Stitch Browser SDK клиент предлагает пользователю войти в учетную запись Google (если вход еще не выполнен) и передает учетные данные пользователя Google в StitchAppClient.
let credential = new stitch.GoogleRedirectCredential();
client.auth.loginWithRedirect(credential);Идентификатор пользователя, который необходимо использовать при отправке запроса API в серверную часть, можно получить из StitchAppClient следующим образом:
let userId = client.auth.authInfo.userId;Затем его можно включить в заголовок при вызове API. Вот пример вызова API createCluster:
export const createCluster = (uid, data) => {
	let url = `${baseURL}/createCluster`
	const params = {
		method: "post",
		headers: {
			"Content-Type": "application/json;charset=utf-8",
			...(uid && { Authorization: uid })
		},
		...(data && { body: JSON.stringify(data) })
	}
	return fetch(url, params)
	.then(handleErrors)
	.then(response => response.json())
	.catch(error => console.log(error) );
};Все вызовы API можно посмотреть в файле webhooks.js.
Полезный совет
Нам принесли большую пользу командные рабочие пространства в Postman. Этот инструмент позволяет совместно разрабатывать серверные API и проверять их работоспособность.

Заключение
Этот прототип создан для демонстрации возможностей, о которых, надеемся, вы уже получили представление! Основа решения есть. Как ее использовать, решаете вы сами.
Узнать подробнее о курсе "NoSQL"
Участвовать в двухдневном интенсиве «MongoDB Map-Reduce Framework»
 
          