In the previous part of this tutorial, How to Build a Login/Register App with the MERN Stack (Part 2): Setting Up the Database, we created a database and wrote some code that makes it much easier to use our database all over our app. In this part we will write code for our API endpoints, using Express, that we will use on client side.
Defining API Endpoints
Let’s create an entry point (file) for our Express app. I’ll call mine index.js
. Now let’s define paths for the API endpoints.
Switch to your terminal and run npm run dev
to start up this app. And as you can see, we now have a basic Express app listening on port 5000
if there is no environment variable called PORT
. We did so because the deployment platform will use the PORT
variable to specify which port the app should use.
NOTE: Do not forget to correctly set the name of the file that nodemon
runs from the scripts section, specifically the dev
script.
Setting Up Middleware
Before we continue working on our API endpoints, let’s first require
and set up some middleware to help us complete the code for the API endpoints.
On line 9
we create a new instance of the User
class we wrote earlier to be able to use the register
and login
methods defined in it.
We create a new session store for the express-session
middleware, on line 10
, to make it able to communicate with the database.
On lines 12
and 13
we use body-parser
to parse bodies of incoming requests and get data out of them.
On lines 15-20
we make use of the cors
middleware to enable and control CORS
. The origin
option specifies which domains are allowed to use our API, later we will add the production URL to this list. And the credentials
option sets the Access-Control-Allow-Credentials
header to true
, which allows the client to send cookies along with the request.
On lines 21-28
we use the express-session
middleware, and use the value of an environment variable, SESSION_KEY
, as the secret to be used to sign the cookie. We also set the store
option to be the one we created earlier on line 10
.
NOTE: You should add these lines to the top of the index.js
file. Do not forget to add SESSION_KEY
to your .env
file.
The ‘/api/users/register’ Endpoint
This endpoint is very simple. First, we extract the username, password, and email from the body of the incoming
POST
request. Then we use user.register()
function, that takes in username, password, and email and returns an HTTP status code, to register that user. We store the returned HTTP status code in a variable called result
. Finally, we send that status code back to the client.
The ‘/api/users/login’ Endpoint
This endpoint is a little bit similar to the ‘register‘ one. First, we extract the username and password from the request body. Then, we use
user.login()
function, that takes in username and password and returns an HTTP status code and a user
object representing the authenticated user, to log the user in. Afterwards, we put the id, username, and email of authenticated use in a variable called loggedInUser
. And if the status code is 204
we put that user in the session data, as shown on line 11
. Finally, we send back a JSON
response with the authenticated user data and status code.
The ‘/api/users/logout’ Endpoint
Because we use sessions to identify logged in users, destroying the session of a client that makes request to the
logout
endpoint is enough to mark them as logged out. The sesssion.destroy()
function takes in a callback to execute after destroying the session. In this case, we want to send a status code of 200 OK
as response to the client.
The ‘/api/users/currentUser’ Endpoint
We will use this endpoint later, on client side, to know whether user is logged in or not. It simply sends back a JSON
response containing logged in user data if there is a user
in the session. If not, then it sends a JSON
response with loggedIn
set to false
.
Custom Middleware
Now, we will make a couple of middleware, requireLoggedin
and requireNotLoggedin
, to control who can access our endpoints.
requireLoggedin
We basically check to see if there is a user in the session, if not then we send back a 403 FORBIDDEN
response. If there is a user, we continue on to the next handler by calling next()
. BTW, I put these lines in a file called requireLoggedin.js
in a folder called middlewares
.
requireNotLoggedin
This is basically the same as requireLoggedin
. The only difference is that we flipped the if
statement in this one.
Making Use of Them
After, bringing them in, we use them as middleware on our endpoints. We use requireNotLoggedin
on '/api/users/register'
and '/api/users/login'
to prevent logged in users from using these endpoints. We also use requireLoggedin
on '/api/users/logout'
to prevent not logged in users from using this endpoint.
Conclusion
We used some external middleware to set up and secure our Express app. Then, we made some endpoints for our API, so that we can use them on client side to communicate with the server. Finally, we created and used our custom middleware to protect our API endpoints against misuse. In the next part (or two) we will work on frontend and client side using React.
NOTE: You can test your API using Postman. You can find all parts of this tutorial here and code here.