Come strutturare un'applicazione express.js?


102

Esiste una convenzione comune per suddividere e modulare il app.jsfile in un'applicazione Express.js ? O è comune conservare tutto in un unico file?


3
Ci sono state persone che li hanno suddivisi in percorsi. Inoltre puoi dare un'occhiata a express-resources.
BRampersad

@Brandon_R hai provato le risorse? L'ho guardato e ho pensato che fosse pulito, ma non ho ancora preso a calci le gomme.
Possibilità

1
Un po 'tardi, ma di recente ho reso open source un router per express che ti consente di rompere i controller di app.js ben intro + visualizzazioni ecc. Vedi: github.com/kishorenc/road
jeffreyveon

Risposte:


82

Ho il mio rotto come segue:

~/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

Uso le esportazioni per restituire ciò che è rilevante. Ad esempio, nei modelli che faccio:

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

e quindi se ho bisogno di creare un numero di telefono, è semplice come:

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

se ho bisogno di usare lo schema, allora PhoneNumber.schema

(il che presuppone che stiamo lavorando dalla cartella dei percorsi e che sia necessario salire di 1 livello e poi scendere ai modelli)


MODIFICA 4

Il wiki express ha un elenco di framework costruiti sopra di esso.

Di questi, penso che il matador di Twitter sia strutturato abbastanza bene. In realtà abbiamo utilizzato un approccio molto simile al modo in cui caricano parti dell'app.

anche derby.js sembra estremamente interessante. È simile a meteor senza tutto il clamore e in realtà dà credito a dove è dovuto (in particolare, nodo ed espresso).


MODIFICA 3

Se sei un fan di CoffeeScript (io non lo sono) e vuoi reeeeaaaaaally il L&F di Rails, c'è anche Tower.js .


MODIFICA 2

Se hai familiarità con Rails e non ti dispiace il sanguinamento di alcuni concetti, c'è Locomotive . È un framework leggero costruito su Express. Ha una struttura molto simile a RoR e trasporta alcuni dei concetti più rudimentali (come il routing).

Vale la pena dare un'occhiata anche se non prevedi di usarlo.


MODIFICA 1

nodejs-express-mongoose-demo è molto simile a come ho strutturato il mio. Controlla.


2
Dove va a finire la logica aziendale? Usi mai gli helper per cose come l'autenticazione?
Eric il Rosso

@ErictheRed, se hai familiarità con il pattern MVC (rails, Asp.Net mvc, ecc.), Considero le mie rotte come i miei controller e dopo tutto va a posto. La logica aziendale va nei modelli (anche se ho difficoltà con la convalida e la mangusta). Per gli helper, utilizzo le esportazioni su una semplice libreria di utilità interna che sto mettendo insieme per me stesso per le cose che riutilizzo.
Possibilità

Sarebbe bello caricare una configurazione di esempio su GitHub per farci esaminare. Cosa va nella cartella / file dei percorsi?
chovy

1
@chovy ho aggiunto un collegamento a github.com/qed42/nodejs-express-mongoose-demo che ha una struttura molto simile
Chance

Consiglio di evitare qualsiasi framework gonfio costruito su express
Raynos

9

Attenzione: facendo riferimento al codice che ho hackerato insieme per il knockout del nodo, in qualche modo funziona ma è tutt'altro che elegante o raffinato.

Per essere più specifici sulla divisione, app.jsho il seguente file app.js

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

bootstrap(app);

Questo significa fondamentalmente che metto tutto il mio bootstrap in un file separato, quindi avvio il server.

Allora cosa fa il 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");
};

Bene, divide tutte le impostazioni di inizializzazione del server in bei pezzi. In particolare

  • Ho una parte che configura tutta la mia autenticazione OAuth remota utilizzando everyauth.
  • Ho un blocco che configura la mia applicazione (fondamentalmente chiamando app.configure)
  • Ho un po 'di codice che colpisce di meno, quindi ricompila il mio meno in CSS in fase di esecuzione.
  • Ho un codice che imposta tutti i miei percorsi
  • Io chiamo questo piccolo modulo nko
  • Infine avvio il server ascoltando una porta.

Solo per esempio diamo un'occhiata al file di instradamento

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);

Qui carico tutti i miei modelli e percorsi come array di file.

Disclaimer: readdirSync va bene solo se chiamato prima di avviare il server http (prima .listen). Chiamare chiamate di blocco sincroniche all'avvio del server rende il codice più leggibile (è fondamentalmente un hack)

    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);
        }
    });

Qui premo socket.io per utilizzare effettivamente l'autorizzazione piuttosto che lasciare che qualsiasi tom e jack parli con il mio server socket.io

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

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

Qui inizio i miei percorsi passando il modello pertinente in ogni oggetto di percorso restituito dal file di percorso.

Fondamentalmente il succo è che organizzi tutto in piccoli moduli carini e poi hai un meccanismo di bootstrap.

L'altro mio progetto (il mio blog) ha un file init con una struttura simile .

Disclaimer: il blog è rotto e non si costruisce, ci sto lavorando.



0

Ho le mie app basate sullo strumento generatore di espresso. Puoi installarlo eseguendolo npm install express-generator -ged eseguirlo usando express <APP_NAME>.

Per darti una prospettiva, una delle strutture della mia applicazione più piccola era simile a questa:

~/
|~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 interessante che mi piace di questa struttura che finisco per adottare per qualsiasi applicazione express che sviluppo è il modo in cui sono organizzati i percorsi. Non mi piaceva dover richiedere ogni file di percorso in app.js e app.use()ogni percorso, soprattutto quando il file diventa più grande. In quanto tale, ho trovato utile raggruppare e centralizzare tutto il mio app.use()su un file ./routes/index.js.

Alla fine, il mio app.js avrà un aspetto simile a questo:

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

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

e il mio ./routes/index.js avrà un aspetto simile a questo:

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

Sono in grado di farlo semplicemente require(./users)perché ho scritto il percorso degli utenti utilizzando express.Router () che mi permette di "raggruppare" più percorsi e di esportarli contemporaneamente, con l'obiettivo di rendere l'applicazione più modulare.

Questo è un esempio di cosa andresti bene sul mio percorso ./routers/users.js:


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

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

module.exports = router;

Spero che questo abbia aiutato a rispondere alla tua domanda! Buona fortuna!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.