Creating Users and Teams with the Compose API

Published

Do you want to add other users or create teams on your Compose account quickly without using the console? We'll show you how using the powerful Compose API and some NodeJS.

This summer, we made a few updates to the Compose API that give you more control over who has access to your deployment. One update is an extension of the accounts endpoint that enables you to get lists of users, add them, and delete them from your account. In addition, to that we've added a teams endpoint that lets you create teams, assign users to them, as well as update, delete, list, and get information about teams.

Previously, we showed you how simple it is to use the Compose API and NodeJS to get deployment backups and create on-demand backups. In this article, we'll continue along the same vein by using NodeJS and the API to create users and teams for your account. If you want to follow along, you can get the code from the Github repository here and follow along.

Let's start setting up the code ...

Setting up the code

First, you'll need the npm package manager and NodeJS installed on your system. If you're on macOS and have homebrew installed, just run brew install node in the terminal. This will install NodeJS and npm for you. To initialize your project using npm, run npm init from the terminal, which will create a package.json file where your NodeJS packages will be installed. After that, create a JavaScript file and put the following code at the top.

#!/usr/bin/env node
const yargs = require('yargs');  
const fetch = require('node-fetch');

let apibase = 'https://api.compose.io/2016-07';  
let apitoken = process.env.COMPOSEAPITOKEN;  

let apiheaders = {  
    "Authorization": "Bearer " + apitoken,
    "Content-Type": "application/json"
};

This is just the preliminary set up. We'll be using yargs to set up a command-line interface later and use node-fetch for making the HTTP requests to the Compose API. The base URI for the API is stored as apibase, which is needed for all API endpoints. But to access the API, you'll have to create an API token for your account first. Get that from the Compose console Account view. Once you've got the API token, then save the token as an environment variable using process.env and a name like COMPOSEAPITOKEN. That way you don't expose your token to the outside world. The apiheaders object contains the headers that need to be available for HTTP request. They include the API token and application/json so we get JSON results.

Getting the account information first

Once that code is in your NodeJS file, we can start the process of getting users. We'll first need to get the Compose account id for that. We need that id first before we create users since the HTTP request endpoints to create, list, and delete users requires it. To get your account id, we'll make an HTTP request to GET 2016/07/accounts using the following function:

let userAccounts = () => {  
    fetch(`${apibase}/accounts/`, { headers: apiheaders })
    .then(res => {
        return res.json();
    })
    .then(json => {
        let accounts = json["_embedded"].accounts;
        for (let account of accounts) {
            console.log(`Account id: ${account.id}`)
        }
    })
    .catch(err => {
        console.log(err);
    });
}

In this function, we use fetch provided by node-fetch. This function uses promises and takes care of decoding responses and converting strings to utf-8 for us. With fetch, we make a request using the accounts GET endpoint with the base API URI (apibase) and the HTTP header fields (apiheaders). fetch returns a promise that we'll return as JSON in the first then() function. Once the JSON response is returned, it will be passed to the second then() function where we'll log the account data. This JSON object returned from the accounts API call is wrapped in a _embedded element. Using that element, we'll drill down to the accounts array and print out the account id belonging to all the accounts you're a member of.

Running this function we'll get something like:

Account id: 7g89sc0dce45vt3000260044  

Now, with the account id, we can start listing, creating, and deleting users.

Getting all the users

The first step we want to do is list all the users on an account. The API call for this step will include the account id we just got. To do that, we'll write another function that will list all the users of this account getting their names and ids. Inside the function, we'll make an HTTP request to the GET /2016-07/accounts/:id/users endpoint which needs the account id. The structure of the function looks pretty similar to the last one.

let listUsers = (accountid) => {  
    fetch(`${apibase}/accounts/${accountid}/users`, { headers: apiheaders })
    .then(res => {
        return res.json();
    })
    .then(json => {
        let users = json["_embedded"].users;
        for (let user of users) {
            console.log(`${user.id} ${user.name}`);
        }
    })
    .catch(err => {
        console.log(err);
    });
};

The first change that we made to the function is passing in the accountid as an argument since we need that for the API call. Now, when the JSON object is returned from the promise, we'll drill down into the _embedded key and get the users that were fetched. Using a loop get to the array of users, we'll log the user id and name of each user associated with the account. That will give us something like:

