Πώς να γράψετε μια εφαρμογή Node and Express έτοιμη για παραγωγή

Δομή έργου

Όταν άρχισα να δημιουργώ εφαρμογές Node & Express, δεν ήξερα πόσο σημαντικό ήταν να δομήσει την εφαρμογή σας. Το Express δεν συνοδεύεται από αυστηρούς κανόνες ή οδηγίες για τη διατήρηση της δομής του έργου.

Είστε ελεύθεροι να χρησιμοποιήσετε οποιαδήποτε δομή θέλετε. Όταν μεγαλώνει η βάση κωδικών σας, καταλήγετε να έχετε μακροχρόνιους routeχειριστές. Αυτό καθιστά τον κώδικά σας δύσκολο να κατανοηθεί και περιέχει πιθανά σφάλματα.

Εάν εργάζεστε για μια εκκίνηση, τις περισσότερες φορές δεν θα έχετε χρόνο να διαθλάσετε το έργο σας ή να το διαμορφώσετε. Μπορείτε να καταλήξετε με έναν ατελείωτο βρόχο διόρθωσης σφαλμάτων και επιδιόρθωσης.

Με την πάροδο του χρόνου, ενώ εργαζόμουν τόσο με μικρές όσο και με μεγάλες ομάδες, συνειδητοποίησα τι είδους δομή μπορεί να αναπτυχθεί με το έργο σας και να είναι εύκολο να διατηρηθεί.

Ελεγκτής προβολής μοντέλου

Το πρότυπο MVC βοηθά στην ταχεία και παράλληλη ανάπτυξη. Για παράδειγμα, ένας προγραμματιστής μπορεί να εργαστεί στην προβολή, ενώ ένας άλλος μπορεί να εργαστεί για τη δημιουργία της επιχειρηματικής λογικής στον ελεγκτή.

Ας ρίξουμε μια ματιά σε ένα παράδειγμα μιας απλής εφαρμογής CRUD χρήστη.

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
  • ελεγκτές: Καθορίστε τους διαχειριστές διαδρομών της εφαρμογής σας και την επιχειρηματική λογική
  • util: Γράφει εδώ λειτουργίες βοηθητικού προγράμματος / βοηθού που μπορούν να χρησιμοποιηθούν από οποιονδήποτε ελεγκτή. Για παράδειγμα, μπορείτε να γράψετε μια συνάρτηση όπως mergeTwoArrays(arr1, arr2).
  • middlewares: Μπορείτε να γράψετε middlewares για να ερμηνεύσετε όλα τα εισερχόμενα αιτήματα πριν μεταβείτε στο πρόγραμμα χειρισμού διαδρομής. Για παράδειγμα,

    router.post('/login', auth, controller.login)όπου authείναι μια λειτουργία middleware που ορίζεται στο middlewares/auth.js.

  • μοντέλα: επίσης ένα είδος ενδιάμεσου λογισμικού μεταξύ του ελεγκτή σας και της βάσης δεδομένων. Μπορείτε να ορίσετε ένα σχήμα και να κάνετε κάποια επικύρωση πριν γράψετε στη βάση δεδομένων. Για παράδειγμα, μπορείτε να χρησιμοποιήσετε ένα ORM όπως το Mongoose, το οποίο συνοδεύεται από εξαιρετικές δυνατότητες και μεθόδους για χρήση στο ίδιο το σχήμα
  • διαδρομές: Ορίστε τις διαδρομές της εφαρμογής σας, με μεθόδους HTTP. Για παράδειγμα, μπορείτε να ορίσετε όλα όσα σχετίζονται με τον χρήστη.
router.post('/users/create', controller.create) router.put('/users/:userId', controller.update) router.get('/users', controller.getAll)
  • δημόσια: Αποθηκεύστε στατικές εικόνες σε /img, προσαρμοσμένα αρχεία JavaScript και CSS/css
  • προβολές: Περιέχει πρότυπα προς απόδοση από το διακομιστή.
  • δοκιμές: Εδώ μπορείτε να γράψετε όλες τις δοκιμές μονάδας ή δοκιμές αποδοχής για το διακομιστή API.
  • app.js: Λειτουργεί ως το κύριο αρχείο του έργου όπου προετοιμάζετε την εφαρμογή και άλλα στοιχεία του έργου.
  • package.json: Φροντίζει τις εξαρτήσεις, τα σενάρια για εκτέλεση με την npmεντολή και την έκδοση του έργου σας.

Εξαιρέσεις και χειρισμός σφαλμάτων

