-
Notifications
You must be signed in to change notification settings - Fork 88
Tasks
Solution branch: sln/00-api-server-v2
-
Make sure you are running the LTS version of Node.js (as of May 2017 the LTS version was v6.10.3)
-
Create package.json:
$ npm init
-
Add /node_modules and /dist to .gitignore
Babel transpiles Javascript ES5 to ES6. Node v6 has 99% support for ES6, except for ES6 modules. We will use Babel to only transpile our ES6 modules (i.e. import and export statements).
http://babeljs.io/docs/plugins/transform-es2015-modules-commonjs/
-
Install babel-node:
$ npm install babel-cli --save
-
Install babel modules plugin
$ npm install babel-plugin-transform-es2015-modules-commonjs --save
-
Create .babelrc file and configure babel to only use the modules plugin
{
"plugins": ["transform-es2015-modules-commonjs"]
}
- Create src/test-babel.js with code:
import path from 'path';
const directories = ['/foo', 'bar', 'baz/abc', 'qwerty', '..'];
const result = path.join(...directories);
console.log(result);
- Setup babel in package.json:
"scripts": {
"build": "rm -Rf dist && babel src -d dist"
}
-
Build it:
$ npm run build
-
Test it:
$ node dist/test-babel.js // should work
$ node src/test-babel.js // should fail
- Create simple test server in src/test-server.js
import http from 'http';
const port = process.env.PORT || 4000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello React Class\n');
});
server.listen(port, () => {
console.log(`Server running at port ${port}.`);
});
-
Build it:
$ npm run build
-
Run the test server using node:
$ node dist/test-server.js
-
Test it using your browser.
babel-node will compile ES6 code before running it with node. It will help us avoid having to run build all the time. babel-node comes with babel-cli install.
- Add "babel-node" to package.json:
"scripts": {
"babel-node": "babel-node"
}
- Test it:
$ npm run babel-node src/test-server.js
It monitors file changes and automatically reloads your server.
-
Install it locally:
$ npm install nodemon --save-dev
-
In package.json:
"scripts": {
"nodemon": "nodemon --exec npm run babel-node"
}
-
Test it:
$ npm run nodemon src/test-server.js
-
Make a change to test-server.js and see how it restarts, then hit server with your browser.
A code quality tool, checks Javascript syntax.
http://eslint.org/docs/rules/
- Install it and configure it:
$ npm install eslint --save-dev
$ ./node_modules/.bin/eslint --init
? How would you like to configure ESLint? Answer questions about your style
? Are you using ECMAScript 6 features? Yes
? Are you using ES6 modules? Yes
? Where will your code run? Browser, Node
? Do you use CommonJS? No
? Do you use JSX? Yes
? Do you use React Yes
? What style of indentation do you use? Tabs
? What quotes do you use for strings? Single
? What line endings do you use? Unix
? Do you require semicolons? Yes
? What format do you want your config file to be in? JSON
-
Test it:
$ ./node_modules/.bin/eslint src/*
-
git commit changes
https://devcenter.heroku.com/articles/getting-started-with-nodejs# introduction
-
Install Heroku CLI.
-
Login and create heroku app:
$ heroku login
$ heroku create
- In package.json:
"scripts": {
"postinstall": "npm run build"
}
-
In Procfile:
web: node dist/test-server.js
-
In package.json:
"engines": {
"node": "6.10.3",
"npm": "3.10.10"
}
-
create release branch
-
git commit and merge to release branch
-
Deploy to heroku from release branch.
$ git push heroku release:master
-
Launch app in heroku
$ heroku open
-
If it fails, you can check logs:
$ heroku logs --tail
Popular web server for node.js
-
$ npm install express --save
-
Create api-server.js with sample code below:
import express from 'express';
const app = express();
const port = process.env.PORT || 4000;
app.get('/', (req, res) => {
res.send('Hello React Class, from Express.js!');
});
app.listen(port, () => {
console.log(`Express app listening on port ${port}`);
});
- Test it with nodemon and your browser
-
Install redis
http://redis.io/download
-
Make sure your local redis server is running
-
Install the redis client for node
$ npm install redis --save
-
Create redis-client.js that exposes a setupRedis function and the redisClient
-
Call setupRedis from api-server.js
-
Add /redis-test route that increments a 'inc-test' key in redis and returns the new incremented value
-
In heroku: add redis as an add-on
$ heroku addons:create rediscloud:30
-
Deploy to heroku and test redis route
Solution branch: sln/01-web-server-v2
-
Organize js files into spa, core and api directories.
-
Update run scripts and rename “build” to “build-api”
"build-api": "rm -Rf dist/api && babel src/api -d dist/api",
"postinstall": "npm run build-api"
-
Build api js files using
$ npm run build-api
-
Update Procfile
-
Release to Heroku, test that the api-server works as expected
- Install react and react-dom. Since we are going to build spa locally, install them into devDependencies:
$ npm install react --save-dev
$ npm install react-dom --save-dev
Browsers don’t support ES6 yet. Therefore, we need to transpile all of our spa code.
- Setup babel presets:
es2015 preset is for syntax transformation.
react preset is for react and jsx.
polyfill is to allow es2016+ like Set, Maps, Promises
$ npm install babel-preset-es2015 --save-dev
$ npm install babel-preset-react --save-dev
$ npm install babel-polyfill --save-dev
-
Create src/spa/app.js, this will be the entry point of our spa.
-
Make sure to import babel-polyfill at the top of file.
import 'babel-polyfill';
-
app.js will import react and react-dom and will render a simple message for now.
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(<h1>Hello React Class</h1>, document.getElementById('root'));
-
Install webpack, webpack-dev-server and babel-loader as devDependencies
$ npm install webpack webpack-dev-server babel-loader --save-dev
-
Add webpack.config.js file
(see sln branch)
-
Install gulp to help us run more complex tasks (like webpack dev server and build)
$ npm install gulp --save-dev
-
Add gulpfile.js
(see sln branch)
-
Add www/index.html
(see sln branch)
-
Add npm scripts:
"build-spa": "rm -Rf dist/spa && gulp build-spa",
"spa-server": "gulp spa-server",
-
Test build-spa, it should output app.bundle.js and app.bundle.js.map to dist/spa
$ npm run build-spa
-
Test spa-server, it should run the webpack development server which reloads on file changes
$ npm run spa-server
-
In the api, add /json-test route. It should also increment using Redis and it should return an object using res.json({}).
-
The spa will make ajax calls using jquery ajax library
$ npm install jquery --save-dev
-
Create a react component that will make an ajax call to the /test-json route and will render result.
-
Test json-test route using react component
We need a simple site that will host our index.html and static assets (app.bundle.js, images, etc.). We can use github pages for that.
-
Fork my wa-clone-site repo:
https://github.com/fertrig/wa-clone-site -
On your fork: click on the Settings tab and scroll down to the GitHub Pages section. Then select the master branch source and click on the Save button.
https://pages.github.com/ -
Delete the spa/ directory contents
-
Update index.html with your apiUrl
-
Build the spa in the main repo, then copy the app.bundle.js and app.bundle.js.map over to the site repo, make sure to version it
-
Commit and push to the site repo.
-
Deploy your api to heroku
-
Go to your github site to verify spa is using the api hosted in heroku:
https://[username].github.io/wa-clone-site/ -
Homework: write a gulp task that will automate this process
Solution branch: sln/02-setup-profile-v2
Create an api route that will create a user in Redis.
-
Route will accept a json object
$ npm install body-parser --save
-
User requirements
- A user has a handle and name.
- Validation rules:
- handle must be unique,
- handle cannot be more than 16 characters
Some of our code will run on the server and the client. We need a strategy to share and build this code. A simple strategy is to identify folders that will have files that will run on both client and server.
-
Validation rules will run on both client and server, place them in the core directory
-
Adjust the build-api script to also transpile the core directory
"build-api": "rm -Rf dist/api && rm -Rf dist/core && babel src/api -d dist/api && babel src/core -d dist/core"
-
Create a sample mocha test under
core/__tests__/sample.tests.js
-
Install and setup mocha and chai
$ npm install mocha --save-dev
$ npm install chai --save-dev
-
Create test-root.js file which will help us with test globals
global.assert = require("chai").assert;
-
Setup an npm script for mocha that will run test files inside any
__tests__
folder
"test": "mocha ./test-root.js ./src/**/__tests__/*.tests.js --compilers js:babel-core/register"
-
$ npm test
-
Create unit tests for user validation
-
If validation fails, return error to client.
res.status(400).send(‘some error’);
-
If validation succeeds, save to Redis http://redis.io/commands
-
Store the user in an array of users in Redis.
-
Test api using postman or curl
https://www.getpostman.com/
Create a token that will encode the user’s handle. We’ll use it as a quick way to authenticate requests. The api will sign the user handle using a key, that way only the api can decode it. Note that the token doesn't expire, which is not ideal. For a proper JWT implementation see:
https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/
https://jwt.io/introduction/
- If the validation succeeds, return the JWT token to the client, which will encode the user's handle.
$ npm install jsonwebtoken --save
We would like to require sass files from within our react components. To do so, we need to setup webpack and sass. There is a loader that will bundle our sass files but it needs other dependencies to work.
-
Install sass-loader and its peer dependencies
$ npm install sass-loader node-sass style-loader css-loader --save
-
Configure the loader in webpack.config.js
loaders: [
{
test: /src(\/|\\).*\.scss$/,
loaders: ["style", "css", "sass"]
}
]
- Normalize styles using normalize.css
https://necolas.github.io/normalize.css/
-
Create React component that will submit profile. Feel free to use the UX provided. The spa will use the route to create users when submitting a profile.
-
Manage the request state. A request has several states: default, in-progress, success, hasError. Make sure you render the correct view for each request state.
-
If the request succeeds, store the jwt token in localStorage so that it can be used for subsequent requests and sessions. Be aware that some browsers only give you 5 megabytes of localStorage space.
-
Ajax request should pass Authorization header and cache control headers
-
Homework: use the user validator on the client-side. Validate that the handle and name are provided. Show messages to the user based on the validation errors.
Solution branch: sln/03-add-contact-v2
-
Leverage a base store to reduce flux boilerplate code
-
Prefer to organize your folders by feature
-
If the profile is already created, render the Chats view
-
If no contacts, render the zero state of the Chats view
-
Create a re-usable Modal component
-
Display the Add Contact modal.
-
User should be able to close the modal by clicking outside of it (on the modal overlay)
-
Refactor request state and submit button into a reusable component. This new component will manage the submit button state based on the request state. Can be re-used from add-profile, add-contact, edit-profile, etc. (Solution: see request-submit-button.react.js)
-
Create a component that will render a message for every request state (Solution: see request-message.react.js)
Solution branch: sln/04-real-time
-
Initial setup.
-
Create namespace for each client.
-
Clients and server will communicate via facts. Facts will have types. Setup code to support facts.
- When new messages come in we need to scroll down to the bottom so users can read the new messages.
- Configure webpack to process image assets. Use them from within the application.