7g89sc0dce45vt3000260045 Abdullah Alger  

Creating users

You might be the only one with access to your Compose account right now. To change that, let's show you how to add others. The function we'll write to add a user will send a request to the POST 2016-07/accounts/:id/users endpoint that uses the account id. With that request, we'll send the name, email, and phone number of each user we create in the request's body, but the only required parameters are the user's name and email.

let createUser = (accountid, name, email, phone) => {  
    let userInfo = JSON.stringify({"name": name, "email": email, "phone": phone});
    fetch(`${apibase}/accounts/${accountid}/users`, { headers: apiheaders, method: 'POST', body: userInfo })
    .then(res => {
        return res.json();
    })
    .then(json => {
        console.log(`Added user: ${json.id} ${json.name}`);
    })
    .catch(err => {
        console.log(err);
    });
}

Again, we'll pass into the function the account id, but add a name, email, and phone number as arguments. The name, email, and phone number will be stored as a JSON string and passed in the body field alongside the headers. Since this is a POST request, we'll need to specify that as method: 'POST'. Once we run the function, the promise from the JSON response will then log the new user id and the name we added like:

Added user: 5089sc0rce45vt3000760333 Arthur Pendragon  

Now, listing the users of the account with listUsers() and the user account, will give us something like:

5089sc0rce45vt3000760333 Arthur Pendragon  
7g89sc0dce45vt3000260045 Abdullah Alger  

Remember, when creating new users for your account, each user will receive an email asking them to activate their account and set up 2FA authentication. It's important that each user set up 2FA authentication for everyone's sake.

Removing users

Removing users is just as easy as creating them if they no longer need access to the database. Setting this up straightforward; we'll just add the account id and the user id of the user you want to delete to the DELETE /2016-07/accounts/:id/users/:user_id endpoint.

let removeUser = (accountid, userid) => {  
    fetch(`${apibase}/accounts/${accountid}/users/${userid}`, { headers: apiheaders, method: 'DELETE'})
    .then(res => {
        return res.json();
    })
    .then(json => {
        console.log(`Removed user: ${json.id} ${json.name}`);
    })
    .catch(err => {
        console.log(err);
    });
}

In the function, we pass in the account id and the user id of the user we're removing, and change the method field in fetch to DELETE. The JSON response will contain the user that was deleted, and we'll log the user id and the name. Removing Arthur Pendragon from the account will look like:

Removed user: 5089sc0rce45vt3000760333 Arthur Pendragon  

It's as simple as that. Let's move on to putting the users in teams.

Creating teams

Teams allow the account owner or admin to group together users to give them certain roles as a group. Assuming you don't already have any teams created, let's start by showing you how to create a team. The function we'll write to create a team looks similar the ones we've written above. To create a team, we need to make an HTTP request to the POST /2016-07/teams endpoint and add the name of the team to the request body.

let createTeam = (teamname) => {  
    let team = JSON.stringify({"team": {"name": teamname}});
    fetch(`${apibase}/teams`, { headers: apiheaders, method: 'POST', body: team })
    .then(res => {
        return res.json();
    })
    .then(json => {
        console.log(`Team Created: ${json.id} ${json.name}`)
    })
    .catch(err => {
        console.log(err);
    });
}

For this function, we pass in a teamname as the name of the team that we'll create. The team name will be stored in a JSON string like we did when creating a user. Using the team endpoint, set the method field to POST and the body to the team JSON string. Once we get the JSON response back from the promise, we'll log the team id and the team name so the response will look like:

Team Created: 9b02ab92564722001or497yg team-mighty-postgres  

Create another team for practice. We've created two more: one for Redis called "team-racing-redis", and another for MongoDB called "team-mongo-respect".

Getting all the teams

Hopefully, you tried out the API and created a couple teams. If not, then create another one. To make the next function that will get all the teams in you Compose account, we'll just need to make an HTTP request to the GET /2016-07/teams endpoint. That'll mean we just need to make a couple of tweaks to the last function we wrote.

let listTeams = () => {  
    fetch(`${apibase}/teams`, { headers: apiheaders })
    .then(res => {
        return res.json();
    })
    .then(json => {
        let teams = json["_embedded"].teams;
        for (let team of teams) {
            console.log(`${team.id} ${team.name} ${JSON.stringify(team.users)}`)
        }
    })
    .catch(err => {
        console.log(err);
    });
}

