Getting Started with Express JS for the Impatient

A quick code along with lots of links

Do you know another framework like Rails or Django but want to see what it’s like using fullstack js with Express? Great, you’re the target audience. We’ll cover a lot, but feel free to take your time and please check the links as we go.

Overview

The Simplest App Explaining middleware Serving Static Assets Using Routers and Routes

The goal is to have a static homepage and talk to an API. I’m assuming you have node installed and JS experience, so we won’t deal with that.

0. File Structure and Install

Make these files:

public/

└ index.html

└ styles.css

src/

└ index.js

└ routes/

└ index.js

└ users/

└ get.js

└ list.js

└ index.js

package.json

Leave them blank, but copy this into your package.json :

{

"name": "express-practice",

"version": "0.0.0",

"private": true,

"scripts": {

"start": "nodemon src/server.js"

},

"dependencies": {

"express": "~4.16.1"

},

"devDependencies": {

"nodemon": "^1.19.4"

}

}

Run npm install and you’re set! (nodemon docs).

1. The Simplest App

Wanna code an express app in 3 seconds? Copy this:

/* src/server.js */

const express = require('express'); const app = express(); /* home route */

app.get('/', (req, res, next) => {

res.send('hello there');

});

const port = process.env.PORT || 4321;

const host = '0.0.0.0';

