‘PocketMoney’ — a weekend React app, the Monzo API and a lesson in knowing your user
I’m enjoying using Monzo’s Pots. If you don’t use Monzo (firstly, what are you waiting for?) Pots is a feature that lets you move money into virtual accounts. You can use them for saving, rounding up purchases for a rainy day, you can lock them until a given date, set goal amounts — or if you’re me, you can put your daughter’s pocket money in them.
I made a pot for my daughter, and every day that she completes all the tasks on her reward chart, she gets 50p.
To add money to the pot, I have an IFTTT button on my widgets that automatically moves 50p to her pot (this saves time every evening.)
But there was a sense from my daughter that because she couldn’t see the money (and who has time for cash anymore?) it didn’t exist. And showing her the amount on my iPhone in the Monzo app wasn’t giving her that feeling of being rewarded.
Could I give her some way to check the amount in her pot, without using the Monzo app itself?
Child-Friendly Access to Monzo Pots
My daughter is 5 years old, and like most kids, her favourite thing is her iPad. She uses it for watching TV and films, playing games and for reading school-assigned eBooks. So for that reason, she’s familiar with (amongst other things) the concept of opening Safari, navigating to a bookmarked website, and logging in.
I thought it would be neat if my daughter could keep tabs on her pocket money by accessing a web app. Maybe it’d be a simple app that uses the Monzo API and looks up the total of a pot. And I got to work with a simple spike to test the idea: a React app to consume the Monzo API and show the amount in a pot.
I quickly learned this would be a more significant technical challenge.
The Monzo API uses OAuth — which is great if you are writing consumer-facing apps — but I didn’t really want to have my daughter be faced with a login page every other day, in which she’d have to hand her iPad over to me, and I’d have to perform the (albeit simple) magic link sign-in process.
Instead, I wanted her to pick up the iPad, open the app and see her pocket money. It occurred to me that I’d need to have two parts main to this system:
- Some kind of task that ran periodically fetched the value from the pot using the Monzo API and store it on a backend
- A simple web-app frontend that could retrieve the cached value and display it in a friendly way
But writing server-side apps that consume an API which is secured using OAuth isn’t simple. The Monzo API gives you a very short-lived token. So to keep the access open, I’ll need to:
- Refresh the token when it expires
- Generate new tokens manually as smoothly as possible and without causing the front-end to stop working
So what started as a simple idea was now presenting some technical challenges that would ordinarily make me just give up and come up with some lo-fi solution (I could write down the amount on a board for example). But I was keen to give this a go — it was an interesting challenge, and I was sure it would be fun to solve.
In the end, I ended up going for the following solution:
- a small Node+Express service for managing OAuth token generation (in the event that tokens expire, I can run this locally and get a new one)
- an AWS Lambda function that runs every 5 minutes to fetch the pot total from Monzo’s API and update this in a database (MongoDB Atlas¹) and keep refreshing the token if need be
- an AWS API Gateway endpoint which can read the pot amount from MongoDB
- a small Gatsby+React SPA that would live on S3 and be fronted by CloudFront, with a nice front-end for displaying the total amount
This stack works out well for a few reasons², but primarily it was easy to set up, easy to interface with, well documented and is super fast in terms of performance and response times.
I also didn’t want to host any kind of virtual server which would be running 24/7 for something this trivial. Similarly, I wanted to keep costs down (something like this probably costs me about £1 a month to run on AWS.)
¹ I really enjoyed using MongoDB Atlas; after looking at the data solutions AWS provides, none were quite as good for having a really simple schema-less No-SQL data store that I could access from a Lambda function. I’ve tried DynamoDB in the past but it’s too much friction.
² Could it have been simpler? Sure! I could’ve just put 50p in a jar every day. Sometimes with weekend projects I like to rattle something out without over-thinking it, maybe stick it on a RasperryPi and run it in the living room. But others, I prefer to see if I can either learn a new tech stack, prove a concept that I might later use in a real production app, or make it something that I think would be more resilient and need less maintenance — and in this case, I think it’s all of the above.
Monzo API — Generating and Refreshing TokenExample of using Express to generate and store OAuth tokens for later use
Because the part of the app that will fetch the pot amount will work server-side and without user interaction, the first part is to generate, and store, an auth token for the Monzo API; this works as follows:
- An express server starts and automatically opens the browser to authenticate with Monzo
- I manually auth with Monzo via Email
- The Express server handles the OAuth redirect and then exchanges the resulting auth code with the Monzo API for an auth token (as described in the Monzo API docs)
- The server stores this resulting token in MongoDB and then stops
Refreshing the tokenLambda function to keep an OAuth token alive and fetch Monzo pot data
A CloudWatch event is configured to trigger a Lambda function on a set interval. This will get the token from MongoDB; if the token expires it will refresh the token, store the new one, and then ultimately it will fetch the pot amount from Monzo API and store it in MongoDB.
Refreshing the token means we can keep the token alive for a lot longer. Maybe forever. And if it ever somehow doesn’t get refreshed, well, now we have a simple task that we can run to very quickly restore the token and keep the connection going.
API Endpoint and React+Gatsby App
Next, we add another aspect to our Lambda function to be able to read from the MongoDB and return the pot as a simple JSON object.
AWS API Gateway can then front this Lambda function to provide a way for us to call it from React.As it says, Gatsby is blazing fast; using it for this web-app was a no-brainer.
A React+Gatsby App is then set up to display the amount, and this is deployed to Amazon S3 and fronted with CloudFront; this gives us a nice way to have an HTTPS-enabled site with all the benefits of CloudFront (mostly speed/caching) and lets us take advantage of Gatsby’s amazing Progressive Web-App plugins. Included by default with Gatsby is some app-icon boilerplate to enable me to add the web app to the home screen on iOS.
The app loads and then fetches the amount via AWS API Gateway which calls Lambda and reads the stored pot amount from MongoDB. And looks a bit like this:
“That’s a cute bunny!” — rewarding UI feedback
Closing Feedback Loops and User Testing
One of the best things about this project was that I was able to go from having a real user problem on Thursday night, thinking about the issue on Friday evening and finished the entire thing and wrote it up here on Sunday.
But what was most important was that because my daughter was the only user for this, I was able to ask her things along the way and have her test the app.
Sketch Designs for the very simple PocketMoney App
Similarly, it’s crucial to close feedback loops with the design and infrastructure of the app too. My process is often to:
- Write a quick-and-dirty version of the app (a spike) meant to flush out any unknowns/questions
- Generate a walking skeleton of the app and have the major parts deployed to a production environment (close the architecture feedback loop)
- Commit and deploy along the way, getting feedback with each iteration (close the product feedback loop)
Firstly, it helps to keep things organised and sane along the way; no big plans or to-do lists necessary — things will evolve to solve the problem at hand as you keep iterating.
But most importantly, it helps to make sure you’re building the right thing both from a technical and a product point of view. Rather than getting all the way to the end before realising you have made a mistake, you can figure that out sooner and fail fast. All while making sure to remember the person at the end who uses your product; whether that’s your friends, millions of paying customers or in this case: just your daughter!