So schreiben Sie eine produktionsbereite Node- und Express-App

Projektstrukturierung

Als ich anfing, Node & Express-Anwendungen zu erstellen, wusste ich nicht, wie wichtig es ist, Ihre Anwendung zu strukturieren. Express enthält keine strengen Regeln oder Richtlinien für die Aufrechterhaltung der Projektstruktur.

Sie können jede gewünschte Struktur verwenden. Wenn Ihre Codebasis wächst, haben Sie lange routeHandler. Dies macht Ihren Code schwer verständlich und enthält potenzielle Fehler.

Wenn Sie für ein Startup arbeiten, haben Sie die meiste Zeit keine Zeit, Ihr Projekt zu refraktieren oder zu modularisieren. Sie können mit einer endlosen Schleife von Fehlerbehebungen und Patches enden.

Im Laufe der Zeit wurde mir bei der Arbeit mit kleinen und großen Teams klar, welche Art von Struktur mit Ihrem Projekt wachsen und dennoch einfach zu warten ist.

Model View Controller

Das MVC-Muster hilft bei der schnellen und parallelen Entwicklung. Beispielsweise kann ein Entwickler an der Ansicht arbeiten, während ein anderer an der Erstellung der Geschäftslogik in der Steuerung arbeiten kann.

Schauen wir uns ein Beispiel einer einfachen Benutzer-CRUD-Anwendung an.

project/ controllers/ users.js util/ plugin.js middlewares/ auth.js models/ user.js routes/ user.js router.js public/ js/ css/ img/ views/ users/ index.jade tests/ users/ create-user-test.js update-user-test.js get-user-test.js .gitignore app.js package.json
  • Controller: Definieren Sie Ihre App-Routen-Handler und Ihre Geschäftslogik
  • util: Schreibt hier Dienstprogramm- / Hilfsfunktionen , die von allen Controllern verwendet werden können. Zum Beispiel können Sie eine Funktion wie schreiben mergeTwoArrays(arr1, arr2).
  • Middleware: Sie können Middleware schreiben, um alle eingehenden Anforderungen zu interpretieren, bevor Sie zum Routenhandler wechseln. Zum Beispiel,

    router.post('/login', auth, controller.login)Wo authist eine Middleware-Funktion definiert in middlewares/auth.js.

  • Modelle: auch eine Art Middleware zwischen Ihrem Controller und der Datenbank. Sie können ein Schema definieren und eine Validierung durchführen, bevor Sie in die Datenbank schreiben. Sie können beispielsweise ein ORM wie Mongoose verwenden, das über hervorragende Funktionen und Methoden verfügt, die im Schema selbst verwendet werden können
  • Routen: Definieren Sie Ihre App-Routen mit HTTP-Methoden. Sie können beispielsweise alles definieren, was mit dem Benutzer zusammenhängt.
router.post('/users/create', controller.create) router.put('/users/:userId', controller.update) router.get('/users', controller.getAll)
  • public: Speichern Sie statische Bilder in /imgbenutzerdefinierten JavaScript-Dateien und CSS/css
  • Ansichten: Enthält Vorlagen, die vom Server gerendert werden sollen.
  • Tests: Hier können Sie alle Komponententests oder Abnahmetests für den API-Server schreiben.
  • app.js: Dient als Hauptdatei des Projekts, in der Sie die App und andere Elemente des Projekts initialisieren.
  • package.json: Kümmert sich um die Abhängigkeiten, die mit dem npmBefehl auszuführenden Skripte und die Version Ihres Projekts.

Ausnahmen und Fehlerbehandlung

Dies ist einer der wichtigsten Aspekte, über die Sie nachdenken müssen, wenn Sie ein Projekt mit einer beliebigen Sprache erstellen. Lassen Sie uns sehen, wie Fehler und Ausnahmen in einer Express-App ordnungsgemäß behandelt werden.

Versprechen verwenden

Einer der Vorteile der Verwendung von Versprechungen gegenüber Rückrufen besteht darin, dass sie implizite oder explizite Ausnahmen / Fehler in asynchronen Codeblöcken sowie für synchronen Code, der in .then()einem Versprechungsrückruf definiert ist , behandeln können

Fügen Sie einfach .catch(next)am Ende der Versprechenskette hinzu. Zum Beispiel:

router.post('/create', (req, res, next) => { User.create(req.body) // function to store user data in db .then(result => { // do something with result return result }) .then(user => res.json(user)) .catch(next) })

Try-Catch verwenden

Try-Catch ist eine traditionelle Methode zum Abfangen von Ausnahmen in asynchronem Code.

Schauen wir uns ein Beispiel mit der Möglichkeit einer Ausnahme an:

router.get('/search', (req, res) => { setImmediate(() => { const jsonStr = req.query.params try { const jsonObj = JSON.parse(jsonStr) res.send('Success') } catch (e) { res.status(400).send('Invalid JSON string') } }) })

Vermeiden Sie die Verwendung von synchronem Code

Synchroner Code wird auch als Blockierungscode bezeichnet, da er die Ausführung blockiert, bis sie ausgeführt werden.

Vermeiden Sie daher die Verwendung synchroner Funktionen oder Methoden, die Millisekunden oder Mikrosekunden dauern können. Bei einer Website mit hohem Datenaufkommen wird dies zusammengesetzt und kann zu einer hohen Latenz oder Antwortzeit der API-Anforderungen führen.

Verwenden Sie sie nicht besonders in der Produktion :)

Viele Node.js Module kommen mit den beiden .syncund .asyncMethoden, so Verwendung asynchroner in der Produktion.

Wenn Sie jedoch weiterhin eine synchrone API verwenden möchten, verwenden Sie das --trace-sync-ioBefehlszeilenflag. Es wird eine Warnung und eine Stapelverfolgung gedruckt, wenn Ihre Anwendung eine synchrone API verwendet.

Weitere Informationen zu den Grundlagen der Fehlerbehandlung finden Sie unter:

  • Fehlerbehandlung in Node.js.
  • Erstellen robuster Knotenanwendungen: Fehlerbehandlung (StrongLoop-Blog)
Was Sie nicht tun sollten , ist auf das uncaughtExceptionEreignis zu warten, das ausgegeben wird, wenn eine Ausnahme bis zur Ereignisschleife zurückspringt. Die Verwendung ist im Allgemeinen nicht bevorzugt.

Richtig protokollieren

Die Protokollierung ist für das Debuggen und die App-Aktivität unerlässlich. Es wird hauptsächlich für Entwicklungszwecke verwendet. Wir verwenden console.logund console.erroraber das sind synchrone Funktionen.

Für Debugging-Zwecke

Sie können ein Modul wie Debug verwenden. Mit diesem Modul können Sie die Umgebungsvariable DEBUG verwenden, um zu steuern, an welche Debug-Nachrichten console.err()gegebenenfalls gesendet wird.

Für App-Aktivitäten

Eine Möglichkeit besteht darin, sie in die Datenbank zu schreiben.

Lesen Sie, wie ich Mungo-Plugins verwendet habe, um meine Anwendung zu prüfen.

Another way is to write to a file OR use a logging library like Winston or Bunyan. For a detailed comparison of these two libraries, see the StrongLoop blog post Comparing Winston and Bunyan Node.js Logging.

require(“./../../../../../../”) mess

There are different workarounds for this problem.

If you find any module getting popular and if it has logical independence from the application, you can convert it to private npm module and use it like any other module in package.json.

OR

const path = require('path'); const HOMEDIR = path.join(__dirname,'..','..');

where __dirname is the built-in variable that names the directory that contains the current file, and .. ,..is the requisite number of steps up the directory tree to reach the root of the project.

From there it is simply:

const foo = require(path.join(HOMEDIR,'lib','foo')); const bar = require(path.join(HOMEDIR,'lib','foo','bar'));

to load an arbitrary file within the project.

Let me know in the comment below if you have better ideas :)

Set NODE_ENV to “production”

The NODE_ENV environment variable specifies the environment in which an application is running (usually, development or production). One of the simplest things you can do to improve performance is to set NODE_ENVto “production.”

Setting NODE_ENV to “production” makes Express:

  • Cache view templates.
  • Cache CSS files generated from CSS extensions.
  • Generate less verbose error messages.

Tests indicate that just doing this can improve app performance by a factor of three!

Using Process Manager

For production, you should not simply use node app.j — if your app crashes, it will be offline until you restart it.

The most popular process managers for Node are:

  • StrongLoop Process Manager
  • PM2
  • Forever

I personally use PM2.

For a feature-by-feature comparison of the three process managers, see //strong-pm.io/compare/. For a more detailed introduction to all three, see Process managers for Express apps.

Run your app in a cluster

In a multi-core system, you can increase the performance of a Node app by many times by launching a cluster of processes.

A cluster runs multiple instances of the app, ideally one instance on each CPU core. This distributes the load and tasks among the instances.

Using Node’s cluster module

Clustering is made possible with Node’s cluster module. This enables a master process to spawn worker processes. It distributes incoming connections among the workers.

However, rather than using this module directly, it’s far better to use one of the many tools out there that do it for you automatically. For example node-pm or cluster-service.

Using PM2

For pm2 you can use cluster directly through a command. For example,

# Start 4 worker processes pm2 start app.js -i 4 # Auto-detect number of available CPUs and start that many worker processes pm2 start app.js -i max 

If you encounter any problems, feel free to get in touch or comment below.

I would be happy to help :)

Don’t hesitate to clap if you considered this a worthwhile read!

References: //expressjs.com/en/advanced/best-practice-performance.html

Originally published at 101node.io on September 30, 2018.