Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Project structure

Express does not create folders for you: everything can live in one file. But once there are more than a dozen routes, such a file is hard to read. So a project is split into folders, and each part has its own role.

The blog structure

This is how the project will look by the middle of the course:

blog/
├── package.json
├── .env                  settings, not committed to Git
├── src/
│   ├── app.js            creates the application and wires everything up
│   ├── server.js         starts the server
│   ├── routes/
│   │   └── posts.js      addresses of the posts section
│   ├── controllers/
│   │   └── postsController.js   handler functions
│   ├── models/
│   │   └── post.js       working with data
│   └── middleware/
│       └── requireAuth.js
├── views/                HTML page templates
│   └── posts/
│       └── index.ejs
├── public/               CSS, images, scripts for the browser
│   └── css/style.css
└── test/                 automated tests

The views/ and public/ folders are in the root: by default Express looks for templates in views/ relative to the folder the server is started from.

Folder roles

Folder or fileContents
src/app.jsApplication setup: middleware, routes, error handling
src/server.jsOnly starting: app.listen()
src/routes/Which address is handled by which function
src/controllers/What to do with a request: get data, respond
src/models/Reading and writing data
views/Page templates
public/Files the browser receives as they are

Splitting app.js and server.js

Create the file src/app.js, move the application into it and export it:

// src/app.js
import express from 'express';
 
const app = express();
 
app.get('/', (req, res) => {
  res.send('Hello from the blog!');
});
 
export default app;

And src/server.js now only imports the application and starts it:

// src/server.js
import app from './app.js';
 
const PORT = 3000;
 
app.listen(PORT, () => {
  console.log(`Server started: http://localhost:${PORT}`);
});

npm run dev works as before: http://localhost:3000 shows the same greeting. But now the application from app.js can be imported in tests without starting a real server — this will come in handy in the testing lesson.

How the files connect

A request travels from top to bottom: server.js starts the application from app.js, app.js passes the request to the right file in routes/, the route calls a function from controllers/, the controller takes data from models/ and returns a page built from a template in views/. Each link of the chain gets its own lesson.

Common mistake

In ES modules the path to your own file is written in full, with the extension: import app from './app.js'. Writing './app' without .js works in some bundlers, but Node.js stops with the error ERR_MODULE_NOT_FOUND.

Thanks for reading!

Donate to the site — we work hard for you.