Create a Full Stack Banking Application using React

Part 2: Create an application using PostgreSQL, Express, React and Node.js stack

Yogesh Chavan
JavaScript in Plain English

--

Photo by Christin Hume on Unsplash

This is part two of the series of building Full Stack banking application.
If you have missed part one, check that out HERE

In this article, we will continue to build the banking application by adding options to create a new bank account, allow users to deposit and withdraw an amount from the account and allow users to generate and download transaction reports in pdf format.

Let’s get started

Open server/scripts.sql and add the account and transactions table scripts:

Create a new file Summary.js inside components folder and add the following content:

Create a new file AccountForm.js inside components folder and add the following content:

Create a new file Account.js inside components folder and add the following content:

Here, we are displaying the buttons for the deposit, withdraw, and summary page.

Add a route for /account inside the AppRouter.js file:

<Route path="/account" component={Account} />

Now, inside Header.js add link for Account component:

Now, your account page will look like this:

initial account page

Now, let’s add the forms to withdraw and deposit amount and to update the account details in AccountForm.js file:

Withdraw and deposit amount forms

Now, we have forms to withdraw or deposit amount but we don’t have an option to add a bank account yet to withdraw or deposit amount from so let’s add a form to add a new bank account first.

Create a new file AddAccountForm.js inside components folder and add the following content:

Now, we will check if the account number exists and only then show the form to withdraw or deposit otherwise we will show the form to add an account.

Replace the contents of AccountForm.js with the following content:

Create a new file account.js inside src/actions folder with the following content:

Create a new file mask.js inside src/utils folder with the following content:

This code will display only the last 4 digits of the account number and * will be displayed for other digits.

Create a new file account.js inside server/routes folder and add the following content:

Note, we are exporting here, Router and getAccountByAccountId function so we can use it in another route file without the need of re-writing the same code. This is because we need to call getAccountByAccountId once while getting the transaction and another time while downloading the pdf report.

Add the account route to server/index.js file:

Create a new file account.js inside src/reducers folder with the following content:

Open store/store.js and add account reducer inside it:

Now, restart your application by running yarn start and when you go to the account page and click on the withdraw or deposit button, you will be able to see add account form:

Add bank account form

When the user clicks on Submit button to add an account, then we need to call the initiateGetAccntDetails function so that the user will be able to see the form to withdraw or deposit amount.

added account details

Therefore, we have added a return statement inside try block of actions/account.js file above for initiateAddAccntDetails function:

return await post(`${BASE_API_URL}/account`, {

Because initiateAddAccntDetails is declared as async function, return keyword will return a promise from the function and in AccountForm.js, inside handleAddAccount function, we have added a .then handler to call the initiateGetAccntDetails function:

handleAddAccount = (account) => {
const { account_no, bank_name, ifsc } = account;
this.props
.dispatch(initiateAddAccntDetails(account_no, bank_name, ifsc))
.then(() => this.props.dispatch(initiateGetAccntDetails()));
};

Now, try adding a new account and verify that it’s working as expected.

Add and edit account demo

You will also be able to update the account ifsc code by clicking on “Edit Account Details” link and entering new ifsc code.

Now, create a new file transactions.js inside server/routes folder with the following content:

Here, whenever we are depositing or withdrawing any amount, we are adding the details in transactions table and also updating the total_balance column from account table. So we are doing two things here.

If one of them fails then the other should not happen. If the transactions table is updated but the total_balance column value is not updated due to some error, then we need to revert the new data added in transactions table and vice versa. So we are executing these two things as a transaction therefore instead of pool.query, we are using client.query here where the statements that need to be executed together will be added in between await client.query('begin') and await client.query('commit'). If any of the queries in between these calls fail, we call await client.query('rollback') from inside catch block so the other transaction will be reverted means either both will succeed or none of them.

This will ensure that data will remain consistent in the table.

Open server/db/connect.js and add getClient function inside it and export it from the file

Add transactions route to server/index.js file:

Now, create a new file transactions.js inside src/actions folder with the following content:

Create a new file transactions.js inside src/reducers folder with the following content

Inside utils/constants.js add following constants

export const SET_TRANSACTIONS = 'SET_TRANSACTIONS';
export const ADD_TRANSACTION = 'ADD_TRANSACTION';
export const SET_ACCOUNT = 'SET_ACCOUNT';
export const UPDATE_ACCOUNT = 'UPDATE_ACCOUNT';

Inside store/store.js add transactions reducer

Now, you will be able to withdraw or deposit amount to the bank account.

Withdraw and deposit amount demo

Awesome!

There is one issue though, When we login to the application using one user’s email address, the Redux store will contain that particular user information. Now when we do logout and login using another email address, we will see the same previous user account details displayed because we are not clearing the account information from the Redux store once the user logout from the application.

To understand it better, check out the below gif:

Wrong account information

So to fix this, we need to clear the account information once the user logs out of the application.

To do that, open src/utils/constants.js and add another constant:

export const RESET_ACCOUNT = 'RESET_ACCOUNT';

Inside actions/account.js, add resetAccount function:

export const resetAccount = () => ({
type: RESET_ACCOUNT
});

Inside the reducers/account.js add another switch case

case RESET_ACCOUNT:
return {};

And now, open actions/auth.js and inside initiateLogout function, before calling return dispatch(signOut());, dispatch resetAccount action creator function.

export const initiateLogout = () => {
return async (dispatch) => {
try {
await post(`${BASE_API_URL}/logout`, true, true);
localStorage.removeItem('user_token');
dispatch(resetAccount());
return dispatch(signOut());
} catch (error) {
error.response && dispatch(getErrors(error.response.data));
}
};
};

Now, this will clear the Redux store data and we will get correct logged in user account details.

Correct account information

As you can see, the account information from Redux is cleared and as the second user does not have any account information, add account form is displayed

Now, we are all done with the withdraw and deposit account functionality, Let’s move on with the functionality to generate and download the transaction report.

Install the required dependencies from fullstack_banking_app folder:

yarn add react-datepicker@3.0.0 react-bootstrap-table@4.3.1 downloadjs@1.4.7

Now, Install the required dependencies from server folder:

yarn add ejs@3.1.3 puppeteer@3.3.0 moment@2.26.0

Open components/Summary.js file and replace the contents with the following content:

Open src/index.js and add react-bootstrap-table and react-datepicker CSS imports before main.scss import statement:

import 'react-datepicker/dist/react-datepicker.css';
import 'react-bootstrap-table/dist/react-bootstrap-table-all.min.css';

Open actions/transactions.js and add setTransactions, initiateGetTransactions and downloadReport functions inside it:

Create a new file Report.js inside components folder with the following content:

Open server/routes/transactions.js and add following routes:

Open server/utils/common.js and add the following functions:

And export those functions from the file and import them in routes/transactions.js file:

const { getTransactions, generatePDF } = require('../utils/common');

Create a new views folder inside server folder and add transactions.ejs file inside it with the following content. (ejs is templating engine we are using in Node.js to dynamically generate content)

To learn more about how to use and configure ejs, check out my previous article HERE

Open server/index.js and after app.use(express.json()); add the following line of code

app.set('view engine', 'ejs');

Now, start the server and React app by running yarn start command and once you click on the summary page, you will see the following screen

Summary page screen

You can select the date range for which you want the transaction report and click on Generate Report button.

If there are no transactions during that period, you will see no transactions found message.

No transactions found message

If there are transactions within those periods, then you will see the transactions in table format and will be able to download the report as pdf.

Transactions details

Let's understand the code now.

When we are clicking the “Generate Report” button on the summary page, we are calling the /transactions/account_id route by passing the selected start date and end date and get the list of transactions and add it to the redux store

Transactions in the redux store

and then we pass those transactions to Report.js component and display it in table format.

Now, when we click on “Download Report” button, we are calling the /download/account_id route from routes/transactions.js file by where we are taking the transactions list and passing the transactions list to transactions.ejs file from server/views folder and using ejs.compile method to generate data with dynamic values from transactions array which is stored in the output variable and then we are writing that output html to the transaction.html file and then we call the generatePDF function to generate the pdf file using puppeteer library by passing the generated transactions.html file

The generated transactions.html and transactions.pdf will be stored in views folder

backend code to download pdf

If you want to learn details of how it’s generated using puppeteer, check out my previous article HERE

Then once the pdf is generated, to download the pdf from the server, we are using the downloadjs library by sending the content of pdf to download function provided by downloadjs library.

API call for downloading pdf

Here mentioning, { responseType: 'blob' } for the axios call is very important because this is a pdf file otherwise you will get a blank pdf file.

Note: The downloadjs library is very powerful, we can download any type of file by just passing the content of the file as the first parameter to download function, file to name as the second parameter, and type of file as the third parameter.

Download transactions report functionality

Now, we are done with the application. Let’s revisit the CORS issue.

If you remember, in the first part, we have added the following code in server/index.js file

app.use(cors());

This allows us to make API calls to the server running on port 5000 from our React application running on port 3000.

But adding CORS without any options will allow anyone to access your APIs, which is not good from the security point of view. So let’s see how can we remove the need of CORS at the server-side.

Open package.json from fullstack_banking_app folder and add proxy property at the end of package.json file

React proxy package.json

And remove the following line, as it’s not required now:

app.use(cors());

Now, open src/utils/constants.js and change:

export const BASE_API_URL = 'http://localhost:5000';

To:

export const BASE_API_URL = '';

Now, restart the server and React app by running yarn start command and If you go to http://localhost:3000/, you will see our React application and express APIs will also be available on port 3000 so there is no need of using CORS now. This is because adding proxy allows create-react-app to act as a proxy and now all our server APIs will also run on port 3000 even though we are running the express app on port 5000.

Generating transaction report

You can find Github source code until this point:

  • Using class components HERE
  • Using hooks HERE

To secure this app from XSS and CSRF attacks check out the next part HERE.

That’s it for today. I hope you learned something new.

Don’t forget to subscribe to get my weekly newsletter with amazing tips, tricks, and articles directly in your inbox here.

--

--