Building an expense-tracking and budgeting app as a side project - It begins

ChaitanyaChaitanya
5 min read

So why am I building this?

I was going through my expenses the other day, and I noticed that I have been spending my money as if my UPI provider is paying for it and not my bank account. I have been treating myself a lot lately, well, nothing wrong with that but frequent spending sprees and emotional buying can come back to haunt me later. So It might be time to do all the boring stuff my dad asks me to do. Set my monthly "budget" and spend accordingly.

Desired outcome of this project

  • One place where I can see all my transactions, from all my bank accounts, and debit/credit cards.
  • To be able to set budgets and alerts - if I cross a certain threshold that is set, add all my subscriptions.
  • Just help me be frugal in general - might come up with more things as I build this.

Now we come to the point of why not use any other app that will do all of this for me. The short answer to this is, being a developer, This is what we love to do isn't it? Reinvent the wheel!

What my tech stack looks like

  • Going to use React for UI stuff and a simple node+Express server for the REST service.
  • Will mostly go with Mongo DB as my preferred solution because of the free tier. I don't think my use case requires in- depth look into the choice of database. It's going to be read operations 99% of the time.

How am I planning on building this?

Your email is the best place to get all the info you need about all the transactions happening on your account/card. So, I have decided to poll my mail for emails with transaction alerts and persist them somewhere. I would have to run a CRON job to do this. To make this process easier, I have created a new filter for all my alert messages for bank account and credit card transactions in my Gmail. All these filtered messages are labeled with a custom label I created.

The filtered emails look something like this: image.png

So my CRON job will basically do:

  • Read all messages that have the required label.
  • Parse and save the transaction data to a database.
  • Remove the label from the messages just read, so that they will not be picked up the next time it runs. I have gone through Gmail API docs and that seems to be perfect for my use case - to get the data I need.

A REST service: This will be required to get saved transactions and add budgets and alerts.

Getting access to Gmail API

They have great documentation on their APIs and a great way to try them out using API explorer. You can check out the docs here, maybe you might have some ideas where you would need access to your Gmail. After trying out a few of them, I finalized all the endpoints I would need for my use case:

The next step, figuring out Authentication and Authorization Login to your google cloud console and create a project if you don't already have one. This is like the workspace where you will operate. Next, let's enable the Gmail API by going to the API library, navigating to Gmail API, and clicking on enable. Now, this will be visible under enabled API and services for the project

image.png

Now let's go to the credentials section and create an OAuth 2.0 client.

Add all the necessary info required like redirect URI and callback URI and you will get your client id and secret. If you need help understanding OAuth, play around here. image.png The next part is configuring the consent screen - a pop-up the user will see while authenticating, asking for permissions that allow this application access to your Gmail. We ask for these permissions through 'scopes'. scopes I need for my use case are: "https://www.googleapis.com/auth/gmail.modify, https://www.googleapis.com/auth/gmail.labels, https://www.googleapis.com/auth/gmail.readonly"

the basic flow of authentication and authorization would look something like this

image.png

I am using google API Node SDK which made everything so much easier by handling the refreshing access tokens part, allowing me to use the APIs seamlessly. I went on to create a basic endpoint to fetch me a list of messages filtered by a label I created, which gives me the transaction amount and the transaction date along with the message id. I had to parse the 'snippet' property of the message body returned by Gmail API to pick the transaction amount and date with the help of RegExs. It looks something like this right now:

var fs = require("fs");
const authService = require("./auth-service");
const { google } = require("googleapis");
module.exports = {
  getMessagesList: async () => {
    google.options({ auth: authService.getOauthClient() });
    const gmail = google.gmail("v1");

    const res = await gmail.users.messages.list({
      labelIds: [process.env.LABEL_ID],
      userId: "me",
    });
    let messages = await Promise.all(
      res.data.messages.map((message) =>
        gmail.users.messages.get({
          id: message.id,
          format: "raw",
          userId: "me",
        })
      )
    );
    let messageContent = messages?.map((message) => ({
      snippet: message.data.snippet,
      id: message.data.id,
    }));
    return messageContent
      .filter((m) => (m.snippet.match(/Available/g)?.[0] ? false : true)) // Filtering out mails that show me available balance and not transaction details
      .map((m) => {
        let res = {};
        let transactionDate = m.snippet.match(/\d{2}-\d{2}-\d{2}/g)?.[0];
        res.transactionAmount = parseFloat(
          m.snippet.match(/\d+\.\d+(?!Rs\.)/g)?.[0] || "0.0"
        );
        res.transactionDate = transactionDate;
        res.dd = transactionDate.split("-")[0];
        res.mm = transactionDate.split("-")[1];
        res.yy = transactionDate.split("-")[2];

        return res;
      });
  },
};

And the result is :

image.png

Next Steps

  • Build a proper UI to view this List. Right now I only have a google login button!
  • Build a CRON job that saves these results in a DB (mostly mongo since I would get a free tier) and it's run twice every day.
  • I wanted to try out serverless so would try to make use of the serverless framework to actually deploy this backend and the CRON, will also try to integrate SNS service by AWS to send alerts.
  • Design how creating budgets/goals will work.

Thanks for your time, and wish me luck!

0
Subscribe to my newsletter

Read articles from Chaitanya directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Chaitanya
Chaitanya