We didn't have to do much to change the parameters in the fetch method; basically, we removed the method and body fields. Once we get the JSON response back from the promise, we'll drill down into the JSON object and get the teams array. We loop over the teams and log each team id, team name, and a JSON string of an array of users belonging to that team. This will produce something like the following depending on the number of teams you've created:

0t77ar90i64788492or497o1 team-racing-redis []  
5f33e3862p470913r4r631pw team-mongo-respect []  
9b02ab92564722001or497yg team-mighty-postgres []  

Adding users to teams

As you can see, the teams don't have users. Let's fix that. Compose allows us to add users to our teams by making an HTTP request to the PUT /2016-07/teams/:id/users endpoint. We only need to pass in the id of the team we want to add users to and send a document body with the user ids we want to add to that team.

let addTeamUser = (teamid, ...userids) => {  
    let users = JSON.stringify({"user_ids": userids});
    fetch(`${apibase}/teams/${teamid}/users`, { headers: apiheaders, method: 'PUT', body: users })
    .then(res => {
        return res.json();
    })
    .then(json => {
        console.log(`${json.id} ${json.name} ${JSON.stringify(json.users)}`);
    })
    .catch(err => {
        console.log(err);
    });
}

In the function we pass in the team id that we want to add users to, and using the ES6 spread operator, we can pass in as many user ids as we want to add to the team. The result of that will return an array of ids - even if there is only one user id is added. That array of user ids will be stored in the users JSON string, which will be passed into the body field inside fetch. The method field will set to PUT and the team id will be put in the API call. Once the JSON response is returned from the promise, we'll get back the team id, team name, and an array of users belonging to the team. Below, we've added more users to a team just to show what it looks like when more than one name is added:

9b02ab92564722001or497yg team-mighty-postgres [{"id":"5a32cc97624t28d0e26813dd","name":"William Wallace"},{"id":"5a02a1591f750ey3tdbb8fb4","name":"Robert Bruce"}]  

Listing the teams again with listTeams(), we'll now see some names with "team-mighty-postgres":

0t77ar90i64788492or497o1 team-racing-redis []  
5f33e3862p470913r4r631pw team-mongo-respect []  
9b02ab92564722001or497yg team-mighty-postgres [{"id":"5a32cc97624t28d0e26813dd","name":"William Wallace"},{"id":"5a02a1591f750ey3tdbb8fb4","name":"Robert Bruce"}]  

Updating teams

What if you don't like the name of the team and you want to rename it? You can do that using the API, too. To do that, just make an HTTP request to the PATCH /2016-07/teams/:id endpoint with the team id and the new name.

let updateTeam = (teamid, teamname) => {  
    let team = JSON.stringify({"team": {"name": teamname}});    
    fetch(`${apibase}/teams/${teamid}`, { headers: apiheaders, method: 'PATCH', body: team })
    .then(res => {
        return res.json();
    })
    .then(json => {
        console.log(`Team updated: ${json.id} ${json.name} ${JSON.stringify(json.users)}`)
    })
    .catch(err => {
        console.log(err);
    });
}

This function takes the team id and the new team name. The new name gets stored inside a JSON string and that's passed as a document body inside fetch. Once the promise is passed back the JSON response, the team id, new team name, and the users of that team will be logged.

Changing the name of the team "team-mighty-postgres" to "the-best-team" will show you the same team id and users, but the name will be different:

Team updated: 9b02ab92564722001or497yg the-best-team [{"id":"5a32cc97624t28d0e26813dd","name":"William Wallace"},{"id":"5a02a1591f750ey3tdbb8fb4","name":"Robert Bruce"}]  

Removing a team

So, you might want to remove teams, too. The Compose API can handle that. All we have to do is make an HTTP request to the DELETE /2016-07/teams/:id endpoint giving it the id of the team we want to delete.

let removeTeam = (teamid) => {  
    fetch(`${apibase}/teams/${teamid}`, { headers: apiheaders, method: 'DELETE' })
    .then(res => {
        return res.json();
    })
    .then(json => {
        console.log(`Removed team: ${json.id} ${json.name}`)
    })
    .catch(err => {
        console.log(err);
    });
}

