2017/03/29 Update: Fixed the versions of react-router and react-hot-loader. 2017/03/20 Update: Webpack 2 configuration.

The Javascript stacks, MEAN and MERN on top, are definitely the hottest tech in the web development community nowadays. In fact all the Javascript ecosystem is continuosly expanding with updates and new packages on a daily basis. Finding the direction may be seen as an overwhelming tasks sometimes, especially for beginners, but luckily communites like Scotch.io strive to provide the right direction with always up-to-date tutorials and articles.

In this tutorial we are going to write an archive for retrogames by using Javascript in both backend and frontend:

We will combine express built on top of Node.js with React and Redux to demonstrate how easy is to write a single page app.

To persist data we are using Mongo.db which integrates pretty well with Node.js thanks to its Mongoose ODM.

In addition, to upload pictures with no hassle we are gonna integrate Filestack in our app which returns a CDN url for us:

Filestack hosts our pictures saving the burden to make sure our machines has the space to store them as well as avoid us all the uploading related security.

Upgrade Your JS Go from vanilla JavaScript 👉 React

Watch for FREE

Last but not least, the free account is enough to implement all the functionalities we need for the app.

Our archive allows users to view, create and delete games of the past that made history. I am a huge fan of games like Super Mario Bros., Street Fighter, Sonic, King of Fighters so I really enjoyed writing this app, I hope you too!

I separated the tutorials in different parts to make it easier to digest all the information. In this very first tutorial we are going to setup the Node.js, connect it to Mongo.db, write the games API and test it with postman. Then, we will write it using React and serve it with webpack-dev-server. In the second part of the tutorial we are going to include Redux state container and Redux-saga to perform asynchronous HTTP requests to our real server. Finally, I may add a third part, a bonus one, to show some simple authentication and improve the UI.

I suggest to follow the tutorial and build the app step-by-step, however you can also find the app on github:

Once you cloned/forked the repo, just checkout to tutorial/part1 branch.

git checkout tutoral/part1

In general it would be better to have basic knowledege of the technologies discussed throughout the tutorial (Node.js, React, Mongo.db, Webpack2...).

ES6 syntax, guys we are at the end of 2016 so let's start using it!

Yarn, the new package manager is out and I fell in love with it. I especially like the intrinsic reliability guaranteed by yarn.lock, this makes our install works on different systems. For the record, Scotch.io released a very cool tutorial for Yarn.

--app ----models ------game.js ----routes ------game.js --client ----dist ------css --------style.css ------fonts --------PressStart2p.ttf ------index.html ------bundle.js ----src ------components --------About.jsx --------Archive.jsx --------Contact.jsx --------Form.jsx --------Game.jsx --------GamesListManager.jsx --------Home.jsx --------index.js --------Modal.jsx --------Welcome.jsx ------containers --------AddGameContainer.jsx --------GamesContainer.jsx ------index.js ------routes.js --.babelrc --package.json --server.js --webpack-loaders.js --webpack-paths.js --webpack.config.js --yarn.lock

Notice the two files /client/src/components/index.js and /client/src/containers/index.js :

I am using them to export all the components and containers in a single file to write the import more easily. Take a look at this example:

import c1 from './c1.jsx' ; import c2 from './c2.jsx' ; import c3 from './c3.jsx' ; export { c1 , c2 , c3 } ;

And then we can include them within a single line:

import { c1 , c2 , c3 } from '../components' ;

So we are going to write our server API! let's define the routes first:

Routes Table GET /games Get all the games. POST /games Create a game. GET /games/:id Get a single game. DELETE /games/:id Delete a game.

Nothing exotic up here, we just defined some common routes to edit our archive. Before we start creating the project, you should have already asked yourself where are we going to save the data... Are we gonna persist it? Yes, we are gonna use Mongoose ODM to persist data on a Mongo database.

In the newly created project folder we first initialize the package.json :

yarn init

So now let's start adding our dependencies. For the server part of the project we just need a few, Express (definitely), Mongoose, Body-parser, Morgan and Babel transpiler to use ES6 syntax throughout our app.

NB: Babel is great but not suggested for production as it slows down the server while transpiling from ES6 to ES5.

We need to run two commands as babel is a dev-dependency:

yarn add express mongoose morgan body-parser

yarn add babel-core babel-cli babel-preset-es2015 --dev

Now we are able to run our server with babel-node server.js . however It's good practice to create a specific command inside the package.json under "scripts". So, open the package.json file and add

"scripts" : { "api" : "babel-node server.js" }

So now we can just run

yarn api

At the end your package.json file should be similar to mine:

{ "name" : "tutorial" , "version" : "1.0.0" , "main" : "server.js" , "author" : "Sam" , "license" : "MIT" , "scripts" : { "api" : "babel-node server.js" } , "dependencies" : { "body-parser" : "^1.15.2" , "express" : "^4.14.0" , "mongoose" : "^4.7.2" , "morgan" : "^1.7.0" } , "devDependencies" : { "babel-cli" : "^6.18.0" , "babel-core" : "^6.20.0" , "babel-preset-es2015" : "^6.18.0" } }

In addition to this, to effectively take advantage of Babel transpiler we have to create a file .babelrc in the root folder, then paste the following code:

{ "presets" : [ "es2015" ] }

NB: According to the documentation you can specify your config within the package.json file too.

At this point it's really time to code! We need a server file where we configure our express server, connect the body-parser and morgan middlewares as well as mongoose, write our routes and so on. Create the server.js file in the root folder and past the following code:

import express from 'express' ; import bodyParser from 'body-parser' ; import mongoose from 'mongoose' ; import morgan from 'morgan' ; import Game from './app/models/game' ; import { getGames , getGame , postGame , deleteGame } from './app/routes/game' ; const app = express ( ) ; const port = process . env . PORT || 8080 ; const options = { server : { socketOptions : { keepAlive : 1 , connectTimeoutMS : 30000 } } , replset : { socketOptions : { keepAlive : 1 , connectTimeoutMS : 30000 } } } ; mongoose . Promise = global . Promise ; mongoose . connect ( 'YOUR_MONGO_CONNECTION' , options ) ; const db = mongoose . connection ; db . on ( 'error' , console . error . bind ( console , 'connection error:' ) ) ; app . use ( bodyParser . urlencoded ( { extended : true } ) ) ; app . use ( bodyParser . json ( ) ) ; app . use ( morgan ( 'dev' ) ) ; app . use ( express . static ( __dirname + '/client/dist' ) ) ; app . use ( ( req , res , next ) => { res . header ( "Access-Control-Allow-Origin" , "*" ) ; res . header ( 'Access-Control-Allow-Methods' , 'GET,POST,DELETE' ) ; res . header ( "Access-Control-Allow-Headers" , "Origin, X-Requested-With, Content-Type, Accept" ) ; next ( ) ; } ) ; app . route ( '/games' ) . post ( postGame ) . get ( getGames ) ; app . route ( '/games/:id' ) . get ( getGame ) . delete ( deleteGame ) ; app . route ( "*" ) . get ( ( req , res ) => { res . sendFile ( 'client/dist/index.html' , { root : __dirname } ) ; } ) ; app . listen ( port ) ; console . log ( `listening on port ${ port } ` ) ;

The code is pretty straightforward:

We connect to our Mongo.db database through Mongoose.

We set Body-parser and Morgan middlewares for parsing request bodies and output useful logs in the console.

We enable CORS to allow HTTP requests from the webpack-dev-server on the same machine, this makes our development easier later on, it won't be necessary once we serve our client from node.

to allow HTTP requests from the webpack-dev-server on the same machine, this makes our development easier later on, it won't be necessary once we serve our client from node. ...And then our routes with a specific callback function.

Before writing the functions in /app/routes/game.js let's define our Game model!

The Game model is very simple, we need the game name, a description, the year it was published and a picture. Plus, postDate to track the time it was created.

Paste the following code in /app/models/game.js :

import mongoose from 'mongoose' ; const Schema = mongoose . Schema ; const gameSchema = new Schema ( { name : String , year : Number , description : String , picture : String , postDate : { type : Date , default : Date . now } } ) ; export default mongoose . model ( 'Game' , gameSchema ) ;

Notice I did not mark any field as required:

Although I recommend to do it in your personal projects, for the purpose of the tutorial I wanted to be as concise as possible.

Next, let's create the callback functions to handle the requests and responses and we can test our server.

Create the game.js file in /client/app/routes and paste the following code:

import Game from '../models/game' ; const getGames = ( req , res ) => { Game . find ( null , null , { sort : { postDate : 1 } } , ( err , games ) => { if ( err ) { res . send ( err ) ; } res . json ( games ) ; } ) ; } const getGame = ( req , res ) => { const { id } = req . params ; Game . findById ( id , ( err , game ) => { if ( err ) { res . send ( err ) ; } res . json ( game ) ; } ) ; } const postGame = ( req , res ) => { let game = Object . assign ( new Game ( ) , req . body ) ; game . save ( err => { if ( err ) { res . send ( err ) ; } res . json ( { message : 'game created' } ) ; } ) ; } ; const deleteGame = ( req , res ) => { Game . remove ( { _id : req . params . id } , err => { if ( err ) { res . send ( err ) ; } res . json ( { message : 'successfully deleted' } ) ; } ) ; } ; export { getGames , getGame , postGame , deleteGame } ;

The four functions take care of the user requests: They all gonna communicate with the database through the Game model and return a defined response to the client.

Our server is complete, let's give it a try!

For this simple test we first create a new game and doublecheck whether it gets really added to the archive. We consequently gonna delete it and make sure it really disappeared from the archive! By doing so we test all the routes we previously defined.

I personally use Postman browser extension to achieve this result but feel free to use your favorite tools.

Let's start our server with

yarn api

My database is already populated with a few games, here is the result:

Let's try to add a game with random information since we are going to delete it soon!

As counterproof let's make another GET request to /games and see whether it was really added to the archive.

Cool it was really added!

Let's try to filter the games by id, trivial test but we want to cover all the endpoints.

Seems to be working smoothly.

Now, given the id, let's try to delete it:

Finally, let's doublecheck if it was really deleted:

Awesome the server is ready, time to work on the client-side!

All of you familiar with React already know we need a few steps before really coding the client. While there are some solutions like react-create-app which aims to avoid the initial configuration hassle, I still prefer to manually install all the packages which also has an educational value for the tutorial. For anyone interested in digging into Webpack, I suggest to take a look at survive.js. It's a valuable resource for learning Webpack and React, plus the e-books can be read online for free.

Let's start installing some packages now:

yarn add webpack webpack-dev-server webpack-merge --dev

We are obviously installing webpack to help us create the bundle along with webpack-dev-server for serving us the client during development.

Perhaps not everyone is familiar with the latter one: webpack-merge helps to merge pieces of configurations together.

Other than this, we need a few loaders:

yarn add babel-preset-react babel-loader react-hot-loader@next style-loader css-loader file-loader --dev

These helps in severals tasks like transpiling (guess which one!), include the css and the fonts in our bundle as well as avoid to refresh the page while changing the code in React. As there are continuous updates on these packages, make sure that react-hot-loader is up-to-date because the syntax to include it in .babelrc changed, I am currently using the version 3.0.0-beta.6 thanks to @next . In case you receive a webpack error stating that it cannot find load the plugin, just run

yarn add react-hot-loader@3.0.0-beta.6 --dev

To organize our code for better readability, webpack-config.js will require some data from other files.

Let's create webpack-paths.js and paste the following code:

"use strict" ; const path = require ( 'path' ) ; module . exports = { src : path . join ( __dirname , 'client/src' ) , dist : path . join ( __dirname , 'client/dist' ) , css : path . join ( __dirname , 'client/dist/css' ) } ;

As you can see we want to export some paths we are using inside the webpack configuration. Let's move on and create the webpack.config.js file and paste the following code:

"use strict" ; const merge = require ( 'webpack-merge' ) ; const PATHS = require ( './webpack-paths' ) ; const loaders = require ( './webpack-loaders' ) ; const common = { entry : { app : PATHS . src } , output : { path : PATHS . dist , filename : 'bundle.js' } , module : { rules : [ loaders . babel , loaders . css , loaders . font , ] } , resolve : { extensions : [ '.js' , '.jsx' ] } } ; let config ; switch ( process . env . NODE_ENV ) { case 'build' : config = merge ( common , { devtool : 'source-map' } ) ; break ; case 'development' : config = merge ( common , { devtool : 'eval-source-map' } , loaders . devServer ( { host : process . env . host , port : 3000 } ) ) ; } module . exports = config ;

I created a common configuration, common , where I define the common properties for both development (using webpack-dev-server) and build (the bundle is served by Node.js).

, where I define the common properties for both development (using webpack-dev-server) and build (the bundle is served by Node.js). As you may have noticed, the entry point, the output, the loaders and resolve are common among the configurations.

The switch discriminates between the two configurations, the main difference is that in development we run webpack-dev-server and get better debug informations inside our sourceMap thanks to eval-source-map. Finally, the merged configuration goes through the validate function and gets exported.

The loaders are imported from another file, webpack-loaders.js . Let's create it and paste the following code:

"use strict" ; const webpack = require ( 'webpack' ) ; const PATHS = require ( './webpack-paths' ) ; exports . devServer = function ( options ) { return { devServer : { historyApiFallback : true , hot : true , inline : true , stats : 'errors-only' , host : options . host , port : options . port , contentBase : './client/dist' , } , plugins : [ new webpack . HotModuleReplacementPlugin ( { multistep : true } ) ] } ; } exports . css = { test : /\.css$/ , use : [ 'style-loader' , 'css-loader' ] , include : PATHS . css } exports . font = { test : /\.ttf$/ , use : [ 'file-loader' ] } exports . babel = { test : /\.jsx?$/ , exclude : /node_modules/ , use : [ 'babel-loader' ] } ;

The file just exports the loaders and webpack-dev-server with the hot-reload plugin.

We also have to edit .babelrc :

{ "presets" : [ "es2015" , "react" ] , "plugins" : [ "react-hot-loader/babel" ] }

We added the preset for react and react-hot-loader plugin.

Finally, we also gotta edit package.json to include new scripts commands:

"start" : "NODE_ENV=development webpack-dev-server" , "build" : "NODE_ENV=build webpack"

We set the NODE_ENV variable to switch between the two configurations we defined before in webpack.config.js .

NB: If you are a windows user you may add a & to concatenate the commands:

"start" : "NODE_ENV=development & webpack-dev-server" , "build" : "NODE_ENV=build & webpack"

First, let's create the index file served by the server which includes all our assets, bundle.js included. In /client/dist create a file index.html and paste the following code:

<!DOCTYPE html> < html lang = " en " > < head > < meta http-equiv = " Content-Type " content = " text/html; charset=UTF-8 " > < meta http-equiv = " X-UA-Compatible " content = " IE=edge " > < meta name = " viewport " content = " width=device-width, initial-scale=1 " > < title > Retrogames Archive </ title > < link rel = " icon " href = " https://cdn.filestackcontent.com/S0zeyXxRem6pL6tHq9pz " > < link href = " https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css " rel = " stylesheet " > </ head > < body > < div id = " content " > </ div > < script src = " https://code.jquery.com/jquery-3.1.1.min.js " > </ script > < script src = " https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js " > </ script > < script src = " https://api.filestackapi.com/filestack.js " > </ script > < script src = " ./bundle.js " > </ script > </ body > </ html >

Notice the div with id content, this is where ReactDOM renders our components

In this tutorial we won't use packages like react-bootsrap but just include the CDN url for both the css and javascript sources.

Finally we included Filestack!

Regarding the css, I have just customized two templates that you can find on the boostrap website and included some cool fonts I found. Just take copy them from my project.

Before diggin' into the React components it's better to setup a Filestack account, so on their website just click on the try it free button and follow the instructions:

Once in the developer portal you are immediately proposed to add their snippet in the project which is great because it would save us time but we want to customize the uploader right? So just skip it and instead grab the API key (click on "New application" in case) as we are needing it later!

It's finally time to write our components! Our archive will welcome user with a nice UI:

We provide the user some simple informations about us as well as the app in three different views. Once they click on Browse! they are redirected to the real archive list where they can add new games as well as view the details and delete them:

Our routes configuration is composed by two main routes with their children routes:

The Homepage is a route with three children routes in charge to render the components related to home, features and contacts links. The Games route handles the children routes to list the games and add a new one.

Notice I named them* Homepage* and Games to help you guys understand the structure but in the code they actually don't carry any name themselves.

Let's install a few packages:

yarn add react react-dom react-router@3.0.0

NB: I am using the version 3 of react-router in the tutorial.

We can start by creating a file index.js in /client/src and past the following code:

import '../dist/css/style.css' ; import React from 'react' ; import ReactDOM from 'react-dom' ; import Routes from './routes' ; filepicker . setKey ( "YOUR_API_KEY" ) ; ReactDOM . render ( Routes , document . getElementById ( 'content' ) ) ;

We included react-dom so we can render the routes in the div element with id content! Also, this is where you should set Filestack's API key.

Well, our routes configuration is in the same folder so create routes.js in /client/src and paste the following code:

import React from 'react' ; import { Router , Route , hashHistory , IndexRoute } from 'react-router' ; import { Home , Welcome , About , Contact } from './components' ; const routes = ( < Router history = { hashHistory } > < Route path = "/" component = { Home } > < IndexRoute component = { Welcome } / > < Route path = "/about" component = { About } / > < Route path = "/contact" component = { Contact } / > < / Route > < / Router > ) ; export default routes ;

We imported a few components from react-router and defined our first URL paths structure:

Url Component / Home -> Welcome /about Home -> About /contact Home -> Contact

We are using hashHistory so we don't need any server configuration in case of page refresh. Moreover, notice the four components we are going to write are stateless, they are just presentational components that are not going to touch the state so they are very easy to write. Let's do it!

NB: React 15.3.0 introduced PureComponent to replace pure-render-mixin which does not work with ES6 classes so we can actually extends it for our stateless components.

This component is basically the skeleton for the others, in /client/src/components create a file Home.jsx and paste the following code:

import React , { PureComponent } from 'react' ; import { Link } from 'react-router' ; export default class Home extends PureComponent { active ( path ) { if ( this . props . location . pathname === path ) { return 'active' ; } } render ( ) { return ( < div className = "main" > < div className = "site-wrapper" > < div className = "site-wrapper-inner" > < div className = "cover-container" > < div className = "masthead clearfix" > < div className = "inner" > < nav > < img className = "header-logo" src = "https://cdn.filestackcontent.com/nLnmrZQaRpeythR4ezUo" / > < ul className = "nav masthead-nav" > < li className = { this . active ( '/' ) } > < Link to = "/" > Home < / Link > < / li > < li className = { this . active ( '/about' ) } > < Link to = "/about" > About < / Link > < / li > < li className = { this . active ( '/contact' ) } > < Link to = "/contact" > Contact < / Link > < / li > < / ul > < / nav > < / div > < / div > { this . props . children } < / div > < / div > < / div > < / div > ) ; } }

{this.props.children} is where we render the three children components.

is where we render the three children components. We need to change the class to the li element when clicked, this is easy to achieve through the active() function which checks the pathname against the path parameter we pass. If we had to change the class of the Link component we wouldn't need any function but unfortunately the theme I grabbed from Bootstrap applies "active" on the li element instead.

This component welcomes our user and provides the link to navigate to the games archive, create the file Welcome.jsx in /client/src/component and paste the following code:

import React , { PureComponent } from 'react' ; import { Link } from 'react-router' ; export default class Welcome extends PureComponent { render ( ) { return ( < div className = "inner cover" > < h1 className = "cover-heading" > Welcome < / h1 > < p className = "lead" > Click on browse to start your journey into the wiki of games that made history . < / p > < p className = "lead" > < Link className = "btn btn-lg" to = "/games" > Browse ! < / Link > < / p > < / div > ) ; } }

It doesn't require any explanation, just a welcome message and the browse! link to view the games archive.

Create About.jsx in /client/src/components and paste the following code:

import React , { PureComponent } from 'react' ; export default class About extends PureComponent { render ( ) { return ( < div className = "inner cover" > < h1 className = "cover-heading" > Javascript Everywhere < / h1 > < p className = "lead" > This archive is made with Node . js and React . The two communicate through async HTTP requests handled by Redux - saga ... Yes we love Redux here ! < / p > < / div > ) ; } }

Even simplier, just a simple explanation on how we wrote the app!

Create Contact.jsx in /client/src/components and paste the following code:

import React , { PureComponent } from 'react' ; export default class About extends PureComponent { render ( ) { return ( < div className = "inner cover" > < h1 className = "cover-heading" > Any Questions ? < / h1 > < p className = "lead" > Don't hesitate to contact me : zaza . samuele@gmail . com < / p > < / div > ) ; } }

Feel free to change the text!

We need this file to export all the components, let's create it and paste the following code:

import About from './About' ; import Contact from './Contact' ; import Home from './Home' ; import Welcome from './Welcome' ; export { About , Contact , Home , Welcome } ;

At this point we can already see the client in action, just run

yarn start

and open http://localhost:3000 in the browser. Though we haven't completed the app we can already see the welcome page as well as the other links on the top-right of the page.

Let's now work on the interactive pages.

We already discussed about the url, we need to update our route configuration: We have other two views, one for the games list and one which is basically the form users upload games.

Open routes.js and replace the code with the following:

import React from 'react' ; import { Router , Route , hashHistory , IndexRoute } from 'react-router' ; import { AddGameContainer , GamesContainer } from './containers' ; import { Home , Archive , Welcome , About , Contact } from './components' ; const routes = ( < Router history = { hashHistory } > < Route path = "/" component = { Home } > < IndexRoute component = { Welcome } / > < Route path = "/about" component = { About } / > < Route path = "/contact" component = { Contact } / > < / Route > < Route path = "/games" component = { Archive } > < IndexRoute component = { GamesContainer } / > < Route path = "add" component = { AddGameContainer } / > < / Route > < / Router > ) ; export default routes ;

Let's take a look at the definitive configuration:

Url Component / Home -> Welcome /about Home -> About /contact Home -> Contact /games Archive -> GamesContainer /games/add Archive -> AddGameContainer

As we did for the components we create an index file to export the containers. Create it in /client/src/containers and paste the following code:

import AddGameContainer from './AddGameContainer' ; import GamesContainer from './GamesContainer' ; export { AddGameContainer , GamesContainer } ;

as Home component, it just provides the layour and render the children. Create Archive.jsx in /client/src/components and paste the following code:

import React , { PureComponent } from 'react' ; import { Link } from 'react-router' ; export default class Layout extends PureComponent { render ( ) { return ( < div className = "view" > < nav className = "navbar navbar-inverse" > < div className = "container" > < div className = "navbar-header" > < button type = "button" className = "navbar-toggle collapsed" data - toggle = "collapse" data - target = "#navbar" aria - expanded = "false" aria - controls = "navbar" > < span className = "sr-only" > Toggle navigation < / span > < span className = "icon-bar" / > < span className = "icon-bar" / > < span className = "icon-bar" / > < / button > < Link className = "navbar-brand" to = "/" > < img src = "https://cdn.filestackcontent.com/nLnmrZQaRpeythR4ezUo" className = "header-logo" / > < / Link > < / div > < / div > < / nav > { this . props . children } < footer className = "text-center" > < p > © 2016 Samuele Zaza < / p > < / footer > < / div > ) ; } }

This is the container for the archive list where we are writing all the functions to manipulate the state. Let's first create the file and then comment it! Create GamesContainer.jsx in /client/src/containers and paste the following code:

import React , { Component } from 'react' ; import { Modal , GamesListManager } from '../components' ; export default class GamesContainer extends Component { constructor ( props ) { super ( props ) ; this . state = { games : [ ] , selectedGame : { } , searchBar : '' } ; this . toggleModal = this . toggleModal . bind ( this ) ; this . deleteGame = this . deleteGame . bind ( this ) ; this . setSearchBar = this . setSearchBar . bind ( this ) ; } componentDidMount ( ) { this . getGames ( ) ; } toggleModal ( index ) { this . setState ( { selectedGame : this . state . games [ index ] } ) ; $ ( '#game-modal' ) . modal ( ) ; } getGames ( ) { fetch ( 'http://localhost:8080/games' , { headers : new Headers ( { 'Content-Type' : 'application/json' } ) } ) . then ( response => response . json ( ) ) . then ( data => this . setState ( { games : data } ) ) ; } deleteGame ( id ) { fetch ( `http://localhost:8080/games/ ${ id } ` , { headers : new Headers ( { 'Content-Type' : 'application/json' , } ) , method : 'DELETE' , } ) . then ( response => response . json ( ) ) . then ( response => { this . setState ( { games : this . state . games . filter ( game => game . _id !== id ) } ) ; console . log ( response . message ) ; } ) ; } setSearchBar ( event ) { this . setState ( { searchBar : event . target . value . toLowerCase ( ) } ) ; } render ( ) { const { games , selectedGame , searchBar } = this . state ; return ( < div > < Modal game = { selectedGame } / > < GamesListManager games = { games } searchBar = { searchBar } setSearchBar = { this . setSearchBar } toggleModal = { this . toggleModal } deleteGame = { this . deleteGame } / > < / div > ) ; } }

In the constructor we defined an inital state with an empty array of games that will be soon populated. selectedGame is the specific game to show in the bootstrap modal and searchBar is the search keyword to filter the games.

In componentDidMount() we call game() which make an HTTP call for the games and set them into the state. Notice the new fetch() function.

we call game() which make an HTTP call for the games and set them into the state. Notice the new function. toggleModal() is passed as props to the GamesListManager component to set the current game in the state and toggle the modal.

is passed as props to the component to set the current game in the state and toggle the modal. setSearchBar() updates the state with the current keyword. toLowerCase() guarantees our search is not case-sensitive.

updates the state with the current keyword. guarantees our search is not case-sensitive. Finally, we render Modal and GamesListManager components.

NB: At the present time thinking about refactoring isn't necessary as our code will substantially change with Redux. In fact we should be just focusing on making things work now!

This is another stateless component, just create it in /client/src/components and paste the following code:

import React , { PureComponent } from 'react' ; export default class Modal extends PureComponent { render ( ) { const { _id , img , name , description , year , picture } = this . props . game ; return ( < div className = "modal fade" id = "game-modal" tabIndex = "-1" role = "dialog" aria - labelledby = "myModalLabel" > < div className = "modal-dialog" role = "document" > < div className = "modal-content" > < div className = "modal-header" > < button type = "button" className = "close" data - dismiss = "modal" aria - label = "Close" > < span aria - hidden = "true" > × < / span > < / button > < h4 className = "modal-title" id = "myModalLabel" > { ` ${ name } ( ${ year } )` } < / h4 > < / div > < div className = "modal-body" > < div > < img src = { picture } className = "img-responsive img-big" / > < / div > < hr / > < p > { description } < / p > < / div > < div className = "modal-footer" > < button type = "button" className = "btn btn-warning" data - dismiss = "modal" > Close < / button > < / div > < / div > < / div > < / div > ) ; } }

There is nothing special here, we simply shows the game information in a fancy modal.

Though stateless it is a more meaningful component. Create it in /client/src/components and paste the following code:

import React , { PureComponent } from 'react' ; import { Link } from 'react-router' ; import Game from './Game' ; export default class GamesListManager extends PureComponent { render ( ) { const { games , searchBar , setSearchBar , toggleModal , deleteGame } = this . props ; return ( < div className = "container scrollable" > < div className = "row text-left" > < Link to = "/games/add" className = "btn btn-danger" > Add a new Game ! < / Link > < / div > < div className = "row" > < input type = "search" placeholder = "Search by Name" className = "form-control search-bar" onKeyUp = { setSearchBar } / > < / div > < div className = "row" > { games . filter ( game => game . name . toLowerCase ( ) . includes ( searchBar ) ) . map ( ( game , i ) => { return ( < Game { ... game } key = { game . _id } i = { i } toggleModal = { toggleModal } deleteGame = { deleteGame } / > ) ; } ) } < / div > < hr / > < / div > ) ; } }

We could actually move out the search bar and create another component for it, however we won't use it anywhere else so there is no reusability involved.

In the render function we map the games to a Game component and we do some basic filtering: We make sure the game name contains the keyword from the search bar

The game container is pretty immediate as well, create it into /client/src/components and paste the following code:

import React , { PureComponent } from 'react' ; import { Link } from 'react-router' ; export default class Game extends PureComponent { render ( ) { const { _id , i , name , description , picture , toggleModal , deleteGame } = this . props ; return ( < div className = "col-md-4" > < div className = "thumbnail" > < div className = "thumbnail-frame" > < img src = { picture } alt = "..." className = "img-responsive thumbnail-pic" / > < / div > < div className = "caption" > < h5 > { name } < / h5 > < p className = "description-thumbnail" > { ` ${ description . substring ( 0 , 150 ) } ...` } < / p > < div className = "btn-group" role = "group" aria - label = "..." > < button className = "btn btn-success" role = "button" onClick = { ( ) => toggleModal ( i ) } > View < / button > < button className = "btn btn-danger" role = "button" onClick = { ( ) => deleteGame ( _id ) } > Delete < / button > < / div > < / div > < / div > < / div > ) ; } }

The buttons triggers the functions we wrote in GamesContainer : These were passed as props from GamesContainer to GamesListManager and finally to Game .

The container is gonna render a form where our users can create games for the archive. Create the AddGameContainer.jsx in /client/src/containers and paste the following code:

import React , { Component } from 'react' ; import { hashHistory } from 'react-router' ; import { Form } from '../components' ; export default class AddGameContainer extends Component { constructor ( props ) { super ( props ) ; this . state = { newGame : { } } ; this . submit = this . submit . bind ( this ) ; this . uploadPicture = this . uploadPicture . bind ( this ) ; this . setGame = this . setGame . bind ( this ) ; } submit ( ) { const newGame = Object . assign ( { } , { picture : $ ( '#picture' ) . attr ( 'src' ) } , this . state . newGame ) ; fetch ( 'http://localhost:8080/games' , { headers : new Headers ( { 'Content-Type' : 'application/json' } ) , method : 'POST' , body : JSON . stringify ( newGame ) } ) . then ( response => response . json ( ) ) . then ( data => { console . log ( data . message ) ; hashHistory . push ( '/games' ) ; } ) ; } uploadPicture ( ) { filepicker . pick ( { mimetype : 'image/*' , container : 'modal' , services : [ 'COMPUTER' , 'FACEBOOK' , 'INSTAGRAM' , 'URL' , 'IMGUR' , 'PICASA' ] , openTo : 'COMPUTER' / / First choice to upload files from } , function ( Blob ) { console . log ( JSON . stringify ( Blob ) ) ; $ ( '#picture' ) . attr ( 'src' , Blob . url ) ; } , function ( FPError ) { console . log ( FPError . toString ( ) ) ; } ) ; } / / We make sure to keep the state up - to - date to the latest input values setGame ( ) { const newGame = { name : document . getElementById ( 'name' ) . value , description : document . getElementById ( 'description' ) . value , year : document . getElementById ( 'year' ) . value , picture : $ ( '#picture' ) . attr ( 'src' ) } ; this . setState ( { newGame } ) ; } render ( ) { return < Form submit = { this . submit } uploadPicture = { this . uploadPicture } setGame = { this . setGame } / > } }

In the constructor we define an empty new game in the state. Thanks to setGame() we create its values whenever the user edit one of the inputs from the form (you will see it later).

we create its values whenever the user edit one of the inputs from the form (you will see it later). submit() sends the new game to the server through POST request.

What about the upload() function?

Inside we run the pick() function from Filestack which prompts a modal a picture. If you take a look a the documentation for the function, we may have noticed that the first parameter is an option object for customizing our uploader: For example, if you don't want users to upload non-image files, well Filestack allows you to restrict the mimetype! I love the fact I can create in few minutes my uploader with custom options to fit my needs. For the current tutorial, I defined the option objects as following:

The mimetype equal to image/* limits the upload to image files.

equal to image/* limits the upload to image files. We can choose to show either a modal or dialog uploading interfaces, I personally prefer the modal but you guys could try to customize it the way you like!

What are the sources to upload from? Not just the user's device but there are plenty of other choices. In our case we define an array of all the allowed sources.

Finally, among these choices above, we choose the computer as the default one.

Finally, there are two functions, one for onSuccess and one for onError . Notice the Blob object parameter on onSuccess : This is returned by Filestack, it contains a bunch of information among which the image url!

Let me show you an example:

{ "url" : "https://cdn.filestackcontent.com/CLGctDtSZiFbm4AKYTSX" , "filename" : "background.jpg" , "mimetype" : "image/jpeg" , "size" : 609038 , "id" : 1 , "key" : "w53urmDSga10ndZsOiE5_background.jpg" , "container" : "filestack-website-uploads" , "client" : "computer" , "isWriteable" : true }

For more information don't hesitate to take a look at the documentation, the guys made a big effort to write very clear instructions.

Our last component is Form, let's create it in /client/src/components (used to it yet?!) and paste the following code:

import React , { PureComponent } from 'react' ; import { Link } from 'react-router' ; export default class Form extends PureComponent { render ( ) { return ( < div className = "row scrollable" > < div className = "col-md-offset-2 col-md-8" > < div className = "text-left" > < Link to = "/games" className = "btn btn-info" > Back < / Link > < / div > < div className = "panel panel-default" > < div className = "panel-heading" > < h2 className = "panel-title text-center" > Add a Game ! < / h2 > < / div > < div className = "panel-body" > < form name = "product-form" action = "" onSubmit = { ( ) => this . props . submit ( ) } noValidate > < div className = "form-group text-left" > < label htmlFor = "caption" > Name < / label > < input id = "name" type = "text" className = "form-control" placeholder = "Enter the title" onChange = { ( ) => this . props . setGame ( ) } / > < / div > < div className = "form-group text-left" > < label htmlFor = "description" > Description < / label > < textarea id = "description" type = "text" className = "form-control" placeholder = "Enter the description" rows = "5" onChange = { ( ) => this . props . setGame ( ) } > < / textarea > < / div > < div className = "form-group text-left" > < label htmlFor = "price" > Year < / label > < input id = "year" type = "number" className = "form-control" placeholder = "Enter the year" onChange = { ( ) => this . props . setGame ( ) } / > < / div > < div className = "form-group text-left" > < label htmlFor = "picture" > Picture < / label > < div className = "text-center dropup" > < button id = "button-upload" type = "button" className = "btn btn-danger" onClick = { ( ) => this . props . uploadPicture ( ) } > Upload < span className = "caret" / > < / button > < / div > < / div > < div className = "form-group text-center" > < img id = "picture" className = "img-responsive img-upload" / > < / div > < button type = "submit" className = "btn btn-submit btn-block" > Submit < / button > < / form > < / div > < / div > < / div > < / div > ) ; } }

Pretty straightforward! Whenever a users edit any form input, the onChange function update the state.

The components were all created but we have to update /client/src/components/index.js to export them all. Replace its code with the following:

import About from './About' ; import Contact from './Contact' ; import Form from './Form' ; import Game from './Game' ; import GamesListManager from './GamesListManager' ; import Home from './Home' ; import Archive from './Archive' ; import Modal from './Modal' ; import Welcome from './Welcome' ; export { About , Contact , Form , Game , GamesListManager , Home , Archive , Modal , Welcome } ;

And now we can run the app! We first start the api server:

yarn api

And if you haven't, webpack-dev-server:

yarn start

This should work smoothly however we are still not serving the bundle from Node.js. We need to run another command:

yarn build

This will create the bundle.js in the /dist folder... Now connect to http://localhost:8080 and the client is served from our real server instead.

Congratulations for finishing the first part of the tutorial!

In this first part of the tutorial we went through the initial project configuration.

We first built the backend of the app, an API server with Node.js and Express. We also made a preliminary test with postman to doublecheck that everything works as expected. For a real-world app this is not exhaustive, if you a curious about testing, take a look at my previous post on testing Node.js with Mocha and Chai here on Scotch!

Then we spent some time configuring Webpack to include javascript and css inside the same bundle file. Eventually we wrote React components to see the app in action.

In the next tutorial we are going to include Redux and related packages, we will see how easily we can manage the state if we separate it into a container. We will include lots of new packages, not just redux but redux-saga, redux-form... We will work with immutable data structure as well.

Stay tuned!