gitkeep

Git does not track empty directory. In order to track a directory, a file must exist within it. The trick that many people use is to create a hidden file usually called .gitkeep. The name of the file does not matter. As long as there is a file in the directory .gitkeep, readme.txt etc, git will track directory.

App

One of the main directories. Here you will find all the development structures of your application.

Assets

Directory where all assets files are located.

Images

Directory where all images are located.

JavaScripts

Directory where all JavaScript files are located.

Stylesheets

Directory where all Stylus files are located.

style.styl

Stylus file example.

body
  padding 50px
  font 14px "Lucida Grande", Helvetica, Arial, sans-serif

a
  color #00B7FF

Controllers

Directory where all controllers files are located.

HomepagesController

Controller file example rendering a view.

See more at here
const HomepagesController = {

  index: (req, res) => {
    res.render('index', { title: 'Apogeu' });
  },

};

module.exports = HomepagesController;

UsersController

Controller file example CRUD api.

See more at here
const UsersController = {

  list: (req, res, next) => {
    UsersService
      .list(req.query)
      .then(data => res.status(200).json({ success: true, data }))
      .catch(next);
  },

  create: (req, res, next) => {
    UsersService
      .create(req.body)
      .then(data => res.status(200).json({ success: true, data }))
      .catch(next);
  },

  findById: (req, res, next) => {
    UsersService
      .findById(req.params.id)
      .then(data => res.status(200).json({ success: true, data }))
      .catch(next);
  },

  update: (req, res, next) => {
    UsersService
      .update(req.params.id, req.body)
      .then(data => res.status(200).json({ success: true, data }))
      .catch(next);
  },

  delete: (req, res, next) => {
    UsersService
      .delete(req.params.id)
      .then(() => res.status(200).json({ success: true }))
      .catch(next);
  },

};

module.exports = UsersController;

i18n

Directory where all translation files are located.

en.json

Translation file example.

See more at here
{
  "title": "Apogeu",
  "welcome": "Welcome to Apogeu"
}

Middlewares

Directory where all middlewares files are located.

LogMiddleware

Middleware file example.

See more at here
const log = require('winston');

const LogMiddleware = (req, res, next) => {
  log.debug('log middleware');
  next();
};

module.exports = LogMiddleware;

Models

Directory where all models files are located.

UserModel

Model file example.

See more at here
const mongoose = require('mongoose');

const Schema = mongoose.Schema;

const UserModel = new Schema({
  name: {
    type: String,
    required: true,
  },
});

module.exports = mongoose.model('User', UserModel);

Services

Directory where all services files are located.

UsersService

Service file example CRUD.

See more at here
const UsersService = {

  list: (query = {}) => {
    const { limit, skip, sort } = query;
    return UserModel
      .find({})
      .limit(limit)
      .skip(skip)
      .sort(sort);
  },

  create: (body = {}) => {
    const model = new UserModel(body);
    return model.save();
  },

  findById: _id => new Promise((resolve, reject) => {
    UserModel
      .findOne({ _id })
      .then((data) => {
        if (!data) return reject({ success: false, message: 'Not found', status: 404 });
        resolve(data);
      })
      .catch(reject);
  }),

  update: (_id, body) => new Promise((resolve, reject) => {
    UsersService.findById(_id)
      .then((user) => {
        Object.assign(user, body);
        user.save().then(resolve).catch(reject);
      })
      .catch(reject);
  }),

  delete: _id => new Promise((resolve, reject) => {
    UsersService.findById(_id)
      .then((user) => {
        user.remove().then(resolve).catch(reject);
      })
      .catch(reject);
  }),

};

module.exports = UsersService;

Views

Directory where all views (pug) files are located.

error.pug

Error page example.

See more at here
extends layout

block content
  h1= message
  h2= error.status
  pre #{error.stack}

index.pug

Index page example.

See more at here
extends layout

block content
 h1= __('title')
 p= __('welcome')

layout.pug

Layout example.

