Last Updated: 2021-01-27
BVN is an acronym for Bank Verification Number. It is an initiative of the Central Bank of Nigeria (CBN) to give customers a unique identification number that can be verified and used across the Nigerian banking industry
With BVN Nigerian bank account holder can enjoy valuable benefits like protection of their accounts from unauthorized access, easy access to banking operations, and unified means of identity verification across all Nigerian banks
Your BVN is a sequence of 11 numbers and it represents a code attached to your personal details — your name, fingerprint, date of birth and a photo of your face. The Central Bank of Nigeria uses those details to protect your identity from people who may want to pretend they're you so they can steal your money or steal other people's money in your name.
When you provide your BVN to a bank or a fintech service, they use it to run a check on the central BVN registry to make sure the number you provided is correct and is yours.
Confirming (i.e verifying) your BVN helps such services protect your identity and keep your money and financial exchanges safe
In this codelab, you're going to build a BVN verifier web app in Javascript. Your app will:
This codelab is focused on using the NIBSS API to verify a BVN. Non-relevant concepts and code blocks are glossed over and are provided for you to simply copy and paste.
Our BVN verifier app will be built with APIs from the FSI sandbox which require an API key that is available only to registered sandbox users
After registering and successfully verifying your account, you can access your API key from your Sandbox profile page. More on this later.
Our BVN-Verifier is a JavaScript web app, so we need to have npm (or yarn) installed on our local machine so that we can create a proper project and manage our dependencies. To check and verify that you have an existing npm installation, open a terminal and type :
$ npm --version
You will also need a code editor. If you don't already have one, download and install VSCode, which is what we will be using for this project.
To keep this codelab simple, we will be running the app locally on our machine instead of deploying it online. We will use special NPM packages to run our backend and frontend apps locally, and thus will be installing them as dev dependencies.
Finally, create a folder called bvn-apps as the home directory where we will create the frontend and backend projects of the app.
Now that you have set up your developer environment, let us proceed to create the backend of our BNV verifier app. Shall we!
Within the bvn-apps folder, create a folder called bvn-verifier-server-app-js, go into the folder with our terminal
$ cd bvn-verifier-server-app-js
Once in, we initialize a standard project with npm init and accept all the default options (which can be adjusted later)
$ npm init -y
This will initialize our project and create a package.json
file that will hold information about the project and its dependencies.
We can now install the dependencies as indicated in the sets of commands below:
$ npm i @babel/cli @babel/core @babel/node @babel/preset-env
$ npm i cors dotenv express morgan innovation-sandbox
The @babel/* dependencies allows us to write our application with modern JavaScript syntax even if it is not yet fully supported in Node. The innovation-sandbox dependency is the FSI API SDK that allows us to easily code against the FSI APIs without the drudgery of directly handling HTTP.
Next, let's install some dependencies that we will use mostly as we build our application.
$ npm i -D nodemon eslint eslint-config-airbnb-base eslint-plugin-import
To make running our app easier, let's now add some scripts for building and starting the backend app. Open the project in VSCode by opening VSCode and dragging the folder into it, then double-click the package.json
file to edit it. Place your cursor at the end of the "license" line (after the comma), hit enter, and paste the following code:
"scripts": {
"dev": "nodemon --exec babel-node ./src/index.js",
"start": "node ./dist/index.js",
"build": "npx babel src --out-dir dist --copy-files"
},
With these in place, your package.json file should look something like below, especially the scripts, dependencies and devDependencies portions:
{
"name": "backend",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"dev": "nodemon --exec babel-node ./src/index.js",
"start": "node ./dist/index.js",
"build": "npx babel src --out-dir dist --copy-files"
},
"devDependencies": {
"eslint": "^7.18.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-plugin-import": "^2.22.1",
"nodemon": "^2.0.7"
},
"dependencies": {
"@babel/cli": "^7.12.10",
"@babel/core": "^7.12.10",
"@babel/node": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"innovation-sandbox": "^1.3.1",
"morgan": "^1.10.0"
}
}
Finally, let's setup eslint and prettier so that we are able to write high quality JavaScript code in this project. In the root of your project, create a .prettierrc
file and add the following contents
{
"singleQuote": true,
"semi": true,
"printWidth": 120,
"tabWidth": 2,
"trailingComma": "none",
"overrides": [
{
"files": "*.json",
"options": {
"parser": "json"
}
}
]
}
Also, create a .eslintrc.js
file with the following contents
module.exports = {
env: {
es2021: true,
node: true
},
extends: [
'airbnb-base'
],
parserOptions: {
ecmaVersion: 12,
sourceType: 'module'
},
rules: {
'comma-dangle': ['error', 'never'],
'no-param-reassign': ['error', { props: false }]
}
};
With the project open in VSCode and in the root folder, create a src
folder where we will put the code we intend to write. Also on the root folder, create a .env
file that will contain configurations for the app.
After registering and successfully verifying your FSI account, you can access your API key from your Sandbox profile page. Once there, click on the eye icon to toggle the display of key so that you can copy it
Now that you have the key, open the .env
file, set the default PORT you want the backend app to run on (usually 3000), and also set your Sandbox API Key. Our app will load the configurations from this file and use it where necessary. See the sample below:
PORT=3000
SANDBOX_KEY=your-fsi-sandbox-key-goes-here
In the src folder, create server.js with the following contents
import cors from 'cors';
import express from 'express';
import morgan from 'morgan';
const app = express();
// Add critical middleware
app.use(cors());
app.use(express.json());
app.use(morgan('tiny'));
// Catch-all error handler
app.use((err, req, res, next) => {
console.log(err.status, req.path, err.message);
res.status(err.status || 500).json({
message: err.message
});
});
export default app;
server.js
contains the actual code that creates an ExpressJS web server which will handle and route incoming HTTP requests to the appropriate API endpoints for our backend app. We will soon be adding the endpoints and routing instructions, but for now, we simply create and export the server instance which our index.js
file will import so it can start the server and begin listening for requests on the configured PORT
It's time to create index.js with the following contents:
import path from 'path';
import dotenv from 'dotenv';
const configFile = path.join(__dirname, '../.env');
dotenv.config({ path: configFile });
const runServer = async () => {
const { default: server } = await import('./server');
const PORT = process.env.PORT || 5000;
server.listen(PORT, () => {
console.log(`server started on /:${PORT}`);
});
};
runServer();
index.js
basically uses the path and dotenv node modules to load our .env
file so it can read in our PORT and Sandbox Key configuration settings. It then starts our Express web server instance by importing it and listening on the configured PORT or 5000.
Open your terminal in VSCode (CTRL or Command + ~) and type npm run dev, hit Enter. This will run the "dev" script we created in our package.json
file.
You should see output like the following in the terminal window
[nodemon] 2.0.7
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `babel-node ./src/index.js`
server started on /:5000
The nodemon
module allows us to keep the server running and have it automatically restart each time we save changes to our backend code. This way we don't have to manually rerun the commands to start/restart the server.
As part of our backend app's skeleton, let's make sure we are able to catch errors that may arise from using the sandbox APIs. Within the src folder, create a commons folder and then create an errors folder inside it. Add the following files to the errors folder:
class APIError extends Error {
constructor({ message, status }) {
super(message);
this.status = status;
this.message = message;
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
}
}
export default APIError;
import APIError from './api-error';
class NIBSSError extends APIError {}
export default NIBSSError;
src/commons/errors/index.js
import NIBSSError from './nibss-error';
export default {
NIBSSError
};
With the above three files in the errors folder, we've created a system that allows us to throw app-specific errors in response to errors from our use of the Sandbox APIs/SDK. In this case, we intend to catch all errors emanating from using the NIBSS API, and then throw our own NIBSSError
with the necessary information so that the rest of our app can act accordingly.
We will be putting NIBSSError
to use in the section where we create the BVN verifier endpoint, so watch out!
Before we get into the weeds of handling requests for BVN validation by creating an endpoint for it, lets first create a simple default endpoint for our backend app. This will serve as a fallback endpoint and helps us validate that we are ready to build the BVN verification endpoint.
We will follow this process to create and expose endpoints in our backend app
src/endpoints
folder e.g src/endpoints/ping.js
src/endpoints/index.js
exposes the endpoint to the rest of the applicationsrc/server.js
to listen to the appropriate HTTP requestsLet's follow the above steps to create a PING endpoint
Inside the src folder, create a endpoints folder and add the following into a ping.js file
import { Router } from 'express';
const router = Router();
const defaultResponse = `BVN Verifier: ${new Date()}`;
const defaultHandler = (req, res) => res.status(200).send({ message: defaultResponse });
// handle requests if no path or /ping was specified
router.get('/', defaultHandler);
router.post('/', defaultHandler);
export default router;
We are using the Express router to handle HTTP GET
and POST
requests that are sent to a /ping endpoint. We will also use this endpoint to handle requests where there is no specified endpoint, effectively making it our fallback endpoint.
Next, let's create an index.js file inside the endpoints folder and use it to expose the PING endpoint to the rest of the app
import ping from './ping';
export default {
ping
};
With the above, we can now import src/endpoints/index.js
(the file above) into src/server.js
to complete registering the PING endpoint which will handle requests to /ping
and /
(the root of the server)
Modify src/server.js
and add the following lines where stated
// after the line that imports morgan, add
import endpoints from './endpoints';
// below the app.use(...) section,
// hit enter twice (to make space) then add the following
app.use('/', endpoints.ping);
app.use('/ping', endpoints.ping);
If you don't already have your local dev server running, open your terminal in VSCode (CTRL or Command + ~) and type npm run dev, then hit Enter. This will run the "dev" script we created in our package.json
file.
You should see output like the following in the terminal window
[nodemon] 2.0.7
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `babel-node ./src/index.js`
server started on /:5000
If you already have the dev server running, or after starting it like we just did above, take note of the PORT the server is running on (the number on the last line from the above output, e.g 5000) and type http://localhost:PORT into the Chrome URL bar. Since our PORT is 5000
, we will be typing http://localhost:5000 into Chrome
You should see a response similar to the one below, in your browser screen:
We are able to see the formatted JSON response from the server because we installed JSONViewer.
All of this confirms that our backend is running and able to handle HTTP requests.
It is now time to create our BVN verification endpoint. See you in the next section!
Again, we will follow the earlier process to create and expose endpoints in our backend app
src/endpoints
e.g src/endpoints/verify-bvn.js
src/endpoints/index.js
exposes the endpoint to the rest of the applicationsrc/server.js
to listen to the appropriate HTTP requestsLet's follow the above steps to create a BVN verification endpoint
Inside the endpoints folder and add the following into a verify-bvn.js file
import { Router } from 'express';
import { nibss } from 'innovation-sandbox';
import errors from '../commons/errors/nibss-error';
const router = Router();
const { NIBSSError } = errors;
const handleVerificationReq = async (req, res) => {
// TODO validate the bvn input from the client
const { bvn } = req.body;
try {
// TODO use imported NIBSS SDK to verify the bvn input
let verification;
const message = verification ? 'verification completed' : `BVN ${bvn} could not be found / verified`;
return res.status(200).json({
message,
verification
});
} catch (error) {
console.log(error);
res.status(500).json({
message: 'Unable to handle your verification request. Pls try again or contact support'
});
}
};
router.post('/', handleVerificationReq);
export default router;
The above shows that we import the NIBSS API from the innovation-sandbox
npm module (the FSI sandbox SDK for JavaScript, which we installed while setting up this project). We also import the NIBSSError error utility we created in the Build The Backend Skeleton section earlier.
We then create an async handleVerificationReq
function that we will use to handle HTTP POST
requests for BVN verification. It extracts the BVN to be verified from the HTTP request body and then enters a try/catch
block (in case there are errors) where it declares a verification
variable that will hold the BVN verification status/result after we do the actual verification call.
If an error occurs during the verification flow, handleVerificationReq
returns HTTP status code 500
, with an appropriate error message. It will return the verification data and HTTP status 200
if the verification flow completes successfully.
The src/endpoints/index.js
can now be modified to include and expose the BVN verify endpoint we have just created:
import ping from './ping';
import verifyBVN from './verify-bvn';
export default {
ping,
verifyBVN
};
Since we've already imported src/endpoints/index.js
into src/server.js
, all we need to do now is to modify src/server.js
so that it has an entry where HTTP requests to verify BVNs are handled by the verifyBVN
endpoint exposed by src/endpoints/index.js
// after the last app.use(...) line for /ping, add the following
// Route to the business of this platform!
app.use('/verify/bvn', endpoints.verifyBVN);
With the above setup, we are basically saying all requests to /verify/bvn
on our backend app will be routed to, and handled by verifyBVN
which we will be fleshing out in the next section
First, edit the .env
file in the root of your project folder, add a configuration entry for NIBSS_ORG_CODE
. And set the value to 11111
(as stated in the NIBSS API documentation here).
...
NIBSS_ORG_CODE=11111
Next, in src/endpoints/verify-bvn.js
file, just above the handleVerificationReq
function, add the following :
const generateNIBSSCredentials = async () => {
// use the sandbox key and NIBSS org code
// safely stored locally in our .env file
const credentials = await nibss.Bvnr.Reset({
sandbox_key: process.env.SANDBOX_KEY,
organisation_code: process.env.NIBSS_ORG_CODE
});
const { status } = credentials;
if (status && status >= 400) {
const message = 'Request likely has invalid Sandbox key and/or NIBSS ORG code';
console.error(`[HTTP:${status}] ${message}`);
throw new NIBSSError({
status,
message
});
}
return credentials;
};
const getNIBSSCredentials = async () => {
// TODO call generateNIBSSCredentials as few times as possible
// and save certs in memory. E.g save to Redis
const certs = await generateNIBSSCredentials();
return certs;
};
const verifyBVN = async (data) => {
const verification = await nibss.Bvnr.VerifySingleBVN({
...data,
sandbox_key: process.env.SANDBOX_KEY,
organisation_code: process.env.NIBSS_ORG_CODE
});
return verification;
};
The generateNIBSSCredentials
function uses the NIBSS BVN Reset API to obtain the credentials with which we can securely make subsequent calls to the NIBSS APIs, e.g to verify a BVN. We get the credentials cheaply by using the sandbox SDK, since it saves us the headache of having to know or handle the required encryption involved.
generateNIBSSCredentials
returns the NIBSS credentials or uses our NIBSSError class to throw a custom error that makes sense to our backend application
The getNIBSSCredentials
function currently just delegates to the generateNIBSSCredentials
function to obtain the credentials and returns it. getNIBSSCredentials
is a great place to do important things like saving the credentials so that our backend app does not always have to generate a new one for every single request for BVN validation.
The verifyBVN
function is where the magic happens. It will be called with data needed for the validation (including the BVN in question), and it uses the SDK to make a call to the verifySingleBVN API. It then returns the verification result payload it gets from the API call.
With all of these specialized utility functions out of the way, we are now ready to modify the handleVerificationReq
function to complete the verification flow for our application. Inside the try/catch block in the function, replace the let verification;
line in src/endpoints/verify-bvn.js
file with the following snippet:
const { ivkey, aes_key: aesKey, password } = await getNIBSSCredentials();
const { data: verification } = await verifyBVN({
bvn,
ivkey,
password,
aes_key: aesKey
});
The above basically calls getNIBSSCredentials
to get credentials to securely call the NIBSS API. We get back a credential payload which is destructured to ivkey, aesKey,
and password
These are then used to call verifyBVN
while also passing in the bvn
to be verified. The result of that call is further destructured to obtain the verification
payload which the rest of handleVerificationReq
needs to complete its task.
The complete src/endpoints/verify-bvn.js
should look like the following:
import { Router } from 'express';
import { nibss } from 'innovation-sandbox';
import errors from '../commons/errors/nibss-error';
const router = Router();
const { NIBSSError } = errors;
const generateNIBSSCredentials = async () => {
const credentials = await nibss.Bvnr.Reset({
sandbox_key: process.env.SANDBOX_KEY,
organisation_code: process.env.NIBSS_ORG_CODE
});
const { status } = credentials;
if (status && status >= 400) {
const message = 'Request likely has invalid Sandbox key and/or NIBSS ORG code';
console.error(`[HTTP:${status}] ${message}`);
throw new NIBSSError({
status,
message
});
}
return credentials;
};
const getNIBSSCredentials = async () => {
// TODO call generateNIBSSCredentials as few times as possible
// and save certs in memory. E.g save to Redis
const certs = await generateNIBSSCredentials();
return certs;
};
const verifyBVN = async (data) => {
const verification = await nibss.Bvnr.VerifySingleBVN({
...data,
sandbox_key: process.env.SANDBOX_KEY,
organisation_code: process.env.NIBSS_ORG_CODE
});
return verification;
};
const handleVerificationReq = async (req, res) => {
// TODO validate the bvn input
const { bvn } = req.body;
try {
const { ivkey, aes_key: aesKey, password } = await getNIBSSCredentials();
const { data: verification } = await verifyBVN({
bvn,
ivkey,
password,
aes_key: aesKey
});
const message = verification ? 'verification completed' : `BVN ${bvn} could not be found / verified`;
return res.status(200).json({
message,
verification
});
} catch (error) {
console.log(error);
res.status(500).json({
message: 'Unable to handle your verification request. Pls try again or contact support'
});
}
};
router.post('/', handleVerificationReq);
export default router;
It was definitely fun building the backend of our BVN verifier app. Now let's run it in our local machine so we can do some very basic BVN verification tests.
Like we did before, you can run the backend app from the VSCode terminal (with the project open in VSCode) by hitting Ctrl or Cmd + ~ and typing npm start
or npm run dev
(to run it in dev mode). We will go with npm run dev
in this example and the output should be similar to:
[nodemon] 2.0.7
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `babel-node ./src/index.js`
server started on /:5000
If you go to localhost:5000
on Chrome (since the PORT in the last line of the above output is 5000), and you installed the JSONViewer extension, you should see something like the following output in your browser. This is coming from our PING endpoint, and it confirms that our backend app is running and able to handle HTTP requests.
Open Postman and make a HTTP POST request to localhost:5000/verify/bvn
making sure to supply the sample BVN number as a JSON request data payload like in the screenshot below
In the annotated sample above, (2) sets the request URL to the URL where your server is running (e.g localhost:5000). Clicking the Send button will send the request to the backend application which handles the verification request and returns the response displayed in (8). If there are any errors, you can head back to the terminal in VSCode to see what hints are available there.
Within the bvn-apps folder you created in an earlier step, create a folder called bvn-verifier-client-app-js, and go into the folder with our terminal
$ cd bvn-verifier-client-app-js
Once in, we initialize a standard project with npm init
and accept all the default options (which can be adjusted later)
$ npm init -y
This will initialize our project and create a package.json
file which will hold information about the project and its dependencies.
We can now install the dev dependencies as indicated in the commands below:
$ npm i -D lite-server eslint eslint-config-airbnb-base eslint-plugin-import
Open the project in VSCode and edit the package.json file. Add the following as the scripts section, which will allow us locally run our web app with the lite-server npm module
"scripts": {
"dev": "lite-server --baseDir='src'"
},
Feel free to set up and configure ESlint and Prettier with the .eslintrc.js and .prettierrc files in the app repo on GitHub.
Next, create a src folder in the root of the project folder, then create an images folder within src. Download and copy the files from the images folder in the repo into your new images folder.
In the src folder, create index.html
with the following contents:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="./images/fsi-favicon.ico" type="image/x-icon">
<title>BVN Verifier</title>
<style>
input {
padding: 0.5em;
padding-right: 1.3em;
letter-spacing: 10px;
background-repeat: no-repeat;
background-position: right center;
}
input:placeholder-shown {
letter-spacing: normal;
}
input.busy {
background-image: url(./images/busy.svg);
}
input.valid {
background-image: url(./images/valid.svg);
}
input.invalid {
background-image: url(./images/invalid.svg);
}
input.error {
background-image: url(./images/feedback.svg);
}
img {
width:100px;
height: auto;
display: block;
}
</style>
</head>
<body>
<div data-app>
<img src="./images/bvn-logo.webp" alt="BVN" />
<input data-bvn-input
placeholder="Enter a valid BVN"
type="text">
</div>
<script type="module" src="./app.js"></script>
</body>
</html>
Finally, create app.js
inside the src folder:
const startApp = () => {
};
document.addEventListener('DOMContentLoaded', startApp);
So far, we have a very simple web page displaying an image and an input field. These are styled with inline CSS (for simplicity and speed) and controlled by our currently very simple app.js
file that runs the startApp
function when the browser is done loading our web page.
Let's try to serve our app and see how it looks in a browser. While in the frontend app's project in VSCode, open the command line and type npm run dev
. This will use lite-server
to open our src/index.html
in a new browser tab to display to following
First let's update the markup of the input field in the HTML file and give it some super powers:
...
<input data-bvn-input
placeholder="Enter a valid BVN"
type="text"
inputmode="numeric"
pattern="[0-9]*" />
...
Our BVN input field has a data-bvn-input
attribute which we will use to identify and select it. You can set an id for it if you prefer.
We've given it a pattern
to improve validation by restricting input to only numbers, and also set the new magic inputmode
attribute to numeric
so that only a numeric keypad is displayed when trying to make an entry on the app on mobile devices. These allow us to customize the text input field for our use case, instead of using a number input field that introduces controls for incrementing and decrementing the input number, which is not what we want in our app.
Moving on, we now need to update src/app.js
so that we can handle what the user types into our input field
const digitPattern = /^\d+$/;
const bvnPattern = /^\d{11}$/;
const BACKEND = 'localhost:5000';
let alreadyChecking = false;
const verifyBVN = async (bvn) => {
};
const handleInput = async (e) => {
const { target } = e;
const { value } = target;
if (alreadyChecking
|| !digitPattern.test(value)
|| !bvnPattern.test(value)) return;
alreadyChecking = true;
requestAnimationFrame(() => {
target.classList.remove('invalid', 'valid');
target.classList.add('busy');
});
const { verification, error } = await verifyBVN(value);
alreadyChecking = false;
requestAnimationFrame(() => {
target.classList.remove('busy');
target.removeAttribute('disabled');
let status = verification ? 'valid' : 'invalid';
status = error ? 'error' : status;
target.classList.add(status);
});
};
const startApp = () => {
const field = document.querySelector('[data-bvn-input]');
field.addEventListener('input', handleInput);
};
From the bottom up, startApp
grabs the input field and calls handleInput
as the user is typing into it. handleInput
gets what the user has typed up to that point, checks that it is only a sequence of numbers and exits if it isn't.
After the user has entered 11 digits (a potential valid BVN), handleInput
sets alreadyChecking
to true
to indicate that it now wants to validate the user's entry and prevent unnecessary subsequent checks until this one is completed. It then proceeds to update the app user about the check it is about to initiate by updating the UI and displaying a busy icon in the input field.
The UI update is wrapped and scheduled with requestAnimationFrame to ensure the update is effectively rendered.
After that, we pass the bvn entry to verifyBVN
and wait for it to give us a verification
or an error
response so that we can reset alreadyChecking
and update the user again by displaying a valid or invalid icon.
The verifyBVN
function simply makes a POST
call to our backend server and returns the response or any raised error
...
const verifyBVN = async (bvn) => {
try {
const response = await fetch(`${BACKEND}/verify/bvn`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ bvn })
});
const data = await response.json();
return data;
} catch (error) {
console.warn(error);
return {
error: error.message
};
}
};
...
The complete app.js file that powers our frontend app looks like below. Make sure your BACKEND
variable is pointing to where your local server is running, which is likely localhost:3000
or localhost:5000
const digitPattern = /^\d+$/;
const bvnPattern = /^\d{11}$/;
const BACKEND = 'http://localhost:5000';
let alreadyChecking = false;
const verifyBVN = async (bvn) => {
try {
const response = await fetch(`${BACKEND}/verify/bvn`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ bvn })
});
const data = await response.json();
return data;
} catch (error) {
console.warn(error);
return {
error: error.message
};
}
};
const handleInput = async (e) => {
const { target } = e;
const { value } = target;
if (alreadyChecking
|| !digitPattern.test(value)
|| !bvnPattern.test(value)) return;
alreadyChecking = true;
requestAnimationFrame(() => {
target.classList.remove('invalid', 'valid');
target.classList.add('busy');
});
const { verification, error } = await verifyBVN(value);
alreadyChecking = false;
requestAnimationFrame(() => {
target.classList.remove('busy');
target.removeAttribute('disabled');
let status = verification ? 'valid' : 'invalid';
status = error ? 'error' : status;
target.classList.add(status);
});
};
const startApp = () => {
const field = document.querySelector('[data-bvn-input]');
field.addEventListener('input', handleInput);
};
document.addEventListener('DOMContentLoaded', startApp);
We've done so much work to build our backend and frontend apps, it's time to put them to the test. Let's do that in the next section.
If you have previous terminals running any of the apps (e.g VSCode terminal), exit from all of them (type Ctrl+C in the terminal) so that we are sure there aren't multiple instances of our apps running at the same time.
Open a terminal, move into the backend app's folder and run the dev script to start the server. Note the PORT the server is running on, incase you need to change the PORT of the BACKEND
variable in the frontend app, as stated in the previous section:
$ npm run dev
Open another terminal (or a new tab in the previous terminal if tabs are supported), move into the frontend app's folder and run the dev script which should open the app in the browser
$ npm run dev
Type 12345678901 as the sample BVN into the input field and observe what happens in the frontend app. Feel free to open the Chrome developer console to see if any additional details, warnings, or errors are logged there.
The editorial team built and deployed a reference frontend app to http://bvn-verifier.netlify.app in case you want to load it up and also give it a try. On mobile, the app looks like below
Congratulations, you've successfully built and tested your FSI powered BVN verifier web app which uses the JavaScript SDK to interact with the NIBSS API
You now know the key steps required to turn a simple idea into an MVP, powered by the FSI Innovation Sandbox
Check out some more codelabs...