MR шаблон для написания сервисов на Node.js

image
Суть такова: node.js не дает готового решения для создания проекта. Первый мой проект на node.js состоял из одного coffeescript файла и run.js для запуска из IDE. Когда роутов было пять штук, все было замечательно, но когда проект оброс моделями и роутами, это превратилось в ад. Решают эту проблему разными способами, кто-то используй hub, кто-то global, кто-то все в один файл заносит.

Внимание: решение не претендует быть единственным правильным и вообще правильным. Просто решил поделиться. Срач на тему node.js vs erlang прошу не разводить, они в разных категориях.

Для нетерпеливых есть проект на гитхабе.

MR — Model Route, View я опускаю потому, что res.json это не view, а сервису большего и не надо.

Структура проекта:
├── app.coffee
├── config.coffee
├── models
│   └── example.coffee

├── package.json
├── public
│   ├── images
│   ├── javascripts
│   └── stylesheets
├── routes
│   ├── index.coffee
│   └── root.coffee
├── run-cluster.js
└── run.js


app.coffee

Это точка входа в приложение.
exports.start = () ->
  log     = require('logule') # Удобный логер
  log.debug "Spawning new worker"
  require 'coffee-script' # нужно для того, чтобы нода переваривала .coffee файлы в require
  ###
     Basic dependencies
  ###
  express       = require 'express'
  app           = module.exports = express.createServer()
  app.__        = require 'underscore' # на всякий случай
  app.mongoose  = require 'mongoose' # ODM для MongoDb, and yes, it's web scale
  app.log       = log # костыль
  config        = require('./config.coffee')(app,express) # конфигурация приложения 

  models        = {} # Сюда складываются все модели
  models.example= require('./models/example')(app.mongoose).model # Пример модели

  require('./routes/index.coffee')(app, models) # Файл, в котором прописаны все маршруты
  port = process.env.PORT || 5000 # Для Heroku и других схем деплоя
  app.listen port
  log.info "Express server listening on port #{port}"

config.coffee

module.exports = (app, express) ->
  config = this
  app.configure ->
    app.use express.bodyParser()
    app.use express.methodOverride()
    app.use app.router
    app.use express.static(__dirname + '/public')
  app.configure 'development', () ->
    app.use express.errorHandler({dumpExveption: true, showStack: true}) # "полезный" дамп стека
    app.mongoose.connect process.env.MONGOLAB_URI || "mongodb://localhost/skel" # Для heroku 
  app.configure 'production', () ->
    app.use express.errorHandler()
    app.mongoose.connect "mongodb://localhost/production"
  return config

models/example.coffee

module.exports = (mongoose) ->
  Schema    = mongoose.Schema
  ObjectId  = Schema.ObjectId
  #Comment   = mogoose.model('Comment') # пример того, как получить модель которая уже ассоциирована с mongoose.
  PostSchema = new Schema {
    author  : { type:ObjectId, index:true }
    date    : Date,
    content : String
    #, comments : [Comment] # пример того как ее использовать
  }
  this.model = mongoose.model('Post', PostSchema) # ассоциирование
  
  return this

package.json

engines секция это для Heroku, все остальное понятно.
{
    "name": "mr-skel"
  , "version": "0.0.1"
  , "private": true
  , "engines": {
      "node": "0.6.x",
      "npm":  "1.x.x"
  }
  , "dependencies": {
      "express": "2.5.x"
    , "mongoose": "2.6.x"
    , "mongoose-types": "*"
    , "underscore": "*"
    , "coffee-script": "*"
    , "logule": "0.x.x"
  },
  "devDependencies": {
      "forever": "*"
    , "mocha": "*"
    , "should": "*"
  }
}




routes/index.coffee

В этом файле хранятся все роуты которые есть в приложении.
routes = {}
routes.root = require('./root.coffee')
module.exports = (app, models) ->
  app.get '/', routes.root.getIndex(models) # Насамом деле это костыль, я не смог придумать как сделать models доступной для всех роутов из root.cofee

routes/root.coffee

exports.getIndex = (models) ->
  (req, res) ->
    Post  = models.example
    Post.find {}, (err, posts) ->
      if err?
        return res.send 500
      res.json { status: 'Ba dum tssh', meta: posts }

run-cluster.js

Запуск приложения при помощи Cluster API. На Heroku не работает, но на своем vps я запускаю приложение именно так.
cluster = require('cluster');
log     = require('logule');
require('coffee-script');

if (cluster.isMaster) {
  log.info("Master have just started");
  # i = кол-во ядер
  for (i = 1; i <= 2; i++) {
    cluster.fork();
  }
  cluster.on('death', function(worker) {
    log.error("Worker " + worker.pid + " died!");
    return cluster.fork();
  });
} else {
  app = require('./app.coffee');
  app.start()
}

run.js

require('coffee-script');

app = require('./app.coffee');
app.start();
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 13

    +1
    Просьба не начинать холивар на тему erlang'a и ноды не распространяется на mongodb is webscale…
    +1
    Каковые ваши впечатления от mongoose? Я пока только негатив читал. В итоге написал собственный велосипед с использованием fibers.
      +1
      Если закрыть глаза на то как создаются модели — нормально. Ну и документация в некоторых местах отстает.
      +1
      Если вы подключили 'coffee-script', то можно просто
      require('app');
      require('./config')(app,express) # конфигурация приложения 
      

        0
        Я знаю, у меня просто еще есть config.js :)
        0
        а в чём преимущество разделения маршрутизации на два файла?

        чем не устраивает такой способ? —
        app.post('/somepath', function (req, res) {    
            p = { subgroup_id: req.body.subgroup_id, order: 'status' };
            itemStock.getList(p, function (data) {
                res.json(data);
            });    
        });
        
        app.get('/socketest', function (req, res) {
            res.render('socketest', {});
        });
        
          0
          Когда роутов много

          app.get '/', routes.root.getIndex(models)
          app.get '/orders', routes.orders.getIndex(models)
          app.post '/orders', routes.orders.postIndex(models)
          app.get '/clients', routes.clients.getIndex(models)
          app.post '/clients', routes.clients.postIndex(models)
          


          Считается проще. А разделение на файлы это просто для удобства. Каждый ресурс имеет свой фаил, визуально не грузит так сильно.
            0
            Читается*
              0
              ну то есть типа того:
              app.get( '/', middleWare_prolongSession, index);
              app.get( '/login', loginGet);
              app.get( '/logout', middleWare_isUser, logout);
              
              // ...
              
              function logout (req, res) {
                  auth.logOut(req, res);
                  res.redirect('/');              
              }
              

              впрочем, опять же, оставлять это в одном файле или раскидывать на два — исключительно дело вкуса.

              ps. ностальгирую по джанговскому роутингу.
                0
                На самом деле виной такой разбивке — Webstorm, на каждый чих он переиндексирует весь фаил.

          Only users with full accounts can post comments. Log in, please.