See more at here
doctype html
html
  head
    title= title
    link(rel='stylesheet', href='/public/stylesheets/style.css')
  body
    block content

Config

Directory where all config files are located

Scaffold

Directory where all scaffold files are located

See more at here

Scaffold controller

Scaffold to create a new controller

See more at here
const {{model}}Controller = {

  index: (req, res) => {
    res.send('{{model}}Controller');
  },

};

module.exports = {{model}}Controller;

Scaffold controllerCrud

Scaffold to create a new controller crud

See more at here
const {{model}}Controller = {

  list: (req, res, next) => {
    {{model}}Service
      .list(req.query)
      .then(data => res.status(200).json({ success: true, data }))
      .catch(next);
  },

  create: (req, res, next) => {
    {{model}}Service
      .create(req.body)
      .then(data => res.status(200).json({ success: true, data }))
      .catch(next);
  },

  findById: (req, res, next) => {
    {{model}}Service
      .findById(req.params.id)
      .then(data => res.status(200).json({ success: true, data }))
      .catch(next);
  },

  update: (req, res, next) => {
    {{model}}Service
      .update(req.params.id, req.body)
      .then(data => res.status(200).json({ success: true, data }))
      .catch(next);
  },

  delete: (req, res, next) => {
    {{model}}Service
      .delete(req.params.id)
      .then(() => res.status(200).json({ success: true }))
      .catch(next);
  },

};

module.exports = {{model}}Controller;

Scaffold middleware

Scaffold to create a new middleware

See more at here
const {{model}}Middleware = (req, res, next) => {
  next();
};

module.exports = {{model}}Middleware;

Scaffold model

Scaffold to create a new model

See more at here
const mongoose = require('mongoose');

const Schema = mongoose.Schema;

const {{model}}Model = new Schema({

});

module.exports = mongoose.model('{{model}}', {{model}}Model);

Scaffold service

Scaffold to create a new service

See more at here
const {{model}}Service = {

  index: () => new Promise((resolve, reject) => {
  }),

};

module.exports = {{model}}Service;

Database

Here your database is defined. By default in Apogeu the database is MongoDB using the Mongoose module.

See default file:

const mongoose = require('mongoose');
const log = require('winston');
const mongooseTimestamps = require('mongoose-timestamp');

const envs = require('./envs');

const { timestamps, mongodb, node_env } = envs;

log.debug(`mongoose timestamps: ${timestamps}`);

mongoose.Promise = Promise;

if (timestamps) mongoose.plugin(mongooseTimestamps);

module.exports = () => new Promise((resolve, reject) => {
  const debugMongoose = node_env !== 'production';
  log.debug(`debug mongoose: ${debugMongoose}`);

  mongoose.set('debug', debugMongoose);

  mongoose.connect(mongodb);

  mongoose.connection.on('connected', () => log.info(`Mongoose default connection open to ${mongodb}`));

  mongoose.connection.on('error', err => reject(err));

  mongoose.connection.on('disconnected', () => log.info('Mongoose default connection disconnected'));

  mongoose.connection.once('open', () => {
    resolve();
    log.info('Mongoose default connection is open');
  });

  process.on('SIGINT', () => {
    mongoose.connection.close(() => {
      log.info('Mongoose default connection disconnected through app termination');
      process.exit(0);
    });
  });
});

Envs

Here is where all environments are declared

All possible environments:
NameTypeDefault valueDescription
node_envStringdevelopmentThe environment definition of your application. Possible values: development | test | production.
timestampsBooleantrueEnable ou disable the mongoose timestamps. See more in mongoose-timestamp. Possible values: true | false.
mongodbStringmongodb://localhost:27017/apogeuMongoose uri connection. See more in mongoose connections.
portNumber3000Port setting for your application.
loggerStringdevHTTP request logger middleware for node.js. See more in morgan
clusterNumberNumber of your cpuThe cluster module allows you to easily create child processes that all share server ports. See more in nodejs cluster

See default file:

module.exports = {

  node_env: 'development',

  timestamps: true,

  mongodb: 'mongodb://localhost:27017/apogeu',

};

