[Back for the future] Episode 1: Setting up a simple API
Let's fool around with Node/Express and expose an express API :)
Table of contents
DISCLAIMER: I am not a back-end developer, I specialize in front-end and specifically React. I'm however, excited about node.js and I'm going to acquire those new back-end skills through this new series. Even if the process of writing those blog posts usually requires extensive research and practice on my part for it to be useful and understandable, everything might not be perfect, and this is going to reflect my learning process. Any remarks about improvements/corrections are welcome.
Hello there! It's nice to see you back here :) (I mean, I'm not actually watching you right now, this is just a figure of speech, do not freak out!)
Today we do something different... today we're going back, while moving forward. I know. This is INSANE!
So, what are we going to do today? And what is backend anyways?
You may have read some of my other blog posts here. They would always be about creating some nice dynamic and responsive web app, using technologies like Javascript or Typescript, React, Redux, SASS etc.
As you probably know, those are front end technologies. Their purpose is to gather, order, style and sort-of "bring to life" data, and create an interactive experience for the end user in his favorite web browser. This is the tip of the iceberg. Although, depending on the architecture, the tip might be a very big part of said iceberg.
The thing is, for most of the apps you're going to build, you'll need at least to access a datasource to populate your front. And in many cases, you're going to need to persist some data in this datasource: when creating a user, when saving an order on a web shop, when marking a task as complete in a todo... Use Cases are plenty.
The thing is, since all of the intelligence is on the client side (Meaning, in the user's internet browser), and the data source is not, as it is on another server somewhere, it was necessary to find a way for this server to EXPOSE the data so the front end could access it.
But event if we could, we don't want anyone accessing our data on the server directly by querying the database. So we won't allow anyone to actually dig into our dataset with their dirty hands, but we need a way for someone outside the back end server to ask for stuff to happen. This is called an API.
You should have trust issues
Bear with me. Say, you're going to a store selling automobile spare parts. You know what you want, you got the reference and everything. But you won't be allowed to access the storage area. Nope. Because nobody wants to take the risk of you making a mess in the back. This is a private area. This is clean, tidy, ordered, and full of valuable things, and as the shop owner don't know you, and doesn't know if he can trust you, he cannot take that risk. So he has one of his employees check where the part is stored using the reference you provided, get it for you, and hand it to you over the counter. Well, that guy may not not it, but he is a living API.
An application programming interface (API) is a connection between computers or between computer programs. It is a type of software interface, offering a service to other pieces of software.[1] [...]
One purpose of APIs is to hide the internal details of how a system works, exposing only those parts a programmer will find useful and keeping them consistent even if the internal details later change. An API may be custom-built for a particular pair of systems, or it may be a shared standard allowing interoperability among many systems (SOURCE: Wikipedia)
Another side effect, as described above, is that it hides complexity from the one asking for some data. When you call an API, you don't want to know how it works, what it does, where the data comes from and how it has been processed. Nope. You just want a simple response corresponding to the API specifications. It means, this API could be rewritten with another language, a different database, but still you wouldn't have to change anything in your front end code. You ask for something, you get what you asked for, and whatever happens in the back, stays in the back.
Let's break the #1 rule and talk about backend :)
Before we dig into the specifics of how we setup and API, let's just define the main blocks of any backend architecture. We usually have at least some kind of web server and a database.
The web server is like a receptionist: he's there, behind his counter, and he LISTENS to whatever is said to him, and when getting a REQUEST, it takes the appropriate ACTION and return a RESPONSE to whoever asked for this information.
The database is where the data is stored. In our previous spare-parts-dealer example, it would be the warehouse, where all the parts are stored in an orderly fashion, so you know where to search for them.
So say, I'm a web server, I'm getting a request, I will find the right thing to do depending on the type of request. It might be to access some data from the database, update it, create some new data, or delete existing data (The famous CRUD )
To achieve this, we will need to define some endpoints: think of those as specific URLs leading to specific actions from the web server. For example, I might define a "user/:id" endpoint, and if, say, I access myBackendURL/user/12 from my browser, then the web server will know I'm asking for information about user with ID 12. It will then query the database (After making sure I have the rights to access this information, but we'll talk about that later) and retrieve this user information. It will then PROCESS it so it fits the specifications of the API endpoint, and it will return the user data to whoever asked for it.
Pretty cool hey? Less complexity for front end developers and easier to maintain and extend the backend without any side effects on the front end.
Back(end) to for the future
Why "for the future" you might ask? Well, because we're going to be using a technology here which is not the most widespread in the backend world: Node.js
It has spread for sure in the last years, but is still marginal: as of today, a little less than 2% of the websites use Node, while PHP is used in about 80% of websites with a backend. Because it is way older, mature, has great frameworks making your life easier when you know how to use them (like simfony), and it does the job for most needs.
Node.js however is more powerful for some types of apps due to the way it works:
How it works under-the-hood is pretty interesting. Compared to traditional web-serving techniques where each connection (request) spawns a new thread, taking up system RAM and eventually maxing-out at the amount of RAM available, Node.js operates on a single-thread, using non-blocking I/O calls, allowing it to support tens of thousands of concurrent connections (held in the event loop). learn more here
And also, the cool thing when you have a front-end background, is that it speaks YOUR LANGUAGE, because it uses V8, the same javascript engine used in chrome, but outside of a browser context. So you feel at home from the start, and also when you do backend, you're still maintaining and improving your javascript skills instead of switching languages.
Ok now, that was the last bit of context, let's actually dig in!
Setting up the environment.
I'm going to start by creating the web server locally and implement some endpoints.
So first, we need to create a new project. Create a folder wherever you want, cd to that folder in a terminal, and run:
npm init
You'll be asked to give some information about the new project. Below is mine. Notice the entry point is set to app.js.
{
"name": "expressapi101",
"version": "1.0.0",
"description": "Just a simple express server with some endpoints",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "clement benezech",
"license": "ISC"
}
Now, first thing first, we need to add a new module with NPM: Express.
npm install express
Wait Wait WAIT!" What is it?
We'll express is a web server based on node.js. Though you could actually create your own web server from scratch with node if you wanted (and I'm sure some people do for specific needs) in most cases it's a strong case a "reinventing the wheel" which will lead you to handling low level stuff you don't need to and adding unnecessary complexity
After that, we just need to create an app.js file in the root directory of the project, and we import express so that we can use its functionalities.
const express = require('express')
const app = express()
There we go, we have a web server. Remember, it needs to be listening for requests, so we add these instructions:
app.listen(8080, () => {
console.log("Serveur à l'écoute")
})
Defining and accessing an endpoint
Now that the server is listening, it means we'll be able to "catch" incoming requests and take the corresponding actions. Let's define a basic endpoint, to start with:
app.get('/test-api/user/:id', (req,res) => {
const id = parseInt(req.params.id)
const user = getUserById(id) //Returns a user name if ID exists
res.send(user)
})
Time for an explanation:
What we've done here, is define a GET route (So a route which purpose is to return specific data).
The first argument of app.get is the definition of the endpoint. We tell Express to do stuff whenever he receives a GET request on a specific path, here, /test-api/user/{someUserId}
Req is the content of the request, it is where we'll retrieve the value of the userId requested.
Res is the response this endpoint will send, this will be achieved by the Res.send('whatever you want to send back") command at the end.
Now that we have created the endpoint, we can start the server:
node app.js
If you want to test it yourself, then you'll need to add the code for the getUserById function in the app.js file before starting the server, like this:
/*Import and initialize express*/
const getUserById = (userId) => {
const users = [{id: 1, name: 'Dr Super User'},{id: 2, name: 'Ms Great User'}]
const user = users.find(user => user.id === userId )
return user ? user : "Sorry, this user does not exist"
}
/*definition of the endpoint*/
/*tell server to listen*/
Now that the server is up, let's get a specific user name by calling our new endpoint from a web browser:
This is it, the API is exposed and will return information about the requested user, if it exists.
i wanted to add a http status code so that if user was not found, I can send a 404 status code. Here is how you do it:
const getUserById = (userId) => {
const users = [{id: 1, name: 'Dr Super User'},{id: 2, name: 'Ms Great User'}]
const user = users.find(user => user.id === userId )
return {value: user ? user : "Sorry, this user does not exist", resStatusCode : user ? 200 : 404}
}
//User Endpoint: based on a provided ID, will provide information about the user
app.get('/test-api/user/:id', (req,res) => {
console.log(req.params)
const id = parseInt(req.params.id)
const user = getUserById(id)
res.status(user.resStatusCode).send(user.value)
})
What is next in this series?
Now that we have this working api that does nothing useful but will be the basis for the next episodes (Yes, this is going to be a series ; ) where we'll build an API to extend the functionalities of my React/Redux task management app by:
- Creating a database to store users and tasks.
- Creating a set of CRUD endpoints to be able to read, delete, update, or add tasks from the database.
- Manage user creation and authentication.
- deploying the API on planetHoster (the world plan)
If you haven't seen the app yet, go here and have a look :) : clementbenezech.hashnode.dev/a-cool-react-t..
I hope you enjoyed this! Please let me know if you have any questions, and see you soon for episode 2! ;)