Αυτή είναι μια από τις πιο σημαντικές πτυχές που πρέπει να σκεφτείτε όταν δημιουργείτε οποιοδήποτε έργο με οποιαδήποτε γλώσσα. Ας δούμε πώς να χειριστούμε τα λάθη και τις εξαιρέσεις σε μια εφαρμογή Express.

Χρησιμοποιώντας υποσχέσεις

Ένα από τα πλεονεκτήματα της χρήσης υποσχέσεων έναντι επιστροφών κλήσεων είναι ότι μπορούν να χειριστούν σιωπηρές ή ρητές εξαιρέσεις / σφάλματα σε ασύγχρονα μπλοκ κώδικα καθώς και για συγχρονισμένο κώδικα που ορίζεται σε .then(), μια κλήση υπόσχεσης

Απλώς προσθέστε .catch(next)στο τέλος της αλυσίδας υπόσχεσης. Για παράδειγμα:

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

Χρησιμοποιώντας το catch-catch

Το Try-catch είναι ένας παραδοσιακός τρόπος σύλληψης εξαιρέσεων στον ασύγχρονο κώδικα.

Ας ρίξουμε μια ματιά σε ένα παράδειγμα με δυνατότητα εξαίρεσης:

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

Αποφύγετε τη χρήση σύγχρονου κώδικα

Ο σύγχρονος κώδικας είναι επίσης γνωστός ως κώδικας αποκλεισμού, επειδή εμποδίζει την εκτέλεση έως ότου εκτελεστούν.

Επομένως, αποφύγετε τη χρήση σύγχρονων συναρτήσεων ή μεθόδων που ενδέχεται να απαιτούν χιλιοστά του δευτερολέπτου ή μικροδευτερόλεπτα. Για έναν ιστότοπο υψηλής επισκεψιμότητας, θα συγκεντρώσει και μπορεί να οδηγήσει σε υψηλό χρόνο καθυστέρησης ή απόκρισης των αιτημάτων API.

Μην τα χρησιμοποιείτε στην παραγωγή ειδικά :)

Πολλές μονάδες Node.js συνοδεύονται από δύο .syncκαι .asyncμεθόδους, επομένως χρησιμοποιήστε το ασύγχρονο στην παραγωγή.

Ωστόσο, εάν εξακολουθείτε να θέλετε να χρησιμοποιήσετε ένα σύγχρονο API, χρησιμοποιήστε --trace-sync-ioσημαία γραμμής εντολών. Θα εκτυπώσει μια προειδοποίηση και ένα ίχνος στοίβας όποτε η εφαρμογή σας χρησιμοποιεί ένα σύγχρονο API.

Για περισσότερες πληροφορίες σχετικά με τις βασικές αρχές του χειρισμού σφαλμάτων, δείτε:

  • Σφάλμα χειρισμού στο Node.js
  • Δημιουργία ισχυρών εφαρμογών κόμβου: Διαχείριση σφαλμάτων (blog StrongLoop)
What you should not do is to listen for the uncaughtException event, emitted when an exception bubbles all the way back to the event loop. Using it is generally not preferred.

Logging properly

Logging is essential for debugging and app activity. It is used mainly for development purposes. We use console.log and console.error but these are synchronous functions.

For Debugging purposes

You can use a module like debug. This module enables you to use the DEBUG environment variable to control what debug messages are sent to console.err(), if any.

For app activity

One way is to write them to the database.

Check out How I used mongoose plugins to do auditing of my application .

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.

Ωστόσο, αντί να χρησιμοποιήσετε αυτήν την ενότητα απευθείας, είναι πολύ καλύτερο να χρησιμοποιήσετε ένα από τα πολλά εργαλεία εκεί έξω που το κάνουν αυτόματα για εσάς. Για παράδειγμα node-pm ή cluster-service.

Χρήση PM2

Για το pm2 μπορείτε να χρησιμοποιήσετε το σύμπλεγμα απευθείας μέσω μιας εντολής. Για παράδειγμα,

# 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 

Εάν αντιμετωπίσετε προβλήματα, μη διστάσετε να επικοινωνήσετε ή σχολιάστε παρακάτω.

Θα χαρούμε να βοηθήσω :)

Μην διστάσετε να χειροκροτήσετε αν το θεωρήσατε αξιόλογο!

Αναφορές: //expressjs.com/en/advanced/best-practice-performance.html

Αρχικά δημοσιεύθηκε στο 101node.io στις 30 Σεπτεμβρίου 2018.