Skip to content
This repository has been archived by the owner on Dec 9, 2020. It is now read-only.

micahlmartin/nuts

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Nuts

This is an opinionated template for nodejs apps. The primary means of configuration is:

require('lib/nuts').deez()

Stack

Goals

The main goal of this project is to provide as much out-of-the-box functionality as possible to get a project off the ground. A lot of heavy lifting and boilerplate code is already done so you can focus on what makes your app unique.

Some of the primary features are:

  • Shared templates for server-side and client-side rendering
  • Basic transactional emails and templates
  • Login, forgot password, registration flows
  • Delayed processing of jobs
  • Good architectural patterns and practices

There are lots more things that to be added so suggestions and PR's are welcome.

External Dependencies

  • Redis
  • MongoDB

Running

To spin up the server just run npm start. This will compile all of the assets and start the server on port 3000. You can change this by either setting an environment variable PORT=3000 or by changing the default settings in config/settings.js.

Foreman is used for managing the processes so if you want to change what gets run during development you can update the Procfile.dev file.

REPL

To open up a REPL with a fully loaded environment you can run npm run console. This makes it easy to execute commands in your environment and should be very familiar to those coming from Rails.

Settings

You can configure your environment settings in settings.yml. The file is precompiled using the Lodash template syntax similar to ERB. All settings are available through Nuts.settings.

Conventions

Most things can be accessed via the global Nuts object. This is not necessary, but it's done for convenience.

Routes

Controllers

Models

Views

Views are located in the app/assets/javascript/views folder. This path is already setup so you can easily return the view from your controller by calling reply.view('myview.jsx').

Page layouts

Page layouts are used to render a full page on the server. Constructing things like raw javascript tags for 3rd party libraries or html doctypes is tricky to do with React so it's handy to use a different mechanism.

Layouts are located in the app/assets/javascript/views/layouts/page folder. They are rendered as a lodash template and should have a .template extension. Page layouts should be very generic and provide as little templating as possible. For the most part the default template can just be modified to fit your needs. It's likely rare that you would need to have more than one.

Content layouts

Content layouts are located in the app/assets/javascript/views/layouts/page folder. They are useful for providing the meat-and-potatoes structure of the page. This is where headers, footers, and menus would likely be rendered using React. If you have a splash landing page that doesn't have the same structure as the rest of the app then this would be a case to create a new content layout.

Actions

Actions are simple single purpose functions that act in some way on one or more models. They are used to encapsulate business logic. They can either be required directly or accessed via Nuts.actions.myAction;

They take the following format:

// app/actions/MyAction.js

module.exports = function(/* optional params */) {
    var deferred = Nuts.defer();

    process.nextTick(function() {
      // If process was successful then resolve the promise
      deferred.resolve(/* optional data */);

      // If there was an error then return it
      deferred.reject(/*some err*/)
    })

    return deferred.promise;
}

They can then be invoked like this:

Nuts.actions.MyAction().then(function() {
  // Completed successfully
}).fail(function(err) {
  // Action failed
}).done();

The Nuts.defer() is just a wrapper for the Q promise library. You can find out more about it here;

Environment

The default environment is development. It can be overriden with an environment variable NODE_ENV=production. There should be a corresponding file in app/config/environments for each environment. These can be used if you need to load specific settings or configurations for each environment.

Initializers

These are configurations that are loaded in every environment. They are not loaded in a specific order. They're useful for configuring various libraries and plugins

Hapi Plugins

Hapi plugins are configured just like other initializers. You can access the Hapi server via Nuts.server.

// config/initializers/good.js
Nuts.server.pack.register({
  plugin: require('good'),
  options: {
    subscribers: {
      console: ['request', 'log', 'error']
    }
  }
}, function(err) {
  if(err) throw err;
});

You can read more about Hapi plugins here.

Assets

Assets are built using webpack. If you want to build and watch the assets for changes you can run npm run assets. This process will happen automtically for you in development when you run npm start.

Delayed Jobs

Background jobs are processed using a redis job queue called Kue.

Configuring Redis

As long as Redis is running locally on a default port and IP it will work out of the box when running in development.

If you want to run it on Heroku using a Redis plugin you can just specify which environment variable contains the proper connection string you want to use. Check your settings to figure out which connectionstring your Redis plugin uses and then specify it in the REDIS_ENV_KEY env var.

For instance, if you have the Redis To Go plugin installed you can tell Kue to use that connectionstring by setting it like this:

heroku config:set REDIS_ENV_KEY=REDISTOGO_URL

Sending a job to the queue

First you need to get access the kue jobs object

var jobs = Nuts.require('lib/jobs/queue').connect();

Calling connect returns a singleton that can be used to create the jobs. Here is an example that sends an email confirmation when a user registers:

// app/actions/registerUser.js

module.exports = function(params) {
  var deferred = Nuts.defer();

  new Nuts.models.User(params).save(function(err, savedUser) {
    if(err) return deferred.reject(err);

    var jobs = Nuts.require('lib/jobs/queue').connect();
    jobs
      .create('send_email_confirmation', {
        email: savedUser.email,
        title: savedUser.email
      })
      .priority('high')
      .attempts(5)
      .save(function(err) {
        if(err) Nuts.reportError(err);

        deferred.resolve(savedUser);
      });
  });

  return deferred.promise;
};

Consult the Kue documentation for more information on various options you can use when creating jobs.

Processing jobs

Job processors are grouped by type. This allows different workers to be setup to process different kinds of jobs. Processors must be located in the lib/jobs folder and should have the following format:

module.exports = {
  process: function(concurrency) { /** process Job **/ }
}

Here is a working example that process email confirmation jobs:

//lib/jobs/email.js

var DEFAULT_CONCURRENCY   = 5;
var jobs                  = require('./queue').connect();

module.exports = {
  process: function(concurrency) {
    jobs.process('send_email_confirmation', (concurrency || DEFAULT_CONCURRENCY), function(job, done) {

      Nuts.actions.sendEmailConfirmation(job.data.email).then(function(result) {
        done();
      }).fail(function(err) {
        Nuts.reportError(err);
        done(err);
      });

    });
  }
}

Consult the Kue documentation for more information about processing jobs.

Workers

Workers are segmented by the different types of processors located in lib/jobs. You can start a worker on a specific process like this:

KUE_NAME=email KUE_CONCURRENCY=10 ./bin/kue-worker

This will run the lib/jobs/email processor with a default concurrency of 10.

Be sure to add this to your Procfile to run it in production or your Procfile.dev to run it locally.

email_worker: KUE_NAME=email KUE_CONCURRENCY=10 ./bin/kue-worker

Web Interface

There is a web interface you can access that will allow you to view all of the jobs and their progress. The ports is specified in Nuts.settings.kue.port. By default you can access it locally at http://localhost:3800.

Deployment

This kit is designed to make it easy to deploy to heroku. Here is what you need to setup a new app on Heroku:

heroku apps:create <app_name>
heroku addons:add mongohq:sandbox
heroku config:set NODE_ENV=production
heroku config:set DOMAIN=<app_name>.herokuapp.com

git push heroku master

During deployment, Heroku uses the npm postinstall hook to automatically compile and minfy all of the assets. You can read more about it here.

Contributing

Contributions are welcomed and encouraged. Just fork it and open a PR from your feature branch. I built this template specifically for me to use in side-projects and hackathons but if you find it useful I'd like to hear about it.

Thanks

...and the maintainers of all the other libraries.

#License

This project is released under the MIT License.

About

Starter kit for nodejs, react, and webpack

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published