Routes

Here is where all routes are declared

See default file:

// config/routes.js

module.exports = {
  'get /': HomepagesController.index,

  'get /middleware': [LogMiddleware, HomepagesController.index],

  '/users': [
    {
      method: 'get',
      middlewares: [LogMiddleware],
      controller: UsersController.list,
    },
    {
      method: 'post',
      controller: UsersController.create,
    },
  ],

  '/users/:id': [
    {
      method: 'get',
      controller: UsersController.findById,
    },
    {
      method: 'put',
      controller: UsersController.update,
    },
    {
      method: 'delete',
      controller: UsersController.delete,
    },
  ],

};

Public

Directory where all public files are located

Stylesheets

Directory where all css files are located.

All Stylus files are compiled to this location.

style.css

body{padding:50px;font:14px "Lucida Grande",Helvetica,Arial,sans-serif}a{color:#00b7ff}

test

Directory where all tests are created

integration

Directory where all integration tests are created

A integration test can be executed with the apogeu test integration command:

$ apogeu test integration

Example output console:

info: Mongoose default connection open to mongodb://localhost:27017/apogeu
info: Mongoose default connection is open
info: running integration tests
info: Application listening on port 3000

Mongoose: users.find({}, { limit: undefined, skip: undefined, fields: {} })
  ✓ list users

  1 passing (58ms)

Example integration test:

const assert = require('assert');

it('list users', (done) => {
  UsersService.list()
    .then((results) => {
      assert.equal(Array.isArray(results), true);
      done();
    });
});

unit

Directory where all unit tests are created

A unit test can be executed with the apogeu test unit command:

$ apogeu test unit

Example output console:

info: running unit tests

✓ user service is an object

1 passing (7ms)

Example unit test:

const assert = require('assert');

it('user service is an object', (done) => {
  assert.equal(typeof UsersService, 'object');
  done();
});

.editorconfig

EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs. The EditorConfig project consists of a file format for defining coding styles and a collection of text editor plugins that enable editors to read the file format and adhere to defined styles.

EditorConfig files are easily readable and they work nicely with version control systems.

# EditorConfig is awesome: http://EditorConfig.org

# top-most EditorConfig file
root = true

[*]
end_of_line = lf
insert_final_newline = true

[*.js]
indent_style = space
indent_size = 2

[Makefile]
indent_style = tab

[{package.json,.travis.yml}]
indent_style = space
indent_size = 2

.gitignore

A gitignore file specifies intentionally untracked files that Git should ignore. Files already tracked by Git are not affected.

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Typescript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env

#IntelliJ
.idea/
*.iml

index.js

In Apogeu, index.js is invoked at initialization where it receives parameter app

/*
*
* The app object conventionally denotes the Express application
*
*/

module.exports = (app) => {

};

package.json

A package.json file affords you a lot of great things:

  • It serves as documentation for what packages your project depends on.
  • It allows you to specify the versions of a package that your project can use using semantic versioning rules.
  • Makes your build reproducible which means that its way easier to share with other developers.

See more in:

Using a package.json

Specifics of npm's package.json handling

Default file:

{
  "name": "handson-test",
  "version": "0.0.0",
  "description": "",
  "dependencies": {
    "bluebird": "^3.5.0",
    "mongoose": "^4.8.5",
    "mongoose-timestamp": "^0.6.0",
    "winston": "^2.3.1"
  },
  "devDependencies": {
    "assert": "^1.4.1",
    "mocha": "^3.2.0",
    "eslint": "^3.17.0",
    "eslint-config-airbnb-base": "^11.1.1",
    "eslint-plugin-import": "^2.2.0"
  }
}

README.md

A README file, along with a repository license, contribution guidelines, and a code of conduct, helps you communicate expectations for and manage contributions to your project.

yarn.lock

Yarn is a package manager for your code. It allows you to use and share code with other developers from around the world. Yarn does this quickly, securely, and reliably so you don’t ever have to worry.