Intro
In case of need, check out to the branch called 2_routes
. This branch has already implemented the code for the server.
Register route
- Create a new folder called
routes
on the same level asdatabase-scripts
folder. - Inside the
routes
folder, create a new file calledjwtAuth.ts
. - In the
index.ts
file, add the following code after// ROUTES
:
// ROUTES
app.use("/auth", require("./routes/jwtAuth"));
It means that if you hit the /auth
route, it will go to the jwtAuth.ts
file.
Now, we are going to build the register route step by step and the whole code will be presented at the end of this section. The basic idea is that in the final app, the user will enter the values to the form and the form will be sent in the POST request to the /register
endpoint in JSON format. Then, it will be evaluated and if everything is ok, the user will be inserted into the database and authorized.
Final implementation will be presented later, so you do not need to copy the code snippets one by one.
When the JSON from the client side is received, we need to destructure it and get the values from the request body.
import {Router} from "express";
const router = Router();
// Define the register endpoint route and associated handler function
router.post("/register", async (req, res) => {
// Destructure the req.body object to get user input values
const {
email,
password,
firstname,
lastname,
title,
birthdate,
phone,
sex,
status,
birthnumber,
} = req.body;
}
module.exports = router;
Then, in the highlighted try-catch block, we are going to query the database to check if the user already has a record in the database.
If the user is already registered, we will send an error message.
import {Router} from "express";
import pool from "../dbServer";
const router = Router();
// REGISTER
router.post("/register", async (req, res) => {
const {
email,
password,
firstname,
lastname,
title,
birthdate,
phone,
sex,
status,
birthnumber,
} = req.body;
try {
const user = await pool.query(
"SELECT * FROM useraccount WHERE email = $1",
[email]
);
if (user.rows.length) {
return res.status(401).json("User is already registered.");
}
}
});
module.exports = router;
In the next and final section, we are going to insert the user into the database, return the response or throw an error when something during an execution goes wrong.
import {Router} from "express";
import pool from "../dbServer";
const router = Router();
// REGISTER
router.post("/register", async (req, res) => {
const {
email,
password,
firstname,
lastname,
title,
birthdate,
phone,
sex,
status,
birthnumber,
} = req.body;
try {
const user = await pool.query(
"SELECT * FROM useraccount WHERE email = $1",
[email]
);
if (user.rows.length) {
return res.status(401).json("User is already registered.");
}
const insertStatus = status ?? "patient"
let uniqueUser = await pool.query(
"INSERT INTO useraccount (email, password, firstname, lastname, title, birthdate, phone, sex, status, birthnumber) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING *",
[
email,
password,
firstname,
lastname,
title,
birthdate,
phone,
sex,
insertStatus,
birthnumber,
]
);
res.json({
success: true,
});
} catch (err) {
console.error(err.message);
res.status(500).send("Server error");
}
});
module.exports = router;
Connection to database
When we want to make queries to the database, we also need to define the logic in the dbServer.ts
file located in the same hierarchy as index.ts
file.
Create the file and insert the following code to it:
// Configure connection to database
import {Pool} from 'pg';
const pool = new Pool({
host: "localhost",
port: 5432,
user: "postgres",
password: "postgres",
database: "hackhealthdb",
})
export default pool;
Test
- Start the server by running
npm run dev
and open Postman by runningPostman
in the terminal of the virtual machine. - In the Postman, you should open the
HackHealth
collection, which already contains some predefined requests. - You can try a predefined request called
registration
to register endpoint.
The body of the request should look like this:
{
"email": "will.doe@outlook.com",
"password": "qwerty1234",
"firstname": "Will",
"lastname": "Doe",
"title": "Ing.",
"birthdate": "1972-08-06",
"phone": "+421905524887",
"sex": "M",
"status": "patient",
"birthnumber": "482559/5997"
}
- As a result of sending this POST request to running server, you should get status code 200 and "success": true
Login route
The implementation of the login
route is very analogous to the register
route.
The login route accepts a POST request to the /login
endpoint and expects the email and password properties in the request body.
The route first queries the useraccount table for a user with the provided email address. If no user is found, the route returns an error. If a user is found, the route compares the provided password with the stored password. If the passwords match, the route returns a success message indicating that authentication was successful.
Paste the following code into the jwtAuth.ts
file after the implementation of register
route.
// LOGIN
router.post("/login", async (req, res) => {
const {email, password} = req.body;
try {
const user = await pool.query(
"SELECT * FROM useraccount WHERE email = $1",
[email]
);
if (user.rows.length === 0) {
return res.status(401).json("Invalid email.");
}
const validPassword = password === user.rows[0].password;
if (!validPassword) {
return res.status(401).json("Invalid password.");
}
res.json({
success: true,
message: "Authentication successful!",
});
} catch (err) {
console.error(err.message);
res.status(500).send("Server error");
}
});
- Similarly, as
register
route, you can testlogin
route using a predefined request in Postman (the name of the POST request islogin
). - You can try to log in with the email and password of the user you have just registered.
- Replace the current JSON body with the following:
{
"email": "will.doe@outlook.com",
"password": "qwerty1234"
}
As a result, you should get status code 200 and JSON object with "success": true and "message": "Authentication successful!"
Dashboard route
As the last route in this lecture, we are going to implement the dashboard route.
In the index.ts
file, add the following line in the // ROUTES
section.
app.use("/dashboard", require("./routes/dashboard"));
Then, create a new file dashboard.ts
in the routes
folder and paste the following code.
const router = require("express").Router();
import pool from "../dbServer"
router.get("/", async (req, res) => {
try {
// req.user -> returns the object with id which represents userid in table
const user = await pool.query(
"SELECT firstname FROM useraccount WHERE password = $1",
[req.user.password]
);
res.json(user.rows[0]);
} catch (err) {
console.error(err.message);
res.status(500).send("Server error");
}
});
module.exports = router;
The route handler queries a PostgreSQL database using the pool object to select the firstname column from the useraccount table where the password column matches the password of the authenticated user. The req.user object is expected to contain an id property that represents the ID of the authenticated user in the table.
If the query is successful, the route handler sends a JSON response containing the firstname of the user as the body of the response. The res.json method sends a JSON response to the client and automatically sets the appropriate Content-Type header in the response.
The router object is exported from the module so that it can be mounted to the appropriate URL path in the main application file using the app.use method.
Middleware will be added to this implementation to make it fully functional, so we will not test it in this lecture.
Summary
In this lecture, we have learned how to implement the authentication routes in the Node.js application. In the next section, we will have a closer look at the JWT authentication and we will adjust our current implementation accordingly.