Introduction

Recently I had the need to set up a small internal static site that interacted with a few remote services. I figured this to be an excellent opportunity to learn a bit about Node.js. This will be a three-part series setting up a Node.js/Express/React application.

My template/learning project is available on GitHub. I am a beginner at both the language(s) and the stack. Suggestions for improvements are welcome.

To follow along at home you will need to be running something reasonably UNIXy that supports Docker such as OSX or Linux. All these tools are available in Windows as well, but I can't help you with that. You will also need gitcurldocker, Node.js and npm installed.

Initial project structure

First up is setting up the git repository. I have opted for the following file structure:

/                
  .gitignore     
  server/         Server package
    .eslintrc.json
    package.json  NPM server package description
    server/       Server sources
      index.js    The server application
    test/         Tests
       .eslintrc.json
       test-server.js

Preparations

First, create the git repository to house our things:

$ mkdir hello-node
$ cd hello-node
$ git init
Initialized empty Git repository in /.../hello-node/.git/
$ curl https://www.gitignore.io/api/node,osx,linux,jetbrains > .gitignore
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2407  100  2407    0     0   3974      0 --:--:-- --:--:-- --:--:--  3971
$ git add .gitignore
$ git commit -m "Initial commit"

The .gitignore file contains a list of patterns of files that should be ignored by git. Normally I like to create it by hand, but in the interest of learning new things I used the gitignore.io service to generate it.

The server

Next up is to create the actual server:

$ mkdir -p server/server
$ cd server

Fire up your favorite editor and create server/package.json:

{
  "name": "hello-node-server",
  "version": "1.0.0",
  "description": "Server for hello-node",
  "main": "server",
  "author": "Henrik Gustafsson",
  "license": "ISC",
  "files": [
    "server"
  ]
}

You can also use npm init to generate the template and modify to suit. The server will use Express, so install it:

$ npm install --save --save-bundle express

The --save and --save-bundle options adds the package to the "dependencies" and "bundledDependencies" sections of package.json.

Create server/server/index.js:

const express = require('express')

const app = express()
const port = 8080

app.get('/', (req, res) => res.send('Hello Node\n'))

app.listen(port, () => console.log('Server is running'))

// Export the app so that our tests can get at it later
module.exports = app

Add the start script to package.json:

{
  "name": "hello-node-server",
  "version": "1.0.0",
  "description": "Server for hello-node",
  "main": "server",
  "author": "Henrik Gustafsson",
  "license": "ISC",
  "files": [
    "server"
  ],
  "scripts": {
    "start": "node server"
  },
  "dependencies": {
    "express": "^4.14.0"
  },
  "bundledDependencies": [
    "express"
  ]
}

You should now have a functional server! To try it out, from the server/ directory, run npm start:

$ npm start
> hello-node-server@1.0.0 start /.../hello-node/server
> node server

Server is running

Try visiting http://localhost:8080/ in your browser or use curl:

$ curl http://localhost:8080/
Hello Node

Stop the server by pressing Ctrl-C.

Time to commit!

$ git add .
$ git commit -m "Add Hello Node server application"

Testing

For no particular reason, I guess Mocha and Chai with the chai-http plugin would do for testing:

$ npm install --save-dev mocha chai chai-http

Create server/test/test-server.js:

const chai = require('chai')
const chaiHttp = require('chai-http')
const server = require('../server')

chai.should()
chai.use(chaiHttp)

describe('Hello Node', () => {
  it('should return the string "Hello Node" on / GET', (done) => {
    chai.request(server)
      .get('/')
      .end((err, res) => {
        res.should.have.status(200)
        res.text.should.equal('Hello Node\n')
        done()
      })
  })
})

Add the test script to our package.json, which should now look a bit like this:

{
  ...
  "scripts": {
    "start": "node server",
    "test": "mocha"
  },
  ...
  "devDependencies": {
    "chai": "^3.5.0",
    "chai-http": "^3.0.0",
    "mocha": "^2.5.3"
  }
}

Run the test with npm test!

$ npm test
> hello-node-server@1.0.0 test /.../hello-node/server
> mocha

Server is running
  Hello Node
    ✓ should return the string "Hello Node" on / GET

  1 passing (40ms)

Fancy! Let's commit this too.

$ git add .
$ git commit -m "Add basic server test"

Strunk and White

Let's make sure our code meets some standard. Which one doesn't really matter as long as it's consistent. For this, I use ESLint configured with the StandardJS configuration.

$ npm install --save-dev eslint
...
$ node_modules/.bin/eslint --init
? How would you like to configure ESLint? Use a popular style guide
? Which style guide do you want to follow? Standard
? What format do you want your config file to be in? JSON
Successfully created .eslintrc.json file in /.../hello-node/server

Add the eslint command to the npm test script:

{
  ...
  "scripts": {
    "start": "node server",
    "test": "eslint . && mocha"
  },
  ...
  "devDependencies": {
    "chai": "^3.5.0",
    "chai-http": "^3.0.0",
    "eslint": "^3.2.0",
    "eslint-config-standard": "^5.3.5",
    "eslint-plugin-promise": "^2.0.0",
    "eslint-plugin-standard": "^2.0.0",
    "mocha": "^2.5.3"
  }
}

Running it will reveal problems in the server test:

$ npm test
> hello-node-server@1.0.0 test /.../hello-node/server
> eslint . && mocha

/.../hello-node/server/test/test-server.js
   8:1   error  'describe' is not defined     no-undef
   9:3   error  'it' is not defined           no-undef
  12:12  error  Expected error to be handled  handle-callback-err

✖ 3 problems (3 errors, 0 warnings)

npm ERR! Test failed.  See above for more details.

The first two issues is because the tests use symbols used by the testing framework, and the third is the lack of error handling. In general it's good practice, but in the tests we don't really want to pollute our code with such things.

Create a server/test/.eslintrc.json to override this:

{
    "rules": {
        "handle-callback-err": 0
    },
    "env": {
        "mocha": true
    }
}

Let's try again:

$ npm test
> hello-node-server@1.0.0 test /.../hello-node/server
> eslint . && mocha

Server is running
  Hello Node
    ✓ should return the string "Hello Node" on / GET

  1 passing (48ms)

This is good!

$ git add .
$ git commit -m "Add ESLint code analysis"

Conclusion

We now have a very minimal web server with testing, style checking and code analysis. This is a good starting point for future endeavors.

Next up is adding the client side of things.

$ git tag -a -m "Part 1" getting-started-1
$ git push

1 Comment