In the previous part I laid out my plans for a note-taking application: Pensify. In this follow-up, I will begin building the backend and user authentication system using Express.js and MongoDB.
As you would know from my previous posts, my usual preferred backend framework is Python-Django. However, Django is overly complex for this (relatively simple) app. You see, Django really excels when dealing with relational databases, complex user groups/permission and complex middleware... But for Pensify we really just need a light-weight REST framework to perform basic CRUD operations on a cloud database, and create/validate authentication tokens. Plus the option to deploy in a serverless environment might be nice too (we'll explore this in a later post...)!
So for our needs we look no further than the Express.js/MongoDB stack. The first step is to create our Express.js boilerplate. The resulting directory structure looks like this. I recommend this structure for clearly delineated backend/frontend apps, as it clearly shows the separate node environments.
middleware/- contains request middleware (which in the beginning will only consist of JWT auth token middleware)
models/- contains Mongoose schema definitions. More on Mongoose later...
routes/- defines the URL endpoints served by the backend
app.js- the main backend entry point
server.js- the server process, which creates a HTTP server at the specified port
The next step is to create a MongoDB Atlas cloud database. I'm opting for a cloud database for two reasons:
Creating a MongoDB Atlas cloud database is extremely simple. 5 minutes later we have a the database built and the backend access correctly configured. I find MongoDB a refreshing change from PostgreSQL. Mongo is a
NoSQL database, meaning it is non-tabular and does not rely as heavily on pre-defined table schemas and data types. For anyone who has wrangled with database schema conflicts in Django, this is a godsend!
I also recommend using Mongo Compass, which is a GUI tool for inspecting MongoDB data. It works for MongoDB Atlas cloud instances too. It allows easy inspection and modification of database entries, and is a perfect GUI tool to use while developing your API endpoints.
I want authentication for this app to be as simple as possible. This means: no signup, no passwords, no email resets, nada! We will only use authentication through Google's and Facebook's auth APIs. If you don't know already, this refers to those handy "Login with Google" buttons that are everywhere these days.
It is a huge relief being able to ignore password management. Password management is a nightmare at the best of times, and never wins you brownie-points. "Wow this app's password reset workflow is amaaazing", said no-one ... ever.
Furthermore,without the need for password reset workflow, we have no need for an email backend at all! Again, this is a godsend because ensuring 100% email deliverability is surprisingly convoluted and costly.
So, the first step in creating our Auth backend is to define the
user table schema in our Mongo database. We will follow the user object schema used by Google and Facebook:
** Sidenote on naming conventions: Not every user has a first and last name. Their cultural background determines how their name is formed. For example, if you’re from Latin America, the chances are that you have two last names, one from each parent. If you’re Chinese, your family name is first, personal name is last, and you always use them together.
- Amir Khademi on Medium
On the frontend login page, we want to have buttons for "Login with Google" and "Login with Facebook". These buttons will load an iFrame hosted by Google/Facebook respectively. After the user enters their Google/Facebook credentials, the backend will either i) create a new user record in our database, or ii) return the existing user object if one already exists.
For authentication we will use JSON Web Tokens (JWT). The frontend React application will attach the JWT as a header for all API request. The backend will accordingly check the validity of the JSON Web Token for all API requests. JWTs are simple and flexible and sufficiently secure for this type of application. But JWT still has some security gaps if implemented incorrectly, so make sure to follow a recent best-practice guide if you're implementing one yourself.
We then create three routes:
POST users/login-with-googleused to validate the Google
POST users/login-with-facebookused to validate the Facebook
GET users/mefetches the user object from the Pensify DB based on the JWT authentication header
And after a tiny bit of UI tinkering, we have a working login and signup React component!
So there you have it! In this short post we've initialised an Express.js backend, created and structured our MongoDB cloud database, and created endpoints to allow users to signup or login to our web-app using Google and Facebook. In the next part I'll be progressing the core functionality of the Pensify app: notes!