¿Cómo estructurar una aplicación express.js?

102

¿Existe una convención común para dividir y modularizar el app.jsarchivo en una aplicación Express.js ? ¿O es común mantener todo en un solo archivo?

Eric el rojo
fuente
3
Ha habido gente que los ha dividido en rutas. También puede echar un vistazo a express-resources.
BRampersad
@Brandon_R ¿has probado recursos? Lo miré y pensé que se veía bien, pero todavía no he pateado los neumáticos.
Posibilidad de
1
Un poco más tarde, pero recientemente de código abierto un router para expreso que le permite romper la app.js bien introducción controladores + vistas etc. Véase: github.com/kishorenc/road
jeffreyveon

Respuestas:

82

Tengo el mío dividido de la siguiente manera:

~/app
|~controllers
| |-monkey.js
| |-zoo.js
|~models
| |-monkey.js
| |-zoo.js
|~views
| |~zoos
|   |-new.jade
|   |-_form.jade
|~test
|  |~controllers
|    |-zoo.js
|  |~models
|    |-zoo.js
|-index.js

Utilizo Exportaciones para devolver lo que es relevante. Por ejemplo, en los modelos que hago:

module.exports = mongoose.model('PhoneNumber', PhoneNumberSchema);

y luego, si necesito crear un número de teléfono, es tan simple como:

var PhoneNumber = require('../models/phoneNumber');
var phoneNumber = new PhoneNumber();

si necesito usar el esquema, entonces PhoneNumber.schema

(lo que supone que estamos trabajando desde la carpeta de rutas y necesitamos subir 1 nivel y luego bajar a los modelos)


EDITAR 4

El wiki express tiene una lista de frameworks construidos sobre él.

De ellos, creo que el matador de Twitter está bastante bien estructurado. De hecho, usamos un enfoque muy similar a cómo cargan partes de la aplicación.

derby.js también parece muy interesante. Es similar a un meteoro sin toda la publicidad y, de hecho, da crédito a quien se lo merece (en particular, nodo y expreso).


EDITAR 3

Si eres fanático de CoffeeScript (yo no lo soy) y reeeeaaaaaally quieres el L&F de Rails, también está Tower.js .


EDITAR 2

Si está familiarizado con Rails y no le importa que algunos conceptos se derramen, hay Locomotive . Es un marco ligero construido sobre Express. Tiene una estructura muy similar a RoR y transmite algunos de los conceptos más rudimentarios (como el enrutamiento).

Vale la pena echarle un vistazo incluso si no planea usarlo.


EDITAR 1

nodejs-express-mongoose-demo es muy similar a cómo he estructurado el mío. Echale un vistazo.

Oportunidad
fuente
2
¿A dónde va la lógica empresarial? ¿Alguna vez usas ayudantes para cosas como la autenticación?
Eric the Red
@ErictheRed si está familiarizado con el patrón MVC (rieles, Asp.Net mvc, etc.), entonces considero que mis rutas son mis controladores y todo encaja en su lugar después de eso. La lógica empresarial va en los modelos (aunque tengo dificultades con la validación y la mangosta). Para los ayudantes, utilizo Exportaciones en una biblioteca de utilidades interna simple que estoy juntando para las cosas que reutilizo.
Chance
Sería genial cargar una configuración de muestra en github para que la veamos. ¿Qué va en la carpeta / archivos de Rutas?
Chovy
1
@chovy Agregué un enlace a github.com/qed42/nodejs-express-mongoose-demo que tiene una estructura muy similar
Chance
Recomiendo evitar cualquier framework
inflado
9

Advertencia: código de referencia que pirateé para eliminar el nodo, funciona, pero está lejos de ser elegante o pulido.

Para ser más específico sobre la división app.js, tengo el siguiente archivo app.js

var express = require('express'),
    bootstrap = require('./init/bootstrap.js'),
    app = module.exports = express.createServer();

bootstrap(app);

Básicamente, esto significa que coloco todo mi bootstrapping en un archivo separado, luego arranco el servidor.

Entonces, ¿qué hace bootstrap ?

var configure = require("./app-configure.js"),
    less = require("./watch-less.js"),
    everyauth = require("./config-everyauth.js"),
    routes = require("./start-routes.js"),
    tools = require("buffertools"),
    nko = require("nko"),
    sessionStore = new (require("express").session.MemoryStore)()

module.exports = function(app) {
    everyauth(app);
    configure(app, sessionStore);
    less();
    routes(app, sessionStore);
    nko('/9Ehs3Dwu0bSByCS');


    app.listen(process.env.PORT);
    console.log("server listening on port xxxx");
};

