Managing finances can feel like navigating a maze, but it doesn’t have to be daunting. Whether you’re a student, a freelancer, or simply someone looking to get a better handle on your spending, tracking your expenses is a crucial first step. In this tutorial, we’ll build a simple yet functional web-based expense tracker using Node.js. This project is perfect for beginners and intermediate developers who want to deepen their understanding of Node.js, Express.js, and basic front-end concepts. We’ll create a user-friendly interface where users can add, view, and categorize their expenses. By the end of this guide, you’ll not only have a practical application but also a solid foundation for building more complex web applications.
Why Build an Expense Tracker?
Expense tracking offers several benefits:
- Improved Financial Awareness: Knowing where your money goes is the first step towards better financial decisions.
- Budgeting Made Easier: Track spending against your budget to identify areas where you can save.
- Goal Setting: Monitor progress towards financial goals, such as saving for a down payment or paying off debt.
- Financial Control: Take control of your finances and make informed choices.
Building this expense tracker will allow you to:
- Learn the fundamentals of Node.js and Express.js.
- Understand how to handle HTTP requests (GET, POST, etc.).
- Work with basic HTML, CSS, and JavaScript for the front-end.
- Practice using a database (we’ll use a simple in-memory database for this tutorial).
- Gain experience in building a full-stack web application.
Prerequisites
Before we begin, make sure you have the following installed on your system:
- Node.js and npm (Node Package Manager): You can download these from the official Node.js website: https://nodejs.org/.
- A text editor or IDE: VS Code, Sublime Text, or Atom are excellent choices.
- Basic understanding of HTML, CSS, and JavaScript: While we’ll keep the front-end simple, some familiarity is helpful.
Project Setup
Let’s get started by setting up our project directory and installing the necessary dependencies.
- Create a Project Directory: Open your terminal or command prompt and create a new directory for your project:
mkdir expense-tracker cd expense-tracker - Initialize npm: Initialize a new Node.js project using npm. This will create a
package.jsonfile, which will manage our project’s dependencies:npm init -y - Install Dependencies: We’ll need Express.js for our web server and a templating engine (we’ll use EJS, Embedded JavaScript) to render dynamic HTML. Install these dependencies:
npm install express ejs
Setting Up the Server (index.js)
Create a file named index.js in your project directory. This file will contain the code for our server. Let’s start by importing the necessary modules and setting up a basic Express app:
// index.js
const express = require('express');
const app = express();
const port = 3000;
// Middleware to parse JSON and URL-encoded data
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Set the view engine to EJS
app.set('view engine', 'ejs');
// Sample expenses data (in-memory database)
let expenses = [
{ id: 1, description: 'Groceries', amount: 50, category: 'Food' },
{ id: 2, description: 'Movie Ticket', amount: 15, category: 'Entertainment' }
];
// Routes (we'll add these later)
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
Let’s break down this code:
- Importing Modules: We import the
expressmodule to create our server and assign it to theappvariable. - Setting the Port: We define the port number (
3000). - Middleware:
express.json()andexpress.urlencoded({ extended: true })are middleware functions that parse incoming requests with JSON payloads and URL-encoded payloads, respectively. This allows us to easily access data sent from forms. - Setting the View Engine: We tell Express to use EJS as our templating engine. EJS will allow us to dynamically generate HTML.
- Sample Data: We create a sample
expensesarray. In a real-world application, this data would be stored in a database. - Starting the Server: We use
app.listen()to start the server and listen for incoming requests on the specified port. We also log a message to the console to confirm that the server is running.
Creating the Routes
Now, let’s define the routes for our expense tracker. We’ll need routes to:
- GET /: Display the expense tracker page (showing the list of expenses).
- POST /expenses: Add a new expense.
Add the following code inside the index.js file, before the app.listen() call:
// GET / - Display the expense tracker
app.get('/', (req, res) => {
res.render('index', { expenses: expenses }); // Render the 'index.ejs' view and pass the expenses data
});
// POST /expenses - Add a new expense
app.post('/expenses', (req, res) => {
const newExpense = {
id: expenses.length + 1,
description: req.body.description,
amount: parseFloat(req.body.amount), // Convert amount to a number
category: req.body.category
};
expenses.push(newExpense);
res.redirect('/'); // Redirect back to the home page
});
Let’s explain these routes:
- GET /:
- When a user visits the root path (
/), this route is triggered. res.render('index', { expenses: expenses })renders theindex.ejsview, passing theexpensesdata to it.
- When a user visits the root path (
- POST /expenses:
- This route handles the submission of the expense form.
req.bodycontains the data submitted from the form (description, amount, and category).- We create a
newExpenseobject with the form data, generate a unique ID, and add it to theexpensesarray. res.redirect('/')redirects the user back to the home page (/) after adding the expense, so the updated list of expenses is displayed.
Creating the Views (index.ejs)
Now, let’s create the index.ejs file, which will contain the HTML for our expense tracker. Create a new directory named views in your project directory. Inside the views directory, create a file named index.ejs. Add the following code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Expense Tracker</title>
<style>
body {
font-family: sans-serif;
margin: 20px;
}
h2 {
margin-bottom: 10px;
}
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
form {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
}
input[type="text"], input[type="number"], select {
width: 100%;
padding: 8px;
margin-bottom: 10px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
button {
background-color: #4CAF50;
color: white;
padding: 10px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>
</head>
<body>
<h2>Expense Tracker</h2>
<form action="/expenses" method="POST">
<label for="description">Description:</label>
<input type="text" id="description" name="description" required>
<label for="amount">Amount:</label>
<input type="number" id="amount" name="amount" step="0.01" required>
<label for="category">Category:</label>
<select id="category" name="category" required>
<option value="Food">Food</option>
<option value="Entertainment">Entertainment</option>
<option value="Housing">Housing</option>
<option value="Transportation">Transportation</option>
<option value="Other">Other</option>
</select>
<button type="submit">Add Expense</button>
</form>
<table>
<tr>
<th>Description</th>
<th>Amount</th>
<th>Category</th>
</tr>
<% expenses.forEach(expense => { %>
<tr>
<td><%= expense.description %></td>
<td><%= expense.amount %></td>
<td><%= expense.category %></td>
</tr>
<% }); %>
</table>
</body>
</html>
Let’s break down this HTML:
- Basic HTML Structure: We start with the standard HTML structure, including the
<head>and<body>sections. - CSS Styling: We’ve included basic CSS styling within the
<style>tags to make the expense tracker visually appealing. This includes styles for the body, headings, tables, forms, and buttons. - Expense Form: The
<form>section contains the form for adding new expenses. It includes input fields for description, amount, and category, as well as a submit button. Theaction="/expenses"attribute specifies that the form data will be sent to the/expensesroute, and themethod="POST"attribute specifies that the form data will be sent using the POST method. - Expense Table: The
<table>section displays the list of expenses. - EJS Templating: We use EJS syntax (
<% ... %>and<%= ... %>) to dynamically generate the table rows. The<% expenses.forEach(expense => { %>loop iterates through theexpensesarray (passed from the server), and for each expense, it creates a table row (<tr>) with the expense’s description, amount, and category.<%= expense.description %>,<%= expense.amount %>, and<%= expense.category %>output the values of the expense properties.
Running the Application
Now that we’ve set up the server, the routes, and the view, let’s run our expense tracker. Open your terminal or command prompt, navigate to your project directory (expense-tracker), and run the following command:
node index.js
This will start the server, and you should see the message “Server is running on http://localhost:3000” in your terminal. Open your web browser and go to http://localhost:3000. You should see the expense tracker interface. You can now add new expenses using the form, and they will be displayed in the table.
Common Mistakes and Troubleshooting
Here are some common mistakes and how to fix them:
- Incorrect Dependencies: Double-check that you have installed all the necessary dependencies (
expressandejs) usingnpm install. - Typographical Errors: Carefully review your code for any typos, especially in the route definitions, variable names, and file paths.
- Incorrect File Paths: Ensure that the file paths in your code (e.g., the path to
index.ejs) are correct. - Server Not Running: If you’re not seeing anything in your browser, make sure the server is running in your terminal. If the server is not running, try running the command
node index.jsagain. - Form Not Submitting: Make sure the
actionattribute in your form is correct (e.g.,action="/expenses") and that the form method is set toPOST. Also, verify that thenameattributes of your input fields match the names you are using in your server-side code. - Amount Not Being Parsed Correctly: The amount may not be correctly parsed from the form data. Ensure you are using
parseFloat(req.body.amount)to convert the amount to a number.
Enhancements and Future Improvements
This is a basic expense tracker, and there are many ways to enhance it:
- Database Integration: Instead of using an in-memory database, connect to a real database (e.g., MongoDB, PostgreSQL, or MySQL) to store and retrieve expense data. This will allow you to persist the data even after the server restarts.
- User Authentication: Implement user authentication so that only authorized users can access their expense data.
- Data Validation: Add data validation to the form to ensure that the user enters valid data (e.g., the amount is a positive number).
- More Detailed Reporting: Add features such as the ability to generate reports and charts to visualize expenses.
- Categories: Allow users to create and manage custom expense categories.
- Date Selection: Add a date picker to allow users to specify the date of the expense.
- Edit and Delete Functionality: Allow users to edit and delete existing expenses.
- Styling: Improve the user interface with more advanced CSS and potentially a CSS framework like Bootstrap or Tailwind CSS.
Key Takeaways
In this tutorial, we’ve built a simple web-based expense tracker using Node.js and Express.js. We’ve covered the basics of setting up a Node.js project, creating routes, rendering views with EJS, and handling form submissions. You’ve also learned how to use middleware to parse incoming data. This project provides a solid foundation for building more complex web applications with Node.js.
FAQ
- How do I deploy this application?
You can deploy this application to a cloud platform like Heroku, AWS, or Google Cloud. You’ll need to set up a deployment environment and configure the platform to run your Node.js application.
- Can I use a different templating engine?
Yes, you can. Popular alternatives to EJS include Pug (formerly Jade) and Handlebars. You’ll need to install the appropriate package and modify your code to use the syntax of the chosen templating engine.
- How can I add more features?
Start by identifying the features you want to add (e.g., user authentication, database integration). Then, modify your code to implement those features. This might involve creating new routes, updating your view templates, and interacting with a database.
- What is middleware?
Middleware functions are functions that have access to the request object (
req), the response object (res), and the next middleware function in the application’s request-response cycle. They can perform tasks such as parsing request bodies, handling authentication, or logging requests.
Building this expense tracker, even in its basic form, provides a practical introduction to full-stack web development. The ability to create, manage, and display data through a web interface is a fundamental skill. As you continue to learn and experiment with Node.js, Express.js, and other technologies, you’ll be able to build increasingly sophisticated and useful applications. The journey of learning to code is a continuous process of building, experimenting, and refining; embrace the challenges, and celebrate the small victories along the way. Your growing skills will unlock new possibilities, empowering you to create and innovate with technology.