We pass in the team id to the function, which gets put into the API call. We also changed the method field to DELETE to make a DELETE request. The promise will return the JSON response and it will then log the team id and the team name that was deleted.

For example, let's delete "the-best-team" that has William Wallace and Robert Bruce.

Removed team: 9b02ab92564722001or497yg the-best-team  

Now, we should only see the two remaining teams. Let's look at the list of teams using listTeams():

0t77ar90i64788492or497o1 team-racing-redis []  
5f33e3862p470913r4r631pw team-mongo-respect []  

You might think that the API also deletes the users. Don't worry, it doesn't. We'll make sure of that by listing the users again with listUsers():

5a02a1591f750ey3tdbb8fb4 Robert Bruce  
7g89sc0dce45vt3000260045 Abdullah Alger  
5a32cc97624t28d0e26813dd William Wallace  

And that's a wrap ...

Commanding the API

Making these functions useful is what we now need to do, which is easily done with yargs, which we already imported and required at the top of the NodeJS file. It's a really useful command-line parser which we'll use to create commands for each of the functions we wrote.

yargs.version("0.0.1")  
    .usage("Usage: ./apiUsersTeams.js <command>")
    .command("accounts", "List user account numbers", {}, (argv) => userAccounts())
    .command("users <accountid>", "Get users of an account", {}, (argv) => listUsers(argv.accountid))
    .command("add-user <accountid> <name> <email> <phone>", "Add user to an account", {}, (argv) => createUser(argv.accountid, argv.name, argv.email, argv.phone))
    .command("remove-user <accountid> <userid>", "Remove user of an account", {}, (argv) => removeUser(argv.accountid, argv.userid))
    .command("teams", "List teams", {}, (argv) => listTeams())
    .command("add-team <name>", "Create a team", {}, (argv) => createTeam(argv.name))
    .command("update-team <teamid> <name>", "Update a team", {}, (argv) => updateTeam(argv.teamid, argv.name))
    .command("remove-team <teamid>", "Remove a team", {}, (argv) => removeTeam(argv.teamid))
    .command("add-team-users <teamid> <userids..>", "Add users to a team", {}, (argv) => addTeamUser(argv.teamid, argv.userids))
    .help()
    .argv;

The command function is used to define commands that will call the functions we've created. It uses argv to assign the command line arguments to the arguments inside our functions when running the NodeJS file. Since we used #! at the beginning of the NodeJS file, we can run it with the NodeJS runtime after we run chmod u+x on the file to make it executable.

Usage: ./apiUsersTeams.js <command>

Commands:  
  apiUsersTeams.js accounts                   List user account numbers
  apiUsersTeams.js users <accountid>          Get users of an account
  apiUsersTeams.js add-user <accountid>       Add user to an account
  <name> <email> <phone>
  apiUsersTeams.js remove-user <accountid>    Remove user of an account
  <userid>
  apiUsersTeams.js teams                      List teams
  apiUsersTeams.js add-team <name>            Create a team
  apiUsersTeams.js update-team <teamid>       Update a team
  <name>
  apiUsersTeams.js remove-team <teamid>       Remove a team
  apiUsersTeams.js add-team-users <teamid>    Add users to a team
  <userids..>

Options:  
  --version  Show version number                                       [boolean]
  --help     Show help                                                 [boolean]

Summing up

Creating some functions to interact with the Compose API is pretty straightforward and very easy to do. Everything found in the Compose console, however, is not in the API yet, but rest assured that at least the basics of creating users and teams can be easily done just on the command line. Next up, creating roles for our users and what this means.


Read more articles about Compose databases - use our Curated Collections Guide for articles on each database type. If you have any feedback about this or any other Compose article, drop the Compose Articles team a line at articles@compose.com. We're happy to hear from you.

attribution Lysander Yuen

Abdullah Alger
Abdullah Alger is a former University lecturer who likes to dig into code, show people how to use and abuse technology, talk about GIS, and fish when the conditions are right. Coffee is in his DNA. Love this article? Head over to Abdullah Alger’s author page to keep reading.

Conquer the Data Layer

Spend your time developing apps, not managing databases.