Bueno, divide toda la configuración de inicialización del servidor en buenos trozos. Específicamente

  • Tengo un fragmento que configura toda mi autenticación remota de OAuth usando everyauth.
  • Tengo un fragmento que configura mi aplicación (básicamente llamando app.configure)
  • Tengo un poco de código que perfora menos, por lo que vuelve a compilar cualquiera de mis menos en css en tiempo de ejecución.
  • Tengo un código que configura todas mis rutas.
  • Yo llamo a este pequeño módulo nko
  • Finalmente inicio el servidor escuchando un puerto.

Por ejemplo, veamos el archivo de enrutamiento

var fs = require("fs"),
    parseCookie = require('connect').utils.parseCookie;

module.exports = function(app, sessionStore) {
    var modelUrl = __dirname + "/../model/",
        models = fs.readdirSync(modelUrl),
        routeUrl = __dirname + "/../route/"
        routes = fs.readdirSync(routeUrl);

Aquí cargo todos mis modelos y rutas como matrices de archivos.

Descargo de responsabilidad: readdirSync solo está bien cuando se llama antes de iniciar el servidor http (antes .listen). Llamar a llamadas de bloqueo sincrónico a la hora de inicio del servidor solo hace que el código sea más legible (es básicamente un truco)

    var io = require("socket.io").listen(app);

    io.set("authorization", function(data, accept) {
        if (data.headers.cookie) {
            data.cookie = parseCookie(data.headers.cookie);

            data.sessionId = data.cookie['express.sid'];

            sessionStore.get(data.sessionId, function(err, session) {

                if (err) {
                    return accept(err.message, false);
                } else if (!(session && session.auth)) {
                    return accept("not authorized", false)
                }
                data.session = session;
                accept(null, true);
            });
        } else {
            return accept('No cookie', false);
        }
    });

Aquí presiono socket.io para usar la autorización en lugar de dejar que cualquier tom y jack hable con mi servidor socket.io

    routes.forEach(function(file) {
        var route = require(routeUrl + file),
            model = require(modelUrl + file);

        route(app, model, io);
    });
};

Aquí comienzo mis rutas pasando el modelo relevante en cada objeto de ruta devuelto desde el archivo de ruta.

Básicamente, lo importante es que organizas todo en pequeños módulos agradables y luego tienes algún mecanismo de arranque.

Mi otro proyecto (mi blog) tiene un archivo de inicio con una estructura similar .

Descargo de responsabilidad: el blog está roto y no se construye, estoy trabajando en ello.

Raynos
fuente
0

Tengo mis aplicaciones construidas sobre la herramienta de generación rápida. Puede instalarlo ejecutándolo npm install express-generator -gy ejecutándolo usando express <APP_NAME>.

Para darle una perspectiva, una de las estructuras de mi aplicación más pequeña se veía así:

~/
|~bin
| |-www
|
|~config
| |-config.json
|
|~database
| |-database.js
|
|~middlewares
| |-authentication.js
| |-logger.js
|
|~models
| |-Bank.js
| |-User.js
|
|~routes
| |-index.js
| |-banks.js
| |-users.js
|
|~utilities
| |-fiat-converersion.js
|
|-app.js
|-package.json
|-package-lock.json

Una cosa interesante que me gusta de esta estructura que termino adoptando para cualquier aplicación express que desarrolle es la forma en que se organizan las rutas. No me gustó tener que requerir los archivos de cada ruta en el app.js y app.use()cada ruta, especialmente a medida que el archivo crece. Como tal, me resultó útil agrupar y centralizar todos mis app.use()archivos en un archivo ./routes/index.js.

Al final, mi app.js se verá así:

...
const express = require('express');
const app = express();

...
require('./routes/index')(app);

y mi ./routes/index.js se verá así:

module.exports = (app) => {
  app.use('/users', require('./users'));
  app.use('/banks', require('./banks'));
};

Puedo hacerlo simplemente require(./users)porque escribí la ruta de los usuarios usando express.Router () que me permite "agrupar" múltiples rutas y luego exportarlas a la vez, con el objetivo de hacer la aplicación más modular.

Este es un ejemplo de lo que estaría bien en mi ruta ./routers/users.js:


const router = require('express').Router();

router.post('/signup', async (req, res) => {
    // Signup code here
});

module.exports = router;

¡Esperamos que esto haya ayudado a responder su pregunta! ¡La mejor de las suertes!

JKleinne
fuente