By now I have written a couple of express.js apps. After a while a certain pattern emerged, how I write my apps, which I want to share with you.

This post is more or less a brain dump.

Directory structure

The directory structure looks like this:

apps/
  auth/
    controller.js
    router.js
  users/
    router.js
middlewares/
  error.js
public/
  scripts/
    fallback.js
  stylesheets/
    style.css
  vendor/
    jquery-3.2.1.min.js
routes/
  errors.js
  index.js
views/
  partials/
    login.ejs
    navbar.ejs
    profile.ejs
  base.ejs
  error.ejs

Express files

app.js

The express app is called app.js and looks similiar to this:

const path = require('path')

const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser')
const express = require('express')
const logger = require('morgan')

const auth = require('./apps/auth/router')
const errorMiddleware = require('./middlewares/error')
const errors = require('./routes/errors')
const index = require('./routes')
const users = require('./apps/users/router')

const router = express.Router()
const app = express()

// view engine setup
app.set('view engine', 'ejs')
app.set('views', path.join(__dirname, 'views')

// middleware
app.use(logger('dev'))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))
app.use(cookieParser())

app.use(express.static(path.join(__dirname, 'public'))
app.use('/', index)
app.use('/auth', auth)
app.use('/profile', users)

app.use(router)

// catch 404 and forward to error handler
app.use(errorMiddleware)


// error handler
app.use(errors)

module.exports = app

Router

Let’s go more into details. An router looks like this:

const express = require('express')
const router = express.Router()

const controller = require('./controller')

router.get('/', controller.root)
router.get('/login', controller.login)
router.get('/logout', controller.logout)

module.exports = router

So the only task of it is to pass calls to a route to the respective method of a controller.

The general routes object is written like this:

const express = require('express')
const router = express.Router()

/* GET profile page. */
router.get('/', (req, res) => {
  res.redirect('/profile')
})

module.exports = router

The error route is responsible for catching internal server errors:

const httpStatus = require('http-status')

function errorHandler (err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message
  res.locals.error = req.app.get('env') === 'development' ? err : {}

  // render the error page
  res.status(err.status || httpStatus.INTERNAL_SERVER_ERROR)
  res.render('error')
}

module.exports = errorHandler

Controllers

Let’s look at the one for auth:

function root (req, res) {
  return res.redirect('/auth/login')
}

function login (req, res) {
  const context = {
    partial: 'partials/login',
    title: 'Login | My App',
    context: {
      user: req.user
    }
  }
  return res.render('base', context)
}

function logout (req, res) {
  req.logout()
  return res.redirect('/')
}

module.exports = {
  login: login,
  logout: logout,
  root: root
}

The users controller looks similiar with respect to the structure:

function get (req, res) {
  if (!req.user) {
    return res.redirect('/auth/login')
  }

  const context = {
    title: 'My Profile | My App',
    partial: 'partials/profile',
    context: {
      user: req.user
    }
  }
  return res.render('base', context)
}

module.exports = {
  get: get
}

Middleware

Let’s shortly look at a middleware:

const httpStatus = require('http-status')

function errorMiddleware (req, res, next) {
  const err = new Error('Not Found')
  err.status = httpStatus.NOT_FOUND
  next(err)
}

module.exports = errorMiddleware

Views

By now I have some more adjustments on how I’d write my templates. More on that in another article.

Here’s the base.ejs file (with HTML syntax highlight, because rouge has no support for embedded JS yet).

<!DOCTYPE html>
<html>
  <head>
    <meta charset='utf-8' />
    <title><%= title %></title>
    <meta name='viewport' content='width=device-width, initial-scale=1' />
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <%- include('partials/navbar', { user: context.user }) %>
    <%- include(partial, { context: context }) %>
    <script src='/scripts/fallback.js' type='text/javascript'></script>
  </body>
</html>

You may be wondering, what the partial does. Well, it’s my way to have a basic template and inject an arbitrary partial for the content I want to render. But first a look at the navbar partial.

<nav>
  <a href='/'>
    Home
  </a>

  <h4>
    <% if (user) { %>
      <a href='/logout'><%= user.displayName %> logout</a>
    <% } else { %>
      <a href='/auth/login'>Login</a>
    <% } %>
  </h4>
</nav>

So here we have hardcoded routes. Something I may want to rethink.

The login view partial:

<h1>Login</h1>

<p>
  In my demo I had several login providers to pick from here.
</p>

The profile partial is pretty simple:

<div>
  <h1>Hello, <%= context.user.displayName %>!</h1>
</div>

What I need to investigate is how to make those views translateable.

Conclusion

And that’s it! If I write another express app I’ll take another look, whether this instruction is still up-to-date.

Thanks for reading!