Bootstrapping a modern full-stack JavaScript Application using Express & React

The Why

There are several reasons to pick a full-stack JavaScript dev environment in 2019. First off, JavaScript is ubiquitous, it’s got pretty much all the big names in web software aligned behind it: Facebook, Google & Microsoft all have investments in both server and client-side JavaScript frameworks. Who would have thought that we would live in a world where we’ll write code in an editor built by Microsoft, in a framework developed by Facebook, and using design systems inspired by Google. Frankly, it truly is the golden age of client-side web development.

The story on the server-side for JavaScript doesn’t look that rosy, but NodeJS has come a long, long way from its initial demo, and for most web applications that serve an API backend, it’s a solid choice, especially combined with web frameworks such as Express.

And on both ends, JavaScript in general has evolved a lot as a language with first-class support for classes, async/await, spread operators, and string literals. In fact, even coming from a developer-friendly language like Ruby, JavaScript feels nice now, not like five years ago. And some features (like type hinting and automatic formatting) enabled by projects like Babel and Prettier make editing JavaScript code feel like living in the future. In fact, if you haven’t edited JavaScript in VSCode before, do try it out.

The Choices

Well, you’re convinced and ready to jump into full-stack JavaScript. How do you do it? If you come from the Ruby/Rails world like I do, you’re in for a rude shock. Rails picks a lot of default choices for you: there is an emphasis on convention over configuration, and all its moving parts are designed (& tested) to work well together. JavaScript is… not like that. It has a ton of developers actively working on several libraries every day, leading to good examples of what not to do when including dependencies.

So in fact the first step in building full-stack JavaScript is deciding what goes into the stack. At Lucideus, for a new product called Safe Assure, here’s how we made those technology decisions. Each product and technology chosen ideally has to be:

  • mature technology (developed for at least 2 years), as part of the principle to use boring technology.
  • actively maintained, and have updates within the last month, and a history of frequent updates without the developers taking off on long vacations.
  • ideally sponsored by a large organisation that won’t die tomorrow (Google & the like preferred, but Airbnb, Palantir et. al. are acceptable), and,
  • have developer mindshare, as measured by a healthy number of stars on Github.

Based on these criteria, here’s what we chose for the frontend:

& for the backend web framework, we chose to build a simple Express API endpoint. There are of course a lot of default choices (in so much as the community as accepted them as the de-facto default) that come with choosing to build a JavaScript SPA that require configuration as well, amongst them:

  • Webpack (which is used to bundle & minify JavaScript, CSS, and images)
  • & how to deploy this application, for which we chose to build a Docker image that combines the server & client parts of the app, for eventual deployment to a managed Docker cluster.

Note: in future versions, we might explore building an auto-generated and configured GraphQL backend instead of the Express app, probably using Hasura’s excellent frontend for Postgres, but that’s a post for another day.

The Application Structure

Let’s start with a sane application structure. We’re essentially building two apps: a frontend SPA client app, and a backend API that the client consumes to display data from a database. So let’s make:

  • src/
    • client/
    • server/

We also need tests, so a folder for that. Note: we prefer integration tests, so these are independent of the client and server, and indeed will only work if both components are present and running:

  • src/
    • client/
    • server/
  • test/

Eventually when we deploy the app for distribution, we also need a dist/ folder, so let’s create that too:

  • src/
    • client/
    • server/
  • test/
  • dist/

Inside the client/ folder, we start with a simple SPA application:

  • client/
    • images/
    • components/
      • All React components of our SPA
      • App.js
        • The main App/ component.
    • index.html
      • The HTML entry-point of the client-side app, this is what webpack injects assets into & loads in the browser.
    • index.js
      • The JavaScript entry-point of the client-side app.
    • styles.css
      • Generic styles and imports that remain outside of components. This is usually used only for @imports & special things like @tailwind directives.

Note: as our app expands, we might logically create more folders, for e.g. sub-components inside the components/ folder by business theme or any other organisational unit we might choose.

The server folder uses the folder structure generated by the excellent express-generator. Note: we choose to configure it with the EJS view template (i.e. express -v ejs) . It looks like this:

  • server/
    • bin/www
      • The JavaScript entry point of the server application. Makes sure the ports configured are available and loads the app.
    • public/
      • fonts/
      • images/
      • Both these folders will be empty when developing, but we’ll use this in the deployment step to link the client and server applications correctly.
    • routes/
      • api/
        • <endpoint>.js
        • This is the /api/* endpoints that you would configure. Each file would be a separate route.
      • index.js
        • This is the default route that will just serve views/index.ejs. We’ll use this during the deployment step.
    • views/
      • error.ejs
        • Used when there is a 40*, 50* error in the application.
      • index.ejs
        • Copied in by the deployment process to load the client-side SPA.
    • app.js
      • This is called by boot/www during the startup process and sets up the main Express app. You also configure the API routes here.

That’s it for now! In Part 2 of this article, we’ll look at configuring Webpack correctly to minify & remove un-used CSS, and process images and fonts.

Leave a Reply

Create a website or blog at

%d bloggers like this: