/boilerplate-react-reflux

A small and simple boilerplate for a React+Reflux

Primary LanguageJavaScript

React/Reflux Boilerplate

A simple, opinionated project boilerplate/seed.

Libraries/Features

Front-end

Library Purpose
React View-controller layer
Reflux Uni-directional dataflow architecture
react-router Declarative routing
browserify CommonJS module system
es5-shim Support for non-ES5 clients
jquery For its utility methods (I'm sorry)
sass/scss A CSS preprocessor

Back-end

Library Purpose
express v4 Web app framework and routing
body-parser [Middleware] Request body parsing
mongoose MongoDB object modeling

Build Toolchain

Library/Service Purpose
gulp Streaming build system
gulp-uglify [Production] uglify js source
gulp-util Logging in gulp, etc.
gulp-concat Concat'ing vendor scripts
gulp-sass For processing sass/scss into css
gulp-cssmin [Production] minify css
browserify Bundling front-end CommonJS modules
vinyl-source-stream Make Browserify usable with gulp
reactify A React/JSX transform for browserify
watchify Efficient rebundling on changes
Heroku Easy deployment (must add mongolab)

Getting Started

First off...

$ git clone https://github.com/bericp1/boilerplate-react-reflux.git app-name
$ cd app-name
$ git remote remove origin # Remove the boilerplate as the origin
                           # You can optionally rename the remote

If you plan on deploying to heroku, also run the commands in the deployment section below.

In general, you build React components and their associated Reflux stores and actions in separate directories within public/src/scripts/, require and use them in public/src/app.js to build the root front-end app.

SASS styles go in public/src/styles/ in *.scss files which then can be @imported into the entry stylesheet public/src/styles/app.scss.

Static files can go in public/src/assets/.

The compiled front-end is outputted to public/dist/ from which it is served.

Server logic goes in server.js and separate express Routers in routes/, which are required and mounted in server.js and can use mongoose models defined in models/ for persistent storage.

Build tasks

$ gulp clean:[task]

Recursively removes (rimrafs) all files related to the task task as configured in config.build.clean[task]

$ gulp scripts

Runs ['clean:scripts'] and then bundles public/src/scripts/app.js with browserify and places it in public/dist/app.js.

$ gulp styles

Runs ['clean:styles'] and then processes public/src/styles/app.scss into CSS and places it in public/dist/app.css. This means that all styles will be compiled into one file (minified in production) and must be @imported somewhere down the line starting form app.scss. Partials (_*.scss) are your friends.

$ gulp vendor

Runs ['clean:vendor'] and then concats all vendor scripts specified in config.build.vendor.js into public/dist/vendor.js

$ gulp copy

Runs ['clean:copy'] and then copies public/src/{index.html,assets} recursively to public/dist as is.

$ gulp serve or $ gulp dev

  1. runs ['vendor', 'copy', 'styles']
  2. bundles public/src/scripts/app.js with browserify to public/dist/app.js
  3. starts the server (index.js)
  4. fires up watchify which will rebundle on changes to any bundled files. Watchify uses a caching system to make this a comparatively efficient operation.
  5. watches for changes to public/src/styles/**/*.* and runs sass task
  6. watches for changes to static files (public/src/{index.html,assets/}) and runs copy task

$ gulp build

Should be used for building front end into public/dist where the server will serve from. Alias for ['copy', 'scripts', 'styles'].

Structure/Architecture

Directories & Files

Note: These are all mostly just suggestions. Use whatever structure you feel comfortable with. Just note that this structure is what I had in mind while putting together this boilerplate.

├─ config.js                 : app config
├─ lib/                      : first-party general node modules
├─ public/
│  ├─ dist/                  : post-build output; served from here
│  └─ src/                   : front-end code
│     ├─ assets/             : static files; no build processing
│     ├─ index.html          : app entry; usually no need to edit
│     ├─ styles/             : all sass/scss styles for the frontend
│     │  └─app.scss          : entry for styles; use @import here
│     ├─ lib/                : re-usable modules for front-end
│     └─ scripts/            : all CommonJS modules for front-end
│        ├─ app.js           : entry; exports root view-controller
│        └─ [app-component]/ : a component of the app
│           ├─ index.js      : entry point; exports view-controller
│           ├─ actions.js    : Reflux actions for component
│           ├─ store.js      : Reflux store for component
│           └─ my-view.jsx   : A related React view-component
├─ tasks/                    : Gulp tasks; `require`d in `gulpfile.js`
├─ index.js                  : pre-configures the server
├─ server.js                 : this is where routers can be required, etc.
├─ start.js                  : starts server (from `index.js`) with defaults
├─ routes/                   : modules that export an express `Router`
├─ models/                   : modules that export mongoose models
└─ config.js                 : global app config; see [below](#config)

Front-end

The front-end should be routed with react-router, utilizing React for the view-controllers, all tied together with Reflux, a library-backed app architecture that relies on uni-directional data-flow and convention over configuration.

The entry point for the front end (public/src/scripts/app.js) should...

  • Build and render the root Application React view-controller
  • require each app component from their subdirs
  • describe the react-router routes
  • render the base application

See the example: app.js.

Each app component should...

  • be separated into its own subdir within public/src/scripts/
  • be designated/named by the purpose it serves
  • have its own Reflux actions/stores and React view-controller(s)
    • React view-controllers can live in separate *.jsx files or be rendered in the app component's index.js
    • stores in *.store.js or simply store.js
    • and actions in actions.js
  • export via module.exports in its index.js its root React view-controller

See either example: react-example or reflux-example

For SCSS styles, its recommended to have a separate *.scss file in public/styles for each app component which can then be @imported into app.scss. For naming classes, its recommended to use component specific prefixes (perhaps separated form the rest of the class name with a double dash --) to avoid naming conflicts.

Important: public/src/ is added to the browserify search path so you can require modules relative to that path such as require('lib/my-lib') for convenience.

Back-end

Note that the back-end is much more free-from and flexible but these are my formal suggestions when it comes to structure/architecture.

First off, the server is initially split into three different files:

  • index.js: Pre-configures the server; contains boilerplate; exports a function which when called (with a single, optional, overloading config argument) does the following:
    1. loads express and mongoose
    2. creates an express app
    3. detects environment
    4. determines port to run on
    5. determines mongodb URI
    6. attaches a logging interface to app.logger
    7. configures and mounts body-parser
    8. requires server.js and calls it, passing the app
    9. finally mounts a static file server that serves from public/dist
    10. fires up mongodb connection
    11. begins serving on determined port once mongoose has connected
  • server.js: App-specific back-end logic; exports a function which takes a required app argument which is the root express app. Inside this function you should require and mount your routes/Routers.
  • start.js: Used by the build toolchain to start the app quickly with the default configuration. So you can run node ./start.js to fire up the server if you'd like.

Routing

Create individual modules in routes/ that export express Routers. It is recommended for the sake of organization to use file names/paths that directly correlate to the mount path of each Router.

For example, if we're mounting an api endpoint to access posts at /api/posts, you might want to create the route in the file routes/api.posts.js or routes/api/posts.js. I personally prefer the former in order to keep a flatter directory structure that's still pretty descriptive.

Mongoose

Mongoose is included in this boilerplate. I would recommend exporting mongoose models in modules in the models/ directory so that they can be included by routes on an as-needed bases without having to access mongoose's internal model registry-thing.

Deployment

Ready to deploy to Heroku after the mongolab addon is added to the heroku app along with the proper NODE_ENV environment variable:

$ heroku apps:create app-name
$ heroku config:set NODE_ENV=production
$ heroku addons:add mongolab
$ git push heroku master

package.json is configured with the correct start script so that Procfile is auto-generated and the correct postinstall script so that gulp build is run when a new commit is pushed live, building the app. The port to run on and the URI to use to access mongodb will also be properly detected.

Config

Located in config.js. Open up that file to see defaults and general structure.

Important: Notice that the config can contain at its root two special properties: production and development which will override the other config options when the environment variable NODE_ENV is either development or production.

For example, if

config.server.port === 8000

but

config.production.server.port === 80 && process.env.NODE_ENV === 'production'

then 80 will be favored by index.js over 8000. As a side note though, index.js by default prefers the port passed directly to it via its options, then the PORT environment variable, and finally config.server.port.