A modern JS app (I): technology overview and client setup

A modern React / Node.js application

Part 1: Basic technologies overview

Backend

As stated in the title of this post, we’ll use Node.js to run our backend code. For our data-store, we’ll pick Mongo. Our frontend will consume our API via GraphQL: as an alternative to the traditional REST API, it relies not on URLs for accessing resources but customizable queries which allow us to extract only the data we want from our backend.

While we could have chosen a relational data-store like Postgres for our database, we’ll stick with Mongo as it’s very javascript friendly and has virtually nil set-up time and is extremely flexible.

Frontend

Let’s use React and Redux, as the pair provides a simple way to reason about the data-flow and state inside our app.

Vue.js is another library that’s enjoying a ton of use, and some people swear by it over React. But I’m a loyal soldier (and I’m not a big fan of the Angular-like html-attribute-macros Vue employs, but to each their own!).

We’ll use the amazing create-react-app package to supercharge our setup time. It provides tons of boilerplate we’d otherwise have to write ourselves to get stuff like Babel and Webpack working. Run the following:

npm i -g create-react-app

create-react-app client

After the text stops flowing, drop into our newly created client folder and run npm start. Bam! We’re off and running. Visit http://localhost:3000/ in your browser and you should see the React logo spinning around.

You really should read all of the documentation on create-react-app’s GitHub. It provides detailed walkthroughs to just about anything you could think to add or change in the default application.

Additionally, if you haven’t, please, please, please, read the Redux documentation. Basically, this post will provide some of the “how,” but the documentation provides that, plus the “why” – which is arguably more important!

Stop the server with ctrl+c and we’ll also add Redux and its bindings with React. To facilitate asynchronous actions, we’ll use the awesome Redux Saga library, which is an alternative to the more traditional Redux Thunk middleware plugin.

npm i --save redux react-redux redux-saga

Additionally, we’ll throw in a UI framework called Bulma. It’s strictly CSS; no javascript, which is comforting. It’s got great documentation and lots of stars on Github.

npm i --save bulma

Testing, testing, 1-2-3

Of course, we also need a way to easily test the code we’re writing. For that, we’ll use the Jest library. It works well with React, and in fact, it comes bundled with create-react-app’s output. Try running npm test (or npm t). It invokes the Jest testrunner, which executes our tests. See App.test.js for an example. It also would work as App.spec.js or __tests__/*.js. More on Jest later.

Project-wide .gitignore

create-react-app adds a nice default .gitignore file to our client directory. But ideally we want the .gitignore to apply to the entire project structure. Let’s hoist it out of client and into our top-level directory:

mv client/.gitignore .

Additionally, we’ll make a change to it to ignore all node_modules directories. Here’s the diff of the change we want:

+ node_modules/
- /node_modules

Moving the slash to the end will force git to ignore all module folders, instead of only ones inside the same directory as the .gitignore file.

Productivity libraries

Some productivity tools that we’ll use are Flow, ESLint, Ramda.js, and Immutable.js.

With Flow, we can augment our javascript code with type annotations, which the Flow typechecker can use to help us avoid silly errors (invalid property access, incorrect type assumptions, typos, etc.) that would otherwise manifest at runtime when writing regular javascript.

ESLint is a tool that warns us if we stray from our style-guidelines. It helps us maintain a consistent coding style by enforcing a customizable list of rules.

npm i --save-dev flow-bin eslint

Let’s configure ESLint. Run the following:

./node_modules/.bin/eslint --init

It will bring up an interactive prompt that will ask how you want to set up your styling. You can choose to have it ask you questions about your style or just run with a popular style-guide such as AirBnB or Google’s. The end result is a new file called .eslint, which can be JS, YAML, or JSON. Once that’s done, just run the follow to lint our code:

./node_modules/.bin/eslint src/index.js

You’ll notice that it’s marking variables as unused even though we’re using them as JSX:

1:8    error  'React' is defined but never used  no-unused-vars
12:11  error  'App' is assigned a value but never used  no-unused-vars

To alleviate this, add the following two rules to the "rules" property in our .eslintrc file:

"react/jsx-uses-react": 1,
"react/jsx-uses-vars": 1

With these rules, the linter will no longer incorrectly complain about unused variables.

We’ll also add the following line to our .eslintrc.* file under the parserOptions key:

"ecmaVersion": 8

This is so we can use the awesome new async/await keywords without the linter yelling at us.

Next, add this line to the top-level of .eslintrc.*:

"parser": "babel-eslint"

Now, let’s add a lint command to our package.json’s scripts property’:

"lint": "./node_modules/.bin/eslint src/*.js || true"

Now, to lint our app, we simply run npm run lint. The || true suppresses the typical npm error boilerplate (go ahead, try running npm run lint with and without the || true present.)

You’re probably getting lots of “unnecessary semi-colon” warnings. This brings up the age-old question: to use semi-colons or not? We will choose the not option. Some people like them, some people don’t. It’s purely preference, but the important thing is that we pick a rule and stick with it.

If you do want to use semi-colons, simply switch the "never" to "always" in your .eslintrc.* for the "semi" rule.

Whatever you choose, take this opportunity to add/remove the semi-colons where the Linter says they should/shouldn’t be.

We’re almost there! Now, Lint complains because our App.test.js contains the it keyword which has not been defined, as it is a global provided by Jest. To fix this, add the following to .eslintrc.*’s env key:

"jest": true

Whew! ESLint definitely adds some setup overhead but is invaluable in helping maintain clean, consistent code. We’ll re-visit it periodically to update our rules.

Function junction

I’m a big fan of the functional programming style and while Functors, Applicatives, and Monads are beyond the scope of this post, it’s possible to write javascript in a clean, concise, functional style and that’s what we’ll aim to do here.

Object-oriented programming was king for a long time, but there is currently a sea change toward the functional paradigm.

Javascript has its warts, but the ECMAScript spec has really made for a fun and expressive language. It’s no Haskell, but combined with a library or two, Javascript can truly be a very capable and functional-style language. We’ll use Ramda.js to help us with this. It provides many helpful utility functions that embrace and encourage functional code as each method is curried (i.e., you can partially apply it).

Another great library is lodash/fp.

Finally, we’ll use Immutable.js which is a library of collections that play well with React and Redux because they are… well, immutable. One way this helps is that it allows for super-fast reference equality checks (think === vs ==) instead of deep comparisons to determine whether or not a component should be re-rendered. It also allows for cleaner, more readable code in our Redux reducers.

npm i --save immutable ramda

The new hotness

We want to utilize hot-reloading so that our app automatically updates when we make code changes.

You might say, “but, the app is already reloading when I change it!” That’s true, but it completely refreshes the page. This is fine for when our app is small and simple, but for a more complex application, a refresh can cause us to lose our place – or worse – our state. Hot-reloading simply updates the changed component on the page, without a full page refresh, thus maintaining our state.

To enable hot-reloading, all we have to do is add some code to the bottom of our src/index.js file:

if (module.hot) {
  module.hot.accept('./App', () => {
    const NextApp = require('./App').default
    ReactDOM.render(
      <NextApp />,
      document.getElementById('root')
    )
  })
}

After adding this code, change the code in App.js or App.css and watch the magic happen.

Credit to superhighfives and this post for that one.

‘Til next time!

We’re done with our initial setup. In Part 2 of this post, we’ll get our Node server up and running and make our first API request.