Comment structurer une application express.js?

102

Existe-t-il une convention commune pour la division et la modularisation du app.jsfichier dans une application Express.js ? Ou est-il courant de tout conserver dans un seul fichier?

Eric le Rouge
la source
3
Il y a eu des gens qui les ont divisés en itinéraires. Vous pouvez également jeter un œil aux ressources express.
BRampersad
@Brandon_R avez-vous essayé des ressources? Je l'ai jeté un coup d'œil et j'ai trouvé qu'il avait l'air bien, mais je n'ai pas encore mis les pneus à pied.
Chance du
1
Un peu tard, mais j'ai récemment ouvert un routeur pour express qui vous permet de briser joliment les contrôleurs d'intro app.js + vues, etc. Voir: github.com/kishorenc/road
jeffreyveon

Réponses:

82

J'ai le mien brisé comme suit:

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

J'utilise Exportations pour renvoyer ce qui est pertinent. Par exemple, dans les modèles que je fais:

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

et puis si j'ai besoin de créer un numéro de téléphone, c'est aussi simple que:

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

si j'ai besoin d'utiliser le schéma, alors PhoneNumber.schema

(ce qui suppose que nous travaillons à partir du dossier routes et que nous devons monter d'un niveau, puis descendre aux modèles)


MODIFIER 4

Le wiki express a une liste de frameworks construits dessus.

Parmi ceux-ci, je pense que le matador de Twitter est plutôt bien structuré. Nous avons en fait utilisé une approche très similaire pour charger certaines parties de l'application.

derby.js semble également extrêmement intéressant. Cela s'apparente à un météore sans tout le battage médiatique et donne en fait du crédit là où le crédit est dû (notamment, nœud et express).


MODIFIER 3

Si vous êtes fan de CoffeeScript (je ne le suis pas) et que vous voulez vraiment le L&F de Rails, il y a aussi Tower.js .


MODIFIER 2

Si vous êtes familier avec les rails et que cela ne vous dérange pas de perdre certains concepts, il y a Locomotive . C'est un cadre léger construit sur Express. Il a une structure très similaire à RoR et reprend certains des concepts les plus rudimentaires (tels que le routage).

Cela vaut la peine de vérifier même si vous ne prévoyez pas de l'utiliser.


MODIFIER 1

nodejs-express-mongoose-demo est très similaire à la façon dont j'ai le mien structuré. Vérifiez-le.

Chance
la source
2
Où va la logique métier? Avez-vous déjà utilisé des helpers pour des choses comme l'authentification?
Eric the Red
@ErictheRed si vous êtes familier avec le modèle MVC (rails, Asp.Net mvc, etc.) alors je considère mes routes comme mes contrôleurs et tout se met en place après cela. La logique métier entre dans les modèles (même si j'ai des difficultés avec la validation et la mangouste). Pour les assistants, j'utilise Exports sur une simple bibliothèque utilitaire interne que je mets en place pour les choses que je réutilise.
Chance du
Ce serait cool de télécharger un exemple de configuration sur github pour que nous puissions l'examiner. Que se passe-t-il dans le dossier / les fichiers Routes?
chovy
1
@chovy J'ai ajouté un lien vers github.com/qed42/nodejs-express-mongoose-demo qui a une structure très similaire
Chance
Je recommande d'éviter les frameworks gonflés construits sur express
Raynos
9

Attention: le code de référencement que j'ai piraté ensemble pour le knock-out des nœuds, cela fonctionne en quelque sorte mais est loin d'être élégant ou poli.

Pour être plus précis sur le fractionnement, app.jsj'ai le fichier app.js suivant

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

bootstrap(app);

Cela signifie essentiellement que je place tout mon bootstrapping dans un fichier séparé, puis je bootstrap le serveur.

Alors, que fait le 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");
};

Eh bien, il divise toute la configuration d'initialisation du serveur en jolis morceaux. Plus précisément

  • J'ai un morceau qui configure toute mon authentification OAuth à distance en utilisant everyauth.
  • J'ai un morceau qui configure mon application (appelant essentiellement app.configure)
  • J'ai un peu de code qui frappe moins, donc il recompile tout mon moins en css au moment de l'exécution.
  • J'ai du code qui configure tous mes itinéraires
  • J'appelle ce petit module nko
  • Enfin je lance le serveur en écoutant un port.

Par exemple, regardons le fichier de routage

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

Ici, je charge tous mes modèles et routes sous forme de tableaux de fichiers.

Clause de non-responsabilité: readdirSync n'est correct que lorsqu'il est appelé avant de démarrer le serveur http (avant .listen). L'appel d'appels de blocage synchrones au moment du démarrage du serveur rend simplement le code plus lisible (c'est essentiellement 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);
        }
    });

Ici, je frappe socket.io pour utiliser l'autorisation plutôt que de laisser n'importe quel tom et jack parler à mon serveur socket.io

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

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

Ici, je commence mes itinéraires en passant le modèle pertinent dans chaque objet d'itinéraire renvoyé par le fichier d'itinéraire.

Fondamentalement, le fait est que vous organisez tout dans de jolis petits modules et que vous disposez ensuite d'un mécanisme de démarrage.

Mon autre projet (mon blog) a un fichier init avec une structure similaire .

Avertissement: le blog est cassé et ne se construit pas, je travaille dessus.

Raynos
la source
0

J'ai mes applications construites au-dessus de l'outil de générateur express. Vous pouvez l'installer en l'exécutant npm install express-generator -get l'exécuter en utilisant express <APP_NAME>.

Pour vous donner une perspective, l'une des structures de ma plus petite application ressemblait à ceci:

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

Une chose que j'aime bien dans cette structure que je finis par adopter pour toute application express que je développe est la façon dont les itinéraires sont organisés. Je n'aimais pas avoir à exiger chaque fichier de route dans l'app.js et app.use()chaque route, d'autant plus que le fichier grossit . En tant que tel, j'ai trouvé utile de regrouper et de centraliser tous mes app.use()fichiers sur un fichier ./routes/index.js.

En fin de compte, mon app.js ressemblera à ceci:

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

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

et mon ./routes/index.js ressemblera à ceci:

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

Je suis capable de le faire simplement require(./users)parce que j'ai écrit la route des utilisateurs en utilisant express.Router () qui me permet de "grouper" plusieurs routes et de les exporter en même temps, dans le but de rendre l'application plus modulaire.

Voici un exemple de ce que vous feriez bien sur mon itinéraire ./routers/users.js:


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

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

module.exports = router;

J'espère que cela a aidé à répondre à votre question! Bonne chance!

JKleinne
la source