Giter Club home page Giter Club logo

epiphany's Introduction

epiphany

I just got one.

Introduction

This is a library that aims to greatly simplify the creation of Node & Express servers. You have to use:

  1. Node & NPM
  2. Express
  3. MongoDB & Mongoose
  4. DustJS templates (from LinkedIn)

Components

We essentially define the 5 components of an Epiphany server to be:

  1. Configurations
  2. Mongoose entities (plugins, schemas & models
  3. Express Middleware
  4. Express Routes
  5. Dust Templates

This is it. You load all your components simply by telling Epiphany where they are located (which directory). They can be located in different locations. Epiphany has a property directories where it keeps arrays of locations of all components. These directories are used by the loaders.

The loaders

All components are loaded using Epiphany's loaders. The loaders are called internally in Epiphany.prototype.init with the appropriate array from directories. If you want to add more directories before initializing, call the constructor with { init: false }.

The loaders use the directory structure to namespace the routes and middleware. Models are simply added to the mongoose instance, while Schemas are saved to an object that is passed to all model modules.

Configuration

Configuration is set up slightly differently from the other components. While all other components are loaded during init, configuration is loaded in the constructor. Therefore, if you want to add more configuration locations you need to pass them to the constructor using options.config. This can be a single path string, or an array of path strings. If not config path is passed, Epiphany will check for the existence of PWD/server/config and add it to the configuration locations array.

Configuration with higher index in the array take precedence and overwrite previous properties if they exist.

Schemas & Models

Schemas are schema.org templates. They are passed to other schema files and the model files.

Example schema file (Person):

module.exports = function(mongoose, schemas) {
	return new mongoose.Schema({
		_id: String,
		"@context": { type: String, default: "http://schema.org" },
		"@type": { type: String, default: "Person" },
		address: schemas.PostalAddress.objectify(),
		email: String,//Email address.
		familyName: String,//Family name. In the U.S., the last name of an Person. This can be used along with givenName instead of the name property.
		faxNumber: String,//The fax number.
		gender: String,//Gender of the person.
		givenName: String,//Given name. In the U.S., the first name of a Person. This can be used along with familyName instead of the name property.
		jobTitle: String,//The job title of the person (for example, Financial Manager).
		telephone: String,//The telephone number.
		description: String,//A short description of the item.
		image: schemas.ImageObject.objectify(),//	An image of the item. This can be a URL or a fully described ImageObject.
	});
};

Model files need to return a function that takes two parameters, mongooose and schemas. They should register the model on the mongoose instance. Since middleware's have access to mongoose, they can also fetch all models from that instance.

All mongoose models should be extended with the Base schema, and have a save pre middleware that updates dateModified.

Example model file:

var _ = require('lodash');

module.exports = function(mongoose, schemas) {
	var PageSchema = new mongoose.Schema(_.defaults({
		_id: String,
		name: String,
		content: {},
		author: String,
	}, schemas.Base.objectify()));

	PageSchema.pre('save', function(next) {
		if(!this.isNew) {
			this.dateModified = Date.now();
		}
		next();
	});

	mongoose.model('Page', PageSchema);
};

Middleware

All middleware files should return a function that takes two optional parameters, config and mongoose. That function should in turn return an object with middleware functions, or a single middleware function.

Single middleware in one file:

module.exports = function(config, mongoose) {
  return function(req, res, next) {
    res.send('secret/pron/stash');
  };
};

If this file was placed in `PWD/server/middleware/api/pron', it would namespace like this:

{
	api: {
		pron: [ Function ]
	}
}

Multiple middlewares in one file:

module.exports = function(config, mongoose) {
  return {
    index: function(req, res, next) {
      res.send('secret/pron/stash');
    },
    ballsack1: function(req, res, next) {
      res.send('secret/pron/stash');
    },
    anotherBallsack: function(req, res, next) {
      res.send('secret/pron/stash');
    }
  };
};

If this file was placed in `PWD/server/middleware/boo/far', it would namespace like this:

{
	boo: {
		far: {
			index: [ Function ],
			ballsack1: [ Function ],
			anotherBallsack: [ Function ]
		}
	}
}

Routes

All route files should return an Array of routes, each route being a an array on it's own. Each route array contains the following

  1. The method (in lowercase)
  2. The path
  3. The middleware(s) (single or an Array)

'use' middlewares usually need to be placed before or after the verb specific routes. Therefore, instead of saying 'use', you write 'before' or 'after'.

Sample route file:

module.exports = function(mw, config) {
	return [
		[ 'before', null, mw.authorization.isAuthenticated],
		[ 'get', '/', mw.api.events.findAll ],
		[ 'post', '/', mw.api.events.create ],
		[ 'get', '/:id', mw.api.events.findById ],
		[ 'put', '/:id', mw.api.events.update ],
		[ 'delete', '/:id', mw.api.events.remove ],
		[ 'after', null, mw.eventResponder ],
	];
};

Templates

The template system in express has been overriden to use dust native functions for cache things.

All dust templates use their path to generate their name.

A template placed in server/templates/partials/login-form will be named partials/login-form.

Epiphany also sets up a route that enables the fetching of compiled templates at /templates/. To fetch the previously mentioned template we would make a request to /templates/partials/login-form. The route returns the name of the fetched template, and an array of compiled templates with the template itself and all of its dependencies.

At the Code Bureau we have set up Dust in the browser to automatically load templates from this route if it is not found in the cache. We do this with the following code:

dust.onLoad = function(name, callback) {
	// callback is a function provided by dust.
	// run console.log(callback.toString()) if you are interested in it's contents.
	function notFound() {
		callback(new Error('Template Not Found: ' + name));
	}
	// attempt to load the template using the templates route in Express.
	$.ajax({
		method: 'GET',
		url: '/templates/' + name,
		dataType: 'json',
		success: function(res) {
			// the templates route does not only return the specified temlate, but also
			// all templates it depends on. They are placed in the res.compiled array.
			if(res.compiled.length > -1) {
				_.each(res.compiled, dust.loadSource);
				// the specified template will always be the first item in the res.compiled array.
				callback(null, res.compiled[0]);
			} else {
				notFound();
			}
		},
		error: notFound
	});
};

Preware & Postware

Preware is middleware loaded before the routes, and postware is loaded after the routes.

Debugging

Epiphany (and most other server-side modules from TCB) uses the excellent Debug plugin.

To debug everything, simply set DEBUG=epiphany as an environment variable. To debug specific parts, set (for example) DEBUG=epiphany:loaders. Debuggable parts are currently:

  • epiphany:loaders
  • epiphany:errorhandler
  • epiphany:responder

epiphany's People

Contributors

vicgeralds avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.