Building a Monzo Bank API application
Problem
I have three bank accounts across several banking providers. Everytime I need to find the total balance, I have to login to three different apps, check my balance in each of them and sum them up.
I needed an app which automates this work for me.
I started researching ways of doing this. I came across the Open Banking initiative: It allows individuals and businesses to access financial information with the approval of their customers. There exists a number of apps which offer this functionality. Most of them are paid.
Solution
I decided to build a bank balance aggregator myself, using the Open Banking APIs across my bank providers. I took a look at the developers documentation for the three banks where I have accounts, Monzo is one of them. They have a very clear documentation, with step-by-step instructions on how to implement the OAuth2.0 flow.
I implemented a web app, using React for a basic front-end, Express in the back-end, managed by webpack.
Set-up
We need to do a number of things before implementing OAuth:
Get the credentials to access the API:
- Go to Monzo's developers portal. You can sign-in using the email address linked to your Monzo account.
- Add an OAuth client in the
Client
tab, this will create a profile for your app, where you define your redirect URI.
- Keep
client_secret
andclient_id
generated in step 2, we will need these to getaccess_token
from the Monzo OAuth2.0 server.
Setup the foundation for the application:
Implement a minimal app which supports React, Babel, Node.js, Express, and is managed by webpack. The source code for a minimal configuration can be found here.
Implementation
We need two configuration files to store the information about the client:
oauth_credentials
: a file for client parameters;client_id
,client_secret
andredirect_uri
.
oauth_urls
: a file for the OAuth endpoints - this is useful for code clarity.
To get the balance for an account, we need to follow these steps:
- Get an authorisation code from Monzo's OAuth server - this will redirect the client to Monzo's portal.
app.get("/monzopage", (req, res) => { res.redirect( urls.base_url + "?client_id=" + params.client_id + "&redirect_uri=" + params.redirect_uri + "&response_type=" + params.response_type + "&state=" + params.state ); });
- Exchange the authorisation code for an access token, store the latter and redirect to the home page (at this step I store the token in a global variable, this can be improved).
app.get("/oauth/callback", (req, res) => { global.code = req.query.code; var client_id = params.client_id; var client_secret = params.client_secret; var redirect_uri = params.redirect_uri; var grant_type = params.grant_type; var url = urls.token_url; request.post( { url: url, form: { grant_type, client_id, client_secret, redirect_uri, code } }, (err, response, body) => { var jsonBody = JSON.parse(body); global.access_token = jsonBody.access_token; } ); res.redirect("/"); });
- Get the balance using the access token.We start by getting the account ID:
app.get("/account", function(req, res) { var account_type = params.account_type; var access_token = global.access_token; var account_url = urls.account_url; request.get( { url: account_url + "?account_type=" + account_type, headers: { Authorization: "Bearer " + access_token } }, (error, response, body) => { var jsonBody = JSON.parse(body); var account_id = jsonBody.accounts[0].id; res.json({ account_id: account_id }); } ); });
Then we get the balance for the given account ID:app.get("/balance", (req, res) => { var account_id = req.query.account_id; var balance_url = urls.balance_url; request.get( { url: balance_url + "?account_id=" + account_id, headers: { Authorization: "Bearer " + global.access_token } }, (error, response, body) => { var balances = JSON.parse(body); res.json({ balances: balances }); } ); });
The minimal React app makes these calls:
import React from "react"; import fetch from "isomorphic-fetch"; class App extends React.Component { state = { account_id: "", balance: "" }; loginToMonzo() { window.location.replace("/monzopage"); } getBalance() { fetch("/account") .then(result => result.json()) .then(response => { this.setState({ account_id: response.account_id }); fetch("/balance?account_id=" + this.state.account_id) .then(result => result.json()) .then(response => { this.setState({ balance: response.balances.balance }); }); }); } render() { return ( <div> <button onClick={e => this.loginToMonzo(e)}>Login to Monzo</button> <button onClick={e => this.getBalance(e)}>Get balance</button> <p>{this.state.balance}</p> </div> ); } } export default App;
Next steps
Hope this helped you understand how to interact with Monzo's API to obtain the balance. We can improve it by:
- Implementing the access token refresh mechanism.
- changing the access token storage to be more secure - in a database for instance.
- Implementing a minimalist design for the page - currently sketching :).
If you get stuck with any step or need help, you can reach out to me via Twitter.
Happy coding!