app.listen(port, host, () => {

console.log(`

}); /* start the app */const.env.|| 4321;const= '0.0.0.0';app.listen(, () => {.log(` http://localhost:${port}`); });

Run npm start and you should see text on the main page. Now that it runs, let’s explain it:

express and app

We require express , which returns an object. If we call it, a top leve function returns an instance of an express application. 99% of the time it’s called app , so call it that unless you want to be difficult. This app is where we will put all our stuff, like our (temporary) home route.

.get and Route handlers

We use the app.get method, which takes a path and a middleware function. There are all the http verbs, so .post , .delete , etc, but we’ll keep it simple and only use .get . We’re also only using one middleware callback, but it’s possible to use multiple. Here’s ours again:

(request, response, next) => {

response.send('hello there');

});

It will always take those 3 arguments, the request object, the response object, and the next function (more on that later). The request object holds useful stuff like your params; and your response object houses the response methods. You'll use res.send() the most, since it can send JSON. Here we’re simply sending text to the client.

Starting the app

Finally, we cap it off by telling our application to listen to a port environment variable, on the host ‘0.0.0.0’ . You don’t have to include the host, it defaults to localhost but ‘0.0.0.0’ prevents Docker issues. You can also give it a callback to run when it starts. I write the url so I can cmd+click it from iterm.

There you have it! That’s your first express app. But you can’t use Express without knowing about middleware, so let’s talk more about that.

2. Explaining middleware

Middleware is Expresses’ answer to modularity. You essentially tell your app to use a series of functions in order to do things like logging or modifying requests. At the end of each function, it calls the next function, always called next . Eventually, the request will proceed to the actual route (which itself is a middleware function) and send a response to the client. Middleware must end by either calling the next callback or sending a response to the client.

Adding some middleware:

We’re going to make it so that every time we hit a route, we will log the path to the node console. And just for demonstration, I’m going to add a property to the request object that we can access later:

/* src/server.js */

const express = require('express'); const app = express(); /* new logging middleware */

app.use((req, res, next) => {

console.log(`hit: ${req.originalUrl}`);

req.test = 'wow';

next();

}); /* home route */

app.get('/', (req, res, next) => {

console.log('req.test: ', req.test);

res.send('hello there');

}); // ...rest of app...

By calling app.use we register our middleware to the app. We call the next function so requests will proceed to their routes after we log. Notice how we aren’t giving a route argument, that means this middleware will apply to every single route on the app. But that’s not always needed, so sometimes you’ll need a route argument:

app.use('/specific', (req, res, next) => {

The most important thing to remember is that order matters. Middleware is processed in order. If you were to write that below the home route, the res.send would end the chain and our logger would never get called.

app.<httpMethod> vs app.use

You’ll notice that app.use behaves a lot like app.get , so what’s the difference? Read this stackoverflow question for a nice explanation, but TL;DR, the different methods keep things organized and intentions clear.

3. Serving Static Assets

Before we add some real middleware, copy this to your /public/index.html :

<!DOCTYPE html>

<head>

<link

rel="stylesheet"

type="text/css"

href="styles.css"

>

<title>Hi</title>

</head> <body>

<h1>Hello world</h1>

</body>

</html>

And then add this to /public/styles.css :

h1 {

color: red;

}

Breathtaking in its simplicity. Alright, now we’re going to swap our original home route for some static middleware:

// src/server.js

const path = require('path');

const express = require('express');

const app = express(); /*

unchanged logger

*/ /* set up static files router

* path and dirname links

*/

app.use(

express.static(

path.join(

__dirname,

'..',

'public',

)

)

); usestaticjoinpublic /*

unchanged start app

*/

Forgive the wonky spacing, I’m trying to make this work on mobile, but in full screen the static function looks like:

app.use(express.static(path.join(__dirname, '..', 'public')));

The key here is express.static , a built in middleware function that lets us host assets. All it takes is an optional mount path, a string for a directory to check, and an options object. Usually, the default options are fine. We aren’t providing a mount path, so this will host our static files off the root ‘/public’ , but you don’t have to do this. And you can have more than one, check the docks for more info.

A brief explanation of .static

A client request to ‘/’ would look for the default file in the public folder and serve ‘/public/index.html’ ( index is the default filename and .html the default extension). Then the html page’s request for ‘styles.css’ would look in the public folder and find /public/styles.css .

However, a request like ‘/users’ is an api route and not in public . So, static would first check for ‘/public/users.html’ , find nothing, and move on to check the regular api routes.

4. Using Routers and Routes

For the final piece of this project, we’ll set up an API using Routers with express.Router . While we have seen how to add routes to our main application with app.get , there is another way to keep things more organized.

The idea is that each sub route, like /users , would be hosted on its own router . We would add all relevant routes to it, and then call app.use :

app.use('/users', userRouter);

Now our main application switches to the router’s sub routes when the request url hits the base case. With the theory out of the way, let’s look at a real world pattern for routers. We’ll go top down, from individual routes down to the server file.

src/routes/users/get.js and list.js

These two are the actual user routes. Both of these files export functions, which take a router as an argument. All the functions do is add their route to the given router . We are essentially doing app.get , but swapping out the app with a router .

Notice the paths are /:id , and / , and not /users/:id and /users . With Express, the base route, /users , is added at the application level so it can be shared between routes on the router. Also notice in get.js there’s a dynamic route ‘:id’ param and a query param.

/* /src/routes/users/get.js */

// get a single user module.exports = (router) => {

router.get('/:id', (req, res) => {

res.send({

id: req.params.id,

name: req.query.name,

});

});

};

---------------------------------

/* src/routes/users/list.js */

// list all users module.exports = (router) => {

router.get('/', (req, res) => {

res.send([

{

id: 1,

name: 'tom',

},

]);

});

};

src/routes/users/index.js

To keep things tidy, we’re going to create the user router inside our /users/index.js file, and then export that:

/* src/routes/users/index.js */

// add the routes to the router const express = require('express');

const router = express.Router(); /* user only middleware */

router.use((req, res, next) => {

console.log('User middleware');

next();

}); require('./list')(router);

require('./get')(router); module.exports = router;

The require(x)(y) looks weird, but in the real world you’ll add a lot of files and requiring and calling like this is bulky:

const route = require(‘./route’) ;

route(router);

This is also the file where you would add router specific middleware with router.use . Just remember to put middleware before you add your routes, the order matters!

src/routes/index

In the index.js of the routes folder, we will export a function that takes an app, and then adds all the routers to it. We only have one in this case, but you would list each additional router as your application grows. This is finally where we add the base route /users :

/* src/routes/index.js */

// add the routers to an app const addAllRoutes = (app) => {

app.use(

'/users',

require('./users')

);

}; module.exports = addAllRoutes;

Last, we add the function to our src/server.js :

const path = require('path');

const express = require('express');

const addAllRoutes = require('./routes');

// ...unchanged app /* register all routes */

addAllRoutes(app); /* start the app */

const port = process.env.PORT || 4321;

// ......unchanged app

Ta da! That’s our finished project! I hope that this gives you a decent starting point. Some immediate next steps would be incoporating a create-react-app instead of an index.html, and check out passport.js for auth!

happy coding everyone,

mike