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 git, curl, docker, 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"
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