How To Boost Staff Retention and Empower Employees Using NodeJS, React, and Redis
Want to know how to build an application that boosts staff retention and empowers employees? In this article, learn how to do just that by using Redis.
Join the DZone community and get the full member experience.
Join For FreeEmployees are the lifeblood of every business, making it absolutely crucial to align the right personnel with the right job. Doing so will transform a business into a well-oiled machine where every cog performs with maximum efficiency. Those who don’t will experience the rust that comes from a job mismatch, which usually translates to less productivity, high staff turnover rates, and blighted profits.
Having a poor employee retention rate is expensive, but trying to find the right candidate is a time-consuming process. What’s quite often the case is that businesses already have these skills available to them in their workforce, and only require a system that can transfer employees to positions that correspond with their talents.
This was achieved by Aleksandr Markenzon, who was able to create an application that boosts staff retention and empowers employee retention by transferring workers internally to positions that best match their skill-set.
Let’s take a look at how Aleksandr was able to bring this application to life in the following sections:
What Will You Build?
What Will You Need?
Architecture
Getting Started
Installing the Different Data Types
Installing RedisGraph
Installing the API Routes
How It Works
1. What Will You Build?
You’ll build a powerful application that will enable businesses to internally transfer employees to positions that best match their skillset. The application provides a range of additional benefits, including:
Adding more visibility to what positions are open
Providing recommendations to the best possible match
Assisting employees and managers with the workflow of moving into another team
Below, we’ll go through each step in chronological order and highlight all of the components that you’ll need to build this application.
2. What Will You Need?
Node.js: Used as an open-source, cross-platform that executes JavaScript code outside a web browser
Express: Used as a flexible Node.js web application framework that provides a robust set of features for web and mobile applications.
ReactJS: A free and open-source front-end Javascript library for building user interfaces or UI components
Material UI: A free library that enables you to import and use different components to create a user interface in React applications
Redis: Used as an open-sourced tool that allows you to store data in-memory for high-performance data retrieval and storage purposes
RediSearch: Provides querying, secondary indexing, and full-text search
RedisGraph: Used as a powerful graph database that translates Cypher queries to matrix operations executed over a GraphBLAS engine
3. Architecture
Local
The client runs on ReactJS.
Data is sent to the API server, which runs on NodeJS.
Data is then transmitted to the Redis server that also uses RedisGraph and RedisSearch.
Cloud
Both the API and the client are served on NodeJS, which is hosted in Heroku.
Builds are deployed from the GitHub repo.
Two Redis Cloud free tier instances are used: one for RediSearch and one for RedisGraph.
Usage and High-Level Feature Overview
User Registration, Login, and Session Storage
Registration
Only requires an email and password
Email must be unique
Password needs to be at least 8 characters long and must contain an uppercase, lowercase, number, and a special character
Password is hashed and salted
Login
Only requires a registered email and password
Stores a new session entry after every successful login
Session Storage
Has a max-age value of 1200000
Uses RedisStore for session storage
Includes a secret prefix
User Profile Creation
All users must create a new profile to login
Requires a first name, last name, selection of whether you’re a manager or not, manager name, team name, selection of office location, and selection of programming languages
Once submitted, the user should be able to access the main dashboard
Main Dashboard
As a Manager, you can:
Create a new Req for your team
Cancel a created Req
Accept a Req application
Reject a Req application
As an Associate, you can:
Browse open Req
Apply to a Req
Cancel an application
As a Manager or Associate, you can:
View team members
Req Creation
To create a new Req you have to click the "New Req" button.
This requires a Req name, an office location, an associate level, programming languages, and a description.
The Req will be published with the hiring manager's name, team, and a unique Req ID.
Req Workflow
Currently, there’s a Submit (Associate) -> Accept (Hiring Manager) model for completing an internal transfer.
For future state, the model should be Submit (Associate) -> Accept (Hiring Manager) -> Accept Acceptance (Associate) -> Accept Transfer (Current Manager).
Note: This requires all parties to agree on the transfer before completion.
4. Getting Started
Note: The below steps are targeting the local setup using the redislabs/redismod Docker container. If you want to perform cloud-based implementation using Heroku and Redis Enterprise Cloud, then https://developer.redis.com/create/heroku/portal would be a good starting point.
Prerequisites:
Node
NPM
Docker
RedisInsight
Step 1: Clone the Repository
git clone https://github.com/redis-developer/rediteam
Step 2: Installing Redismod Docker Container
docker pull -d -p 6379:6379 redislabs/redismod
Step 3: Run the Executables
Ensure that you have an NPM package installed locally.
npm run build
The first step is to have the application run locally. To achieve this, follow the below steps:
Monitor the Redis Database
1635070843.353134 [0 172.17.0.1:36616] "GRAPH.QUERY" "Employee" "MATCH(req:Req{name:\"Software Engineer 3\",managerId:3,teamName:\"Team C\",manager:\"Ranee Dubreuil\",associateLevel:3,programmingLanguages:[\"Javascript\",\"Node.js\",\"Python\"],officeLocation:\"San Francisco\",description:\"Looking for a very capable engineer with lots of Javascript and Python experience to help lead the buildout out of some of our new systems.\"}) MATCH(o:OfficeLocation{name:\"San Francisco\"}) MERGE(req)-[r:Requires_Office_Location]->(o)"
1635070843.354250 [0 172.17.0.1:36616] "GRAPH.QUERY" "Employee" "MATCH(req:Req{name:\"Software Engineer 3\",managerId:3,teamName:\"Team C\",manager:\"Ranee Dubreuil\",associateLevel:3,programmingLanguages:[\"Javascript\",\"Node.js\",\"Python\"],officeLocation:\"San Francisco\",description:\"Looking for a very capable engineer with lots of Javascript and Python experience to help lead the buildout out of some of our new systems.\"}) MATCH(e:Manager{id:3})--(t:Team) MERGE(req)-[r:Hiring_For]->(t) SET req.teamName=t.name SET req.manager=e.name"
1635070843.355809 [0 172.17.0.1:36616] "GRAPH.QUERY" "Employee" "MATCH(req:Req{name:\"Software Engineer 3\",managerId:3,teamName:\"Team C\",manager:\"Ranee Dubreuil\",associateLevel:3,programmingLanguages:[\"Javascript\",\"Node.js\",\"Python\"],officeLocation:\"San Francisco\",description:\"Looking for a very capable engineer with lots of Javascript and Python experience to help lead the buildout out of some of our new systems.\"}) MATCH(e:Manager{id:3}) MERGE(req)-[r:Hiring_Manager]->(e)"
npm run load
Note: If you receive a bunch of errors - ignore them.
Then run the last command:
npm run dev
You should now be able to navigate to the localhost:3000/Login and login using any of the emails from the populated fake data or register a new user.
Log in using any of the emails from populated fake data or register new users.
From here, you can log in using any of the emails from the fake data or register new users.
Fake Data
Below is some fake data that you will have to load into the application during step 5 of the installation process.
# Fake logins and profiles
[
{
"login" : {"email" : "adrian.yu@test.com", "password" : "Abc123!"},
"profile" : {
"id" : 1,
"name" : "Adrian Yu",
"firstName" : "Adrian",
"lastName" : "Yu",
"programmingLanguages" : ["Python", "Javascript"],
"associateLevel" : "Manager",
"officeLocation" : "New York",
"teamName" : "Team A",
"manager" : "Adrian Yu",
"isManager" : true
}
},
{"login" : {"email" : "ardell.hyer@test.com", "password" : "Abc123!"},
"profile" : {
"id" : 2,
"name" : "Ardell Hyer",
"firstName" : "Ardell",
"lastName" : "Hyer",
"programmingLanguages" : ["Node.js", "Ruby"],
"associateLevel" : "Manager",
"officeLocation" : "Arlington",
"teamName" : "Team B",
"manager" : "Ardell Hyer",
"isManager" : true
}
},
{"login" : {"email" : "ranee.dubreuil@test.com", "password" : "Abc123!"},
"profile" : {
"id" : 3,
"name" : "Ranee Dubreuil",
"firstName" : "Ranee",
"lastName" : "Dubreuil",
"programmingLanguages" : ["Python", "Javascript", "Node.js"],
"associateLevel" : "Manager",
"officeLocation" : "San Francisco",
"teamName" : "Team C",
"manager" : "Ranee Dubreuil",
"isManager" : true
}
},
{"login" : {"email" : "camille.crosbie@test.com", "password" : "Abc123!"},
"profile" : {
"id" : 4,
"name" : "Camille Crosbie",
"firstName" : "Camille",
"lastName" : "Crosbie",
"programmingLanguages" : ["Python"],
"associateLevel" : 1,
"officeLocation" : "San Francisco",
"teamName" : "Team C",
"manager" : "Ranee Dubreuil",
"isManager" : false
}
},
{"login" : {"email" : "maximo.radford@test.com", "password" : "Abc123!"},
"profile" : {
"id" : 5,
"name" : "Maximo Radford",
"firstName" : "Maximo",
"lastName" : "Radford",
"programmingLanguages" : ["Python", "Javascript"],
"associateLevel" : 2,
"officeLocation" : "San Francisco",
"teamName" : "Team C",
"manager" : "Ranee Dubreuil",
"isManager" : false
}
},
{"login" : {"email" : "sol.heckel@test.com", "password" : "Abc123!"},
"profile" : {
"id" : 6,
"name" : "Sol Heckel",
"firstName" : "Sol",
"lastName" : "Heckel",
"programmingLanguages" : ["Python", "Javascript", "Node.js"],
"associateLevel" : 3,
"officeLocation" : "New York",
"teamName" : "Team C",
"manager" : "Ranee Dubreuil",
"isManager" : false
}
},
{"login" : {"email" : "giuseppina.gobin@test.com", "password" : "Abc123!"},
"profile" : {
"id" : 7,
"name" : "Giuseppina Gobin",
"firstName" : "Giuseppina",
"lastName" : "Gobin",
"programmingLanguages" : ["Javascript", "Node.js"],
"associateLevel" : 2,
"officeLocation" : "New York",
"teamName" : "Team B",
"manager" : "Ardell Hyer",
"isManager" : false
}
},
{"login" : {"email" : "chi.romanik@test.com", "password" : "Abc123!"},
"profile" : {
"id" : 8,
"name" : "Chi Romanik",
"firstName" : "Chi",
"lastName" : "Romanik",
"programmingLanguages" : ["Javascript", "Node.js", "Python", "Ruby"],
"associateLevel" : 4,
"officeLocation" : "Arlington",
"teamName" : "Team B",
"manager" : "Ardell Hyer",
"isManager" : false
}
},
{"login" : {"email" : "denny.castaneda@test.com", "password" : "Abc123!"},
"profile" : {
"id" : 9,
"name" : "Denny Castaneda",
"firstName" : "Denny",
"lastName" : "Castaneda",
"programmingLanguages" : ["Python"],
"associateLevel" : 1,
"officeLocation" : "Arlington",
"teamName" : "Team B",
"manager" : "Ardell Hyer",
"isManager" : false
}
},
{"login" : {"email" : "linnie.laroque@test.com", "password" : "Abc123!"},
"profile" : {
"id" : 10,
"name" : "Linnie Laroque",
"firstName" : "Linnie",
"lastName" : "Laroque",
"programmingLanguages" : ["Javascript", "Node.js"],
"associateLevel" : 2,
"officeLocation" : "New York",
"teamName" : "Team A",
"manager" : "Adrian Yu",
"isManager" : false
}
},
{"login" : {"email" : "john.smith@test.com", "password" : "Abc123!"},
"profile" : {
"id" : 11,
"name" : "John Smith",
"firstName" : "John",
"lastName" : "Smith",
"programmingLanguages" : ["Javascript", "Node.js", "Python"],
"associateLevel" : 3,
"officeLocation" : "San Francisco",
"teamName" : "Team A",
"manager" : "Adrian Yu",
"isManager" : false
}
}]
# Fake Reqs
[
{"name" : "Software Engineer 2",
"managerId" : 1,
"teamName" : "Team A",
"manager" : "Adrian Yu",
"associateLevel" : 2,
"programmingLanguages" : ["Javascript","Node.js"],
"officeLocation" : "New York",
"description" : "Looking for a strong Javascript engineer to build and maintain our awesome full stack apps!"
},
{"name" : "Software Engineer 3",
"managerId" : 3,
"teamName" : "Team C",
"manager" : "Ranee Dubreuil",
"associateLevel" : 3,
"programmingLanguages" : ["Javascript", "Node.js", "Python"],
"officeLocation" : "San Francisco",
"description" : "Looking for a very capable engineer with lots of Javascript and Python experience to help lead the buildout out of some of our new systems."
}
]
5. Installing the Different Data Types
Below are the commands to deploy the different data types in this application.
Redis
User - A registered user
Properties
id - unique id
email
password
RedisGraph
:Employee - A Registered User Profile for an Associate
Properties
id - User id
name - Full Name
firstName
lastName
programmingLanguages - List of programming languages
associateLevel - Can be 1,2,3,4
officeLocation - New York, Arlington, or San Francisco (hardcoded for now)
teamName
manager - Manager's full name
isManager - false
:Manager - A Registered User Profile for a Manager
Properties
id - User id
name - Full Name
firstName
lastName
programmingLanguages - List of programming languages
associateLevel - Manager
officeLocation - New York, Arlington, or San Francisco (hardcoded for now)
teamName
manager - Full Name
isManager - true
:Team
Properties
name - Name of the team
:Associate Level
Properties
name - Name of the level
level - Can be 1,2,3,4 or Manager
yearsExperience - Can be 1,3,5, or 10
cost - Can be 75000, 100000, 150000, or 200000
:ProgammingLanguage
Properties
name - Name of the programming language
:OfficeLocation
Properties
name - Name of the office location
state
full_address
:Req
Properties
name - Name of the Req
managerId - ID of the hiring manager
teamName - Name of the team for which the Req is for
manager - Full name of the manager
associateLevel
officeLocation
programmingLanguages
description
6. Installing RedisGraph
RedisGraph Relationships
Below is a collection of RedisGraph relationships.
(:Employee)-[:Has_Skill]->(:ProgrammingLanguage)
(:Employee)-[:Is_Associate_Level]->(:AssociateLevel)
(:Employee)-[:Is_Closest_To]->(:OfficeLocation)
(:Employee)-[:Is_Part_Of]->(:Team) -(:Employee)-[:Is_Managed_By]->(:Manager)
(:Manager)-[:Is_Managed_By]->(:Manager)
(:Manager)-[:Is_Part_Of]->(:Team)
(:Req)-[:Requires_Skill]->(:ProgrammingLanguage)
(:Req)-[:Requires_Associate_Level]->(:AssociateLevel)
(:Req)-[:Requires_Office_Location]->(:OfficeLocation)
(:Req)-[:Hiring_For]->(:Team)
(:Req)-[:Hiring_Manager]->(:Manager)
7. Installing the API Routes
To install the API routes, simply execute the following commands.
/api/health
GET
Returns 'API Healthy' if API is reachable
/api/redis_health
GET
Returns RedisGraph and RedisSearch connection status (single connection if local)
/api/checkSession
GET
Returns 'Active' if currently logged in user's session is active
/api/register
POST
Params:
email (String)
password (String)
Returns 'Registered' if registration is successful
/api/login
POST
Params:
email (String)
password (String)
Returns 'Correct' if login is successful
/api/logout
GET
Returns 'Logged Out' if logout is successful
/api/email
GET
Returns email of logged in user if successful
/api/profile
POST
Params:
name (String)
firstName (String)
lastName (String)
programmingLanguages (List of strings)
associateLevel (Int)
officeLocation (String)
teamName (String)
manager (String)
isManager (Boolean)
Returns 'Employee Profile Created' if successful
/api/profile
GET
Returns profile of logged in user if successful
/api/team/members
GET
Returns team members of logged in user if successful
/api/team/manager
GET
Returns manager of logged in user if successful
/api/team/req
POST
Params:
name (String)
teamName (String)
programmingLanguages (List of strings)
associateLevel (Int)
officeLocation (String)
manager (String)
description (String)
Returns 'Req Created' if successful
/api/team/reqs
GET
Returns reqs for currently logged in user if it's a Manager
/api/team/applications
GET
Returns applications for currently logged in user's team if it's a Manager
/api/reqs
GET
Returns reqs not including recommended reqs for currently logged in user if it's an associate
/api/reqs/recommended
GET
Returns recommended reqs not including other reqs for currently logged in user if it's an associate
/api/reqs/applied
GET
Returns reqs that the current user has applied to if it's an associate
/api/req/apply
POST
Params:
reqId (Int)
Returns 'Applied for Req!' if successful
/api/req/delete
POST
Params:
reqId (Int)
Returns 'Req Deleted!' if successful
/api/req/cancel
POST
Params:
reqId (Int)
Returns 'Req Application Cancelled!' if successful
/api/req/reject
POST
Params:
reqId (Int)
id (Int)
Returns 'Req Rejected!' if successful
/api/req/accept
POST
Params:
reqId (Int)
id (Int)
Returns 'Req Accepted!' if successful
8. How It Works
Now we’re going to go through how the application works from a user’s perspective.
Logging Into the Dashboard as a Manager
To log in, you’ll have to provide your username and password. Once you’ve done this, you’ll be taken to the user’s dashboard (see example below).
The team name will be displayed on the left-hand side of the screen, which in this example, is Team A. From here you’ll have a complete overview of all your team members, their associate level, their skills, and their location.
On the right hand of the screen, you’ll have a list of the Requests (reqs) that need to be fulfilled. The reqs section is essentially a job board area that highlights every position that’s currently vacant.
Each req will display the job title, description and skills required for that position. On a separate tab, you’ll have access to every application that’s been submitted by your employees.
Creating a New Req
Click on the ‘new req’ button. After this, you’ll be required to fill in all of the details of the latest vacancy in your company. From here, you’ll be required to fill in the:
Name of the position
Location of the position
Associate level
Programming language requirements
Description
Logging in as a Team Member
Any team member that logs into RediTeam will be able to see all of the reqs available to them on the dashboard.
Users will also be recommended certain positions if their skills, associate level, and location correspond with those required in the job. This will be highlighted with a ‘recommended’ icon above the title of the reqs.
Applying for a Position (Reqs)
To begin the application process, users simply have to click on the ‘APPLY’ button.
Reviewing Reqs Applications as a Manager
Managers can view every application sent by an employee on the dashboard.
Conclusion: Bolstering Staff Retention With Redis
Employees champion your businesses, making it crucial to recognize the pool of talents you have and the value they offer. However, without a system in place that can clearly identify your vacancies along with the skills you have available, it can be difficult trying to match the right employee with the right job.
With RediTeam, you can view the entire situation with X-ray vision and empower employees by recognizing their talents and transferring them to roles that better suit their skills. This can have a direct impact on job satisfaction which in turn will help to improve staff retention.
The different Redis modules enabled this application to operate with maximum efficiency with no lags or delays in data transmission. RediSearch was able to search for reqs, employees and applications with ease, providing managers with a holistic view of their pool of talents and vacancies.
Check it out. Be inspired. And have fun with Redis.
Opinions expressed by DZone contributors are their own.
Comments