diff --git a/sections/codestylepractices/eslint_prettier.md b/sections/codestylepractices/eslint_prettier.md index 06891dc7f..698857dbc 100644 --- a/sections/codestylepractices/eslint_prettier.md +++ b/sections/codestylepractices/eslint_prettier.md @@ -23,4 +23,4 @@ Source: [https://github.com/prettier/prettier-eslint/issues/101](https://github. ### Integrating ESLint and Prettier -ESLint and Prettier overlaps in the code formatting feature but it can be easily solved by using other packages like [prettier-eslint](https://github.com/prettier/prettier-eslint), [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier), and [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier). For more information about their differences, you can view the link [here](https://stackoverflow.com/questions/44690308/whats-the-difference-between-prettier-eslint-eslint-plugin-prettier-and-eslint). +ESLint and Prettier overlap in the code formatting feature but can be easily combined by using other packages like [prettier-eslint](https://github.com/prettier/prettier-eslint), [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier), and [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier). For more information about their differences, you can view the link [here](https://stackoverflow.com/questions/44690308/whats-the-difference-between-prettier-eslint-eslint-plugin-prettier-and-eslint). diff --git a/sections/errorhandling/apmproducts.md b/sections/errorhandling/apmproducts.md index 1e3659330..2ba711d25 100644 --- a/sections/errorhandling/apmproducts.md +++ b/sections/errorhandling/apmproducts.md @@ -3,7 +3,7 @@ ### One Paragraph Explainer -Exception != Error. Traditional error handling assumes the existence of Exception but application errors might come in the form of slow code paths, API downtime, lack of computational resources and more. This is where APM products come handy as they allow with minimal setup to detect a wide variety of ‘burried’ issues proactively. Among the common features of APM products are – alerting when HTTP API returns errors, detect when API response time drops below some threshold, detection of ‘code smells’, monitor server resources, operational intelligence dashboard with IT metrics and many other useful features. Most vendors offer a free plan. +Exception != Error. Traditional error handling assumes the existence of Exception but application errors might come in the form of slow code paths, API downtime, lack of computational resources and more. This is where APM products come in handy as they allow to detect a wide variety of ‘burried’ issues proactively with a minimal setup. Among the common features of APM products are for example alerting when the HTTP API returns errors, detect when the API response time drops below some threshold, detection of ‘code smells’, features to monitor server resources, operational intelligence dashboard with IT metrics and many other useful features. Most vendors offer a free plan. ### Wikipedia about APM @@ -14,11 +14,11 @@ Major products and segments APM products constitues 3 major segments: -1. Website or API monitoring – external services that constantly monitor uptime and performance via HTTP requests. Can be setup in few minutes. Following are few selected contenders: Pingdom, Uptime Robot, and New Relic +1. Website or API monitoring – external services that constantly monitor uptime and performance via HTTP requests. Can be set up in few minutes. Following are few selected contenders: [Pingdom](https://www.pingdom.com/), [Uptime Robot](https://uptimerobot.com/), and [New Relic](https://newrelic.com/application-monitoring) -2. Code instrumentation – products family which require to embed an agent within the application to benefit feature slow code detection, exceptions statistics, performance monitoring and many more. Following are few selected contenders: New Relic, App Dynamics +2. Code instrumentation – product family which require to embed an agent within the application to use features like slow code detection, exception statistics, performance monitoring and many more. Following are few selected contenders: New Relic, App Dynamics -3. Operational intelligence dashboard – these line of products are focused on facilitating the ops team with metrics and curated content that helps to easily stay on top of application performance. This usually involves aggregating multiple sources of information (application logs, DB logs, servers log, etc) and upfront dashboard design work. Following are few selected contenders: Datadog, Splunk +3. Operational intelligence dashboard – this line of products is focused on facilitating the ops team with metrics and curated content that helps to easily stay on top of application performance. This usually involves aggregating multiple sources of information (application logs, DB logs, servers log, etc) and upfront dashboard design work. Following are few selected contenders: [Datadog](https://www.datadoghq.com/), [Splunk](https://www.splunk.com/), [Zabbix](https://www.zabbix.com/) diff --git a/sections/errorhandling/asyncerrorhandling.md b/sections/errorhandling/asyncerrorhandling.md index 07ec7ba6f..e4a0cc792 100644 --- a/sections/errorhandling/asyncerrorhandling.md +++ b/sections/errorhandling/asyncerrorhandling.md @@ -23,14 +23,14 @@ doWork() ```javascript getData(someParameter, function(err, result){ if(err != null) - //do something like calling the given callback function and pass the error + // do something like calling the given callback function and pass the error getMoreData(a, function(err, result){ if(err != null) - //do something like calling the given callback function and pass the error + // do something like calling the given callback function and pass the error getMoreData(b, function(c){ getMoreData(d, function(e){ if(err != null) - //you get the idea?  + // you get the idea?  }); }); ``` diff --git a/sections/errorhandling/catchunhandledpromiserejection.md b/sections/errorhandling/catchunhandledpromiserejection.md index d7a54e30c..235cc3276 100644 --- a/sections/errorhandling/catchunhandledpromiserejection.md +++ b/sections/errorhandling/catchunhandledpromiserejection.md @@ -4,7 +4,7 @@ ### One Paragraph Explainer -Typically, most of modern Node.JS/Express application code runs within promises – whether within the .then handler, a function callback or in a catch block. Suprisingly, unless a developer remembered to add a .catch clause, errors thrown at these places are not handled by the uncaughtException event-handler and disappear. Recent versions of Node added a warning message when an unhandled rejection pops, though this might help to notice when things go wrong but it's obviously not a proper error handling. The straightforward solution is to never forget adding .catch clause within each promise chain call and redirect to a centralized error handler. However building your error handling strategy only on developer’s discipline is somewhat fragile. Consequently, it’s highly recommended using a graceful fallback and subscribe to process.on(‘unhandledRejection’, callback) – this will ensure that any promise error, if not handled locally, will get its treatment. +Typically, most of modern Node.JS/Express application code runs within promises – whether within the .then handler, a function callback or in a catch block. Suprisingly, unless a developer remembered to add a .catch clause, errors thrown at these places are not handled by the uncaughtException event-handler and disappear. Recent versions of Node added a warning message when an unhandled rejection pops, though this might help to notice when things go wrong but it's obviously not a proper error handling method. The straightforward solution is to never forget adding .catch clauses within each promise chain call and redirect to a centralized error handler. However building your error handling strategy only on developer’s discipline is somewhat fragile. Consequently, it’s highly recommended using a graceful fallback and subscribe to `process.on(‘unhandledRejection’, callback)` – this will ensure that any promise error, if not handled locally, will get its treatment.

@@ -12,7 +12,7 @@ Typically, most of modern Node.JS/Express application code runs within promises ```javascript DAL.getUserById(1).then((johnSnow) => { - //this error will just vanish + // this error will just vanish if(johnSnow.isAlive == false) throw new Error('ahhhh'); }); @@ -23,11 +23,11 @@ DAL.getUserById(1).then((johnSnow) => { ```javascript process.on('unhandledRejection', (reason, p) => { - //I just caught an unhandled promise rejection, since we already have fallback handler for unhandled errors (see below), let throw and let him handle that + // I just caught an unhandled promise rejection, since we already have fallback handler for unhandled errors (see below), let throw and let him handle that throw reason; }); process.on('uncaughtException', (error) => { - //I just received an error that was never handled, time to handle it and then decide whether a restart is needed + // I just received an error that was never handled, time to handle it and then decide whether a restart is needed errorManagement.handler.handleError(error); if (!errorManagement.handler.isTrustedError(error)) process.exit(1); diff --git a/sections/errorhandling/centralizedhandling.md b/sections/errorhandling/centralizedhandling.md index fbe82f6de..f4348831c 100644 --- a/sections/errorhandling/centralizedhandling.md +++ b/sections/errorhandling/centralizedhandling.md @@ -10,13 +10,13 @@ Without one dedicated object for error handling, greater are the chances of impo ### Code Example – a typical error flow ```javascript -//DAL layer, we don't handle errors here +// DAL layer, we don't handle errors here DB.addDocument(newCustomer, (error, result) => { if (error) throw new Error("Great error explanation comes here", other useful parameters) }); -//API route code, we catch both sync and async errors and forward to the middleware +// API route code, we catch both sync and async errors and forward to the middleware try { customerService.addNew(req.body).then((result) => { res.status(200).json(result); @@ -28,7 +28,7 @@ catch (error) { next(error); } -//Error handling middleware, we delegate the handling to the centralized error handler +// Error handling middleware, we delegate the handling to the centralized error handler app.use((err, req, res, next) => { errorHandler.handleError(err).then((isOperationalError) => { if (!isOperationalError) @@ -53,7 +53,7 @@ function errorHandler(){ ### Code Example – Anti Pattern: handling errors within the middleware ```javascript -//middleware handling the error directly, who will handle Cron jobs and testing errors? +// middleware handling the error directly, who will handle Cron jobs and testing errors? app.use((err, req, res, next) => { logger.logError(err); if(err.severity == errors.high) diff --git a/sections/errorhandling/documentingusingswagger.md b/sections/errorhandling/documentingusingswagger.md index a6f424a70..61172e675 100644 --- a/sections/errorhandling/documentingusingswagger.md +++ b/sections/errorhandling/documentingusingswagger.md @@ -3,7 +3,7 @@ ### One Paragraph Explainer -REST APIs return results using HTTP code, it’s absolutely required for the API user to be aware not only about the API schema but also about potential errors – the caller may then catch an error and tactfully handle it. For example, your API documentation might state in advanced that HTTP status 409 is returned when the customer name already exist (assuming the API register new users) so the caller can correspondingly render the best UX for the given situation. Swagger is a standard that defines the schema of API documentation offering an eco-system of tools that allow creating documentation easily online, see print screens below +REST APIs return results using HTTP status codes, it’s absolutely required for the API user to be aware not only about the API schema but also about potential errors – the caller may then catch an error and tactfully handle it. For example, your API documentation might state in advanced that HTTP status 409 is returned when the customer name already exist (assuming the API register new users) so the caller can correspondingly render the best UX for the given situation. Swagger is a standard that defines the schema of API documentation offering an eco-system of tools that allow creating documentation easily online, see print screens below ### Blog Quote: "You have to tell your callers what errors can happen" From the blog Joyent, ranked 1 for the keywords “Node.JS logging” @@ -12,4 +12,4 @@ From the blog Joyent, ranked 1 for the keywords “Node.JS logging” ### Useful Tool: Swagger Online Documentation Creator -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/swaggerDoc.png "API error handling") \ No newline at end of file +![Swagger API Scheme](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/swaggerDoc.png "API error handling") \ No newline at end of file diff --git a/sections/errorhandling/failfast.md b/sections/errorhandling/failfast.md index 4f6eada24..93630793f 100644 --- a/sections/errorhandling/failfast.md +++ b/sections/errorhandling/failfast.md @@ -7,7 +7,7 @@ We all know how checking arguments and failing fast is important to avoid hidden ### Wikipedia: Defensive Programming -Defensive programming is an approach to improve software and source code, in terms of: General quality – reducing the number of software bugs and problems. Making the source code comprehensible – the source code should be readable and understandable so it is approved in a code audit. Making the software behave in a predictable manner despite unexpected inputs or user actions. +Defensive programming is an approach to improve software and source code, in terms of: General quality – reducing the number of software bugs and problems. Making the source code comprehensible – the source code should be readable and understandable so it is approved in a code audit. Making the software behave in a predictable manner despite unexpected inputs or user actions. @@ -22,9 +22,9 @@ var memberSchema = Joi.object().keys({ function addNewMember(newMember) { - //assertions come first + // assertions come first Joi.assert(newMember, memberSchema); //throws if validation fails - //other logic here + // other logic here } ``` @@ -32,7 +32,7 @@ function addNewMember(newMember) ### Anti-pattern: no validation yields nasty bugs ```javascript -//if the discount is positive let's then redirect the user to pring his discount coupons +// if the discount is positive let's then redirect the user to pring his discount coupons function redirectToPrintDiscount(httpResponse, member, discount) { if(discount != 0) @@ -40,7 +40,7 @@ function redirectToPrintDiscount(httpResponse, member, discount) } redirectToPrintDiscount(httpResponse, someMember); -//forgot to pass the parameter discount, why the heck was the user redirected to the discount screen? +// forgot to pass the parameter discount, why the heck was the user redirected to the discount screen? ``` diff --git a/sections/errorhandling/monitoring.md b/sections/errorhandling/monitoring.md index 1f3b30efa..4cc8966a2 100644 --- a/sections/errorhandling/monitoring.md +++ b/sections/errorhandling/monitoring.md @@ -1,4 +1,4 @@ -# Title +# Monitoring ### One Paragraph Explainer diff --git a/sections/errorhandling/operationalvsprogrammererror.md b/sections/errorhandling/operationalvsprogrammererror.md index 497bd2f09..957b57e24 100644 --- a/sections/errorhandling/operationalvsprogrammererror.md +++ b/sections/errorhandling/operationalvsprogrammererror.md @@ -2,18 +2,18 @@ ### One Paragraph Explainer -Distinguishing the following two error types will minimize your app downtime and helps avoid crazy bugs: Operational errors refer to situations where you understand what happened and the impact of it – for example, a query to some HTTP service failed due to connection problem. On the other hand, programmer errors refer to cases where you have no idea why and sometimes where an error came from – it might be some code that tried to read an undefined value or DB connection pool that leaks memory. Operational errors are relatively easy to handle – usually logging the error is enough. Things become hairy when a programmer error pops up, the application might be in an inconsistent state and there’s nothing better you can do than restart gracefully +Distinguishing the following two error types will minimize your app downtime and helps avoid crazy bugs: Operational errors refer to situations where you understand what happened and the impact of it – for example, a query to some HTTP service failed due to connection problem. On the other hand, programmer errors refer to cases where you have no idea why and sometimes where an error came from – it might be some code that tried to read an undefined value or DB connection pool that leaks memory. Operational errors are relatively easy to handle – usually logging the error is enough. Things become hairy when a programmer error pops up, the application might be in an inconsistent state and there’s nothing better you can do than to restart gracefully ### Code Example – marking an error as operational (trusted) ```javascript -//marking an error object as operational +// marking an error object as operational var myError = new Error("How can I add new product when no value provided?"); myError.isOperational = true; -//or if you're using some centralized error factory (see other examples at the bullet "Use only the built-in Error object") +// or if you're using some centralized error factory (see other examples at the bullet "Use only the built-in Error object") function appError(commonType, description, isOperational) { Error.call(this); Error.captureStackTrace(this); @@ -31,18 +31,18 @@ From the blog Joyent, ranked 1 for the keywords “Node.JS error handling” > …The best way to recover from programmer errors is to crash immediately. You should run your programs using a restarter that will automatically restart the program in the event of a crash. With a restarter in place, crashing is the fastest way to restore reliable service in the face of a transient programmer error… - ### Blog Quote: "No safe way to leave without creating some undefined brittle state" +### Blog Quote: "No safe way to leave without creating some undefined brittle state" From Node.JS official documentation > …By the very nature of how throw works in JavaScript, there is almost never any way to safely “pick up where you left off”, without leaking references, or creating some other sort of undefined brittle state. The safest way to respond to a thrown error is to shut down the process. Of course, in a normal web server, you might have many connections open, and it is not reasonable to abruptly shut those down because an error was triggered by someone else. The better approach is to send an error response to the request that triggered the error, while letting the others finish in their normal time, and stop listening for new requests in that worker. - ### Blog Quote: "Otherwise you risk the state of your application" +### Blog Quote: "Otherwise you risk the state of your application" From the blog debugable.com, ranked 3 for the keywords “Node.JS uncaught exception” > …So, unless you really know what you are doing, you should perform a graceful restart of your service after receiving an “uncaughtException” exception event. Otherwise you risk the state of your application, or that of 3rd party libraries to become inconsistent, leading to all kinds of crazy bugs… - ### Blog Quote: "Blog Quote: There are three schools of thoughts on error handling" +### Blog Quote: "Blog Quote: There are three schools of thoughts on error handling" From the blog: JS Recipes > …There are primarily three schools of thoughts on error handling: diff --git a/sections/errorhandling/shuttingtheprocess.md b/sections/errorhandling/shuttingtheprocess.md index 5e36440fb..4cccc2fb6 100644 --- a/sections/errorhandling/shuttingtheprocess.md +++ b/sections/errorhandling/shuttingtheprocess.md @@ -1,4 +1,4 @@ -# Shut the process gracefully when a stranger comes to town +# Exit the process gracefully when a stranger comes to town ### One Paragraph Explainer @@ -10,8 +10,7 @@ Somewhere within your code, an error handler object is responsible for deciding ### Code example: deciding whether to crash ```javascript -//deciding whether to crash when an uncaught exception arrives -//Assuming developers mark known operational errors with error.isOperational=true, read best practice #3 +// Assuming developers mark known operational errors with error.isOperational=true, read best practice #3 process.on('uncaughtException', function(error) { errorManagement.handler.handleError(error); if(!errorManagement.handler.isTrustedError(error)) @@ -19,12 +18,12 @@ process.on('uncaughtException', function(error) { }); -//centralized error handler encapsulates error-handling related logic +// centralized error handler encapsulates error-handling related logic function errorHandler(){ this.handleError = function (error) { return logger.logError(err).then(sendMailToAdminIfCritical).then(saveInOpsQueueIfCritical).then(determineIfOperationalError); } - + this.isTrustedError = function (error) { return error.isOperational; } @@ -33,15 +32,15 @@ this.isTrustedError = function (error) { ### Blog Quote: "The best way is to crash" - From the blog Joyent - - > …The best way to recover from programmer errors is to crash immediately. You should run your programs using a restarter that will automatically restart the program in the event of a crash. With a restarter in place, crashing is the fastest way to restore reliable service in the face of a transient programmer error… +From the blog Joyent + +> …The best way to recover from programmer errors is to crash immediately. You should run your programs using a restarter that will automatically restart the program in the event of a crash. With a restarter in place, crashing is the fastest way to restore reliable service in the face of a transient programmer error… ### Blog Quote: "There are three schools of thoughts on error handling" - From the blog: JS Recipes +From the blog: JS Recipes - > …There are primarily three schools of thoughts on error handling: +> …There are primarily three schools of thoughts on error handling: 1. Let the application crash and restart it. 2. Handle all possible errors and never crash. 3. Balanced approach between the two @@ -50,4 +49,4 @@ this.isTrustedError = function (error) { ### Blog Quote: "No safe way to leave without creating some undefined brittle state" From Node.JS official documentation - > …By the very nature of how throw works in JavaScript, there is almost never any way to safely “pick up where you left off”, without leaking references, or creating some other sort of undefined brittle state. The safest way to respond to a thrown error is to shut down the process. Of course, in a normal web server, you might have many connections open, and it is not reasonable to abruptly shut those down because an error was triggered by someone else. The better approach is to send an error response to the request that triggered the error, while letting the others finish in their normal time, and stop listening for new requests in that worker. \ No newline at end of file +> …By the very nature of how throw works in JavaScript, there is almost never any way to safely “pick up where you left off”, without leaking references, or creating some other sort of undefined brittle state. The safest way to respond to a thrown error is to shut down the process. Of course, in a normal web server, you might have many connections open, and it is not reasonable to abruptly shut those down because an error was triggered by someone else. The better approach is to send an error response to the request that triggered the error, while letting the others finish in their normal time, and stop listening for new requests in that worker. \ No newline at end of file diff --git a/sections/errorhandling/testingerrorflows.md b/sections/errorhandling/testingerrorflows.md index a85d537d9..8727e5d6b 100644 --- a/sections/errorhandling/testingerrorflows.md +++ b/sections/errorhandling/testingerrorflows.md @@ -3,7 +3,7 @@ ### One Paragraph Explainer -Testing ‘happy’ paths is no better than testing failures. Good testing code coverage demands to test exceptional paths. Otherwise, there is no trust that exceptions are indeed handled correctly. Every unit testing framework, like Mocha & Chai, has a support for exception testing (code examples below). If you find it tedious to test every inner function and exception – you may settle with testing only REST API HTTP errors. +Testing ‘happy’ paths is no better than testing failures. Good testing code coverage demands to test exceptional paths. Otherwise, there is no trust that exceptions are indeed handled correctly. Every unit testing framework, like [Mocha](https://mochajs.org/) & [Chai](http://chaijs.com/), supports exception testing (code examples below). If you find it tedious to test every inner function and exception you may settle with testing only REST API HTTP errors. @@ -32,7 +32,7 @@ it("Creates new Facebook group", function (done) { body: invalidGroupInfo, json: true }).then((response) => { - //oh no if we reached here than no exception was thrown + // if we were to execute the code in this block, no error was thrown in the operation above }).catch(function (response) { expect(400).to.equal(response.statusCode); done(); diff --git a/sections/errorhandling/usematurelogger.md b/sections/errorhandling/usematurelogger.md index 9080a9803..cbd61b675 100644 --- a/sections/errorhandling/usematurelogger.md +++ b/sections/errorhandling/usematurelogger.md @@ -3,7 +3,7 @@ ### One Paragraph Explainer We all loovve console.log but obviously a reputable and persisted Logger like [Winston][winston], [Bunyan][bunyan] (highly popular) or [Pino][pino] (the new kid in town which is focused on performance) is mandatory for serious projects. A set of practices and tools will help to reason about errors much quicker – (1) log frequently using different levels (debug, info, error), (2) when logging, provide contextual information as JSON objects, see example below. (3) watch and filter logs using a log querying API (built-in in most loggers) or a log viewer software -(4) Expose and curate log statement for the operation team using operational intelligence tool like Splunk +(4) Expose and curate log statement for the operation team using operational intelligence tools like Splunk [winston]: https://www.npmjs.com/package/winston [bunyan]: https://www.npmjs.com/package/bunyan @@ -12,7 +12,7 @@ We all loovve console.log but obviously a reputable and persisted Logger like [W ### Code Example – Winston Logger in action ```javascript -//your centralized logger object +// your centralized logger object var logger = new winston.Logger({ level: 'info', transports: [ @@ -21,7 +21,7 @@ var logger = new winston.Logger({ ] }); -//custom code somewhere using the logger +// custom code somewhere using the logger logger.log('info', 'Test Log Message with some parameter %s', 'some parameter', { anything: 'This is metadata' }); ``` @@ -41,7 +41,7 @@ var options = { // Find items logged between today and yesterday. winston.query(options, function (err, results) { - //callback with results + // execute callback with results }); ``` diff --git a/sections/errorhandling/useonlythebuiltinerror.md b/sections/errorhandling/useonlythebuiltinerror.md index 5234c0224..cf54ec4f5 100644 --- a/sections/errorhandling/useonlythebuiltinerror.md +++ b/sections/errorhandling/useonlythebuiltinerror.md @@ -1,4 +1,4 @@ -# Use only the built-in Error object +# Use only the built-in Error object ### One Paragraph Explainer @@ -13,15 +13,15 @@ From the blog Ben Nadel, ranked 5 for the keywords “Node.JS error object” ### Code Example – doing it right ```javascript -//throwing an Error from typical function, whether sync or async +// throwing an Error from typical function, whether sync or async if(!productToAdd) throw new Error("How can I add new product when no value provided?"); -//'throwing' an Error from EventEmitter +// 'throwing' an Error from EventEmitter const myEmitter = new MyEmitter(); myEmitter.emit('error', new Error('whoops!')); -//'throwing' an Error from a Promise +// 'throwing' an Error from a Promise return new Promise(function (resolve, reject) { Return DAL.getProduct(productToAdd.id).then((existingProduct) =>{ if(existingProduct != null) @@ -33,7 +33,7 @@ return new Promise(function (resolve, reject) { ### Code example – Anti Pattern ```javascript -//throwing a String lacks any stack trace information and other important properties +// throwing a string lacks any stack trace information and other important data properties if(!productToAdd) throw ("How can I add new product when no value provided?"); @@ -42,7 +42,7 @@ if(!productToAdd) ### Code example – doing it even better ```javascript -//centralized error object that derives from Node’s Error +// centralized error object that derives from Node’s Error function appError(name, httpCode, description, isOperational) { Error.call(this); Error.captureStackTrace(this); @@ -54,7 +54,7 @@ appError.prototype.__proto__ = Error.prototype; module.exports.appError = appError; -//client throwing an exception +// client throwing an exception if(user == null) throw new appError(commonErrors.resourceNotFound, commonHTTPErrors.notFound, "further explanation", true) ``` @@ -63,15 +63,15 @@ if(user == null) ### Blog Quote: "A string is not an error" From the blog devthought.com, ranked 6 for the keywords “Node.JS error object” - > …passing a string instead of an error results in reduced interoperability between modules. It breaks contracts with APIs that might be performing instanceof Error checks, or that want to know more about the error. Error objects, as we’ll see, have very interesting properties in modern JavaScript engines besides holding the message passed to the constructor… +> …passing a string instead of an error results in reduced interoperability between modules. It breaks contracts with APIs that might be performing instanceof Error checks, or that want to know more about the error. Error objects, as we’ll see, have very interesting properties in modern JavaScript engines besides holding the message passed to the constructor… Blog Quote: “All JavaScript and System errors raised by Node.js inherit from Error” ### Blog Quote: "Inheriting from Error doesn’t add too much value" From the blog machadogj - > …One problem that I have with the Error class is that is not so simple to extend. Of course you can inherit the class and create your own Error classes like HttpError, DbError, etc. However that takes time, and doesn’t add too much value unless you are doing something with types. Sometimes, you just want to add a message, and keep the inner error, and sometimes you might want to extend the error with parameters, and such… +> …One problem that I have with the Error class is that is not so simple to extend. Of course you can inherit the class and create your own Error classes like HttpError, DbError, etc. However that takes time, and doesn’t add too much value unless you are doing something with types. Sometimes, you just want to add a message, and keep the inner error, and sometimes you might want to extend the error with parameters, and such… ### Blog Quote: "All JavaScript and System errors raised by Node.js inherit from Error" From Node.JS official documentation - > …All JavaScript and System errors raised by Node.js inherit from, or are instances of, the standard JavaScript Error class and are guaranteed to provide at least the properties available on that class. A generic JavaScript Error object that does not denote any specific circumstance of why the error occurred. Error objects capture a “stack trace” detailing the point in the code at which the Error was instantiated, and may provide a text description of the error.All errors generated by Node.js, including all System and JavaScript errors, will either be instances of, or inherit from, the Error class… +> …All JavaScript and System errors raised by Node.js inherit from, or are instances of, the standard JavaScript Error class and are guaranteed to provide at least the properties available on that class. A generic JavaScript Error object that does not denote any specific circumstance of why the error occurred. Error objects capture a “stack trace” detailing the point in the code at which the Error was instantiated, and may provide a text description of the error.All errors generated by Node.js, including all System and JavaScript errors, will either be instances of, or inherit from, the Error class… diff --git a/sections/production/apmproducts.md b/sections/production/apmproducts.md index 402746683..02e0ab068 100644 --- a/sections/production/apmproducts.md +++ b/sections/production/apmproducts.md @@ -5,7 +5,7 @@ ### One Paragraph Explainer -APM, application performance monitoring refers to a familiy of products that aims to monitor application performance from end to end, also from the customer perspective. While traditional monitoring solutions focuses on Exceptions and standalone technical metrics (e.g. error tracking, slow server endpoints, etc), in real world our app might create disappointed users without any code exceptions, for example if some middleware service performed real slow. APM products measure the user experience from end to end, for example, given a system that encompass frontend UI and multiple distributed services – some APM products can tell how fast a transaction that spans multiple tiers last. It can tell whether the user experience is solid and point to the problem. This attractive offering comes with a relatively high price tag hence it’s recommended for large-scale and complex products that require to go beyond straightforwd monitoring. +APM (application performance monitoring) refers to a familiy of products that aims to monitor application performance from end to end, also from the customer perspective. While traditional monitoring solutions focuses on Exceptions and standalone technical metrics (e.g. error tracking, slow server endpoints, etc), in real world our app might create disappointed users without any code exceptions, for example if some middleware service performed real slow. APM products measure the user experience from end to end, for example, given a system that encompass frontend UI and multiple distributed services – some APM products can tell how fast a transaction that spans multiple tiers last. It can tell whether the user experience is solid and point to the problem. This attractive offering comes with a relatively high price tag hence it’s recommended for large-scale and complex products that require to go beyond straightforwd monitoring.

diff --git a/sections/production/assigntransactionid.md b/sections/production/assigntransactionid.md index 5655659c4..b88e129a3 100644 --- a/sections/production/assigntransactionid.md +++ b/sections/production/assigntransactionid.md @@ -5,7 +5,7 @@ ### One Paragraph Explainer -A typical log is a warehouse of entries from all components and requests. Upon detection of some suspicious line or error it becomes hairy to match other lines that belong to the same specific flow (e.g. the user “John” tried to buy something). This becomes even more critical and challenging in microservices environment when a request/transaction might span across multiple computers. Address this by assigning a unique transaction identifier value to all the entries from the same request so when detecting one line one can copy the id and search for every line that has similar transaction Id. However, achieving this In Node is not straightforward as a single thread is used to serve all requests –consider using a library that that can group data on the request level – see code example on the next slide. When calling other microservice, pass the transaction Id using an HTTP header “x-transaction-id” to keep the same context. +A typical log is a warehouse of entries from all components and requests. Upon detection of some suspicious line or error it becomes hairy to match other lines that belong to the same specific flow (e.g. the user “John” tried to buy something). This becomes even more critical and challenging in a microservice environment when a request/transaction might span across multiple computers. Address this by assigning a unique transaction identifier value to all the entries from the same request so when detecting one line one can copy the id and search for every line that has similar transaction Id. However, achieving this In Node is not straightforward as a single thread is used to serve all requests –consider using a library that that can group data on the request level – see code example on the next slide. When calling other microservice, pass the transaction Id using an HTTP header like “x-transaction-id” to keep the same context.

@@ -13,22 +13,27 @@ A typical log is a warehouse of entries from all components and requests. Upon d ### Code example: typical nginx configuration ```javascript -//when receiving a new request, start a new isolated context and set a transaction Id. The following example is using the NPM library continuation-local-storage to isolate requests +// when receiving a new request, start a new isolated context and set a transaction Id. The following example is using the NPM library continuation-local-storage to isolate requests -var createNamespace = require('continuation-local-storage').createNamespace; +const { createNamespace } = require('continuation-local-storage'); var session = createNamespace('my session'); - router.get('/:id', (req, res, next) => { + +router.get('/:id', (req, res, next) => { session.set('transactionId', 'some unique GUID'); someService.getById(req.params.id); logger.info('Starting now to get something by Id'); -}//Now any other service or components can have access to the contextual, per-request, data +}); + +// Now any other service or components can have access to the contextual, per-request, data class someService { getById(id) { - logger.info(“Starting now to get something by Id”); - //other logic comes here + logger.info(“Starting to get something by Id”); + // other logic comes here } -}//Logger can now append transaction-id to each entry, so that entries from the same request will have the same value -class logger{ +} + +// The logger can now append the transaction-id to each entry, so that entries from the same request will have the same value +class logger { info (message) {console.log(`${message} ${session.get('transactionId')}`);} } diff --git a/sections/production/bestateless.md b/sections/production/bestateless.md index 4cfd9805b..30d752563 100644 --- a/sections/production/bestateless.md +++ b/sections/production/bestateless.md @@ -6,9 +6,10 @@ ### One Paragraph Explainer Have you ever encountered a severe production issue where one server was missing some piece of configuration or data? That is probably due to some unnecessary dependency on some local asset that is not part of the deployment. Many successful products treat servers like a phoenix bird – it dies and is reborn periodically without any damage. In other words, a server is just a piece of hardware that executes your code for some time and is replaced after that. -This approach: -1. allows to scale by adding and removing servers dynamically without any side-affect -2. simplifies the maintenance as it frees our mind from evaluating each server state. +This approach + +- allows to scale by adding and removing servers dynamically without any side-affect +- simplifies the maintenance as it frees our mind from evaluating each server state.

@@ -16,17 +17,20 @@ This approach: ### Code example: anti-patterns ```javascript -//Typical mistake 1: saving uploaded files locally in a server -var multer = require('multer'); //express middleware for fetching uploads +// Typical mistake 1: saving uploaded files locally on a server +var multer = require('multer'); // express middleware for handling multipart uploads var upload = multer({ dest: 'uploads/' }); + app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {}); -//Typical mistake 2: storing authentication sessions (passport) in a local file or memory + +// Typical mistake 2: storing authentication sessions (passport) in a local file or memory var FileStore = require('session-file-store')(session); app.use(session({ store: new FileStore(options), secret: 'keyboard cat' })); -//Typical mistake3: storing information on the global object + +// Typical mistake 3: storing information on the global object Global.someCacheLike.result = { somedata }; ``` diff --git a/sections/production/delegatetoproxy.md b/sections/production/delegatetoproxy.md index 587eb9221..0d0db9b2d 100644 --- a/sections/production/delegatetoproxy.md +++ b/sections/production/delegatetoproxy.md @@ -10,13 +10,15 @@ It’s very tempting to cargo-cult Express and use its rich middleware offering

-### Code Example – explanation +### Nginx Config Example – Using nginx to compress server responses -```javascript +``` +# configure gzip compression gzip on; -#defining gzip compression gzip_comp_level 6; gzip_vary on; + +# configure upstream upstream myApplication { server 127.0.0.1:3000; server 127.0.0.1:3001; @@ -25,11 +27,13 @@ upstream myApplication { #defining web server server { + # configure server with ssl and error pages listen 80; listen 443 ssl; ssl_certificate /some/location/sillyfacesociety.com.bundle.crt; error_page 502 /errors/502.html; - #handling static content + + # handling static content location ~ ^/(images/|img/|javascript/|js/|css/|stylesheets/|flash/|media/|static/|robots.txt|humans.txt|favicon.ico) { root /usr/local/silly_face_society/node/public; access_log off; diff --git a/sections/production/detectvulnerabilities.md b/sections/production/detectvulnerabilities.md index 10b14620c..5c68aae7b 100644 --- a/sections/production/detectvulnerabilities.md +++ b/sections/production/detectvulnerabilities.md @@ -7,8 +7,9 @@ Modern Node applications have tens and sometimes hundreds of dependencies. If any of the dependencies you use has a known security vulnerability your app is vulnerable as well. The following tools automatically check for known security vulnerabilities in your dependencies: -[nsp](https://www.npmjs.com/package/nsp) - Node Security Project -[snyk](https://snyk.io/) - Continuously find & fix vulnerabilities in your dependencies + +- [nsp](https://www.npmjs.com/package/nsp) - Node Security Project +- [snyk](https://snyk.io/) - Continuously find & fix vulnerabilities in your dependencies

diff --git a/sections/production/frontendout.md b/sections/production/frontendout.md index 4a0dca7c7..e9a12323c 100644 --- a/sections/production/frontendout.md +++ b/sections/production/frontendout.md @@ -5,25 +5,30 @@ ### One Paragraph Explainer -In a classic web app the backend serves the frontend/graphics to the browser, a very common approach in the Node’s world is to use Express static middleware for streamlining static files to the client. BUT – Node is not a typical webapp as it utilizes a single thread that is not optimized to serve many files at once. Instead, consider using a reverse proxy, cloud storage or CDN (e.g. Nginx, AWS S3, Azure Blob Storage, etc) that utilizes many optimizations for this task and gain much better throughput. For example, specialized middleware like nginx embodies direct hooks between the file system and the network card and use a multi-threaded approach to minimize intervention among multiple requests. +In a classic web app the backend serves the frontend/graphics to the browser, a very common approach in the Node’s world is to use Express static middleware for streamlining static files to the client. BUT – Node is not a typical webapp as it utilizes a single thread that is not optimized to serve many files at once. Instead, consider using a reverse proxy (e.g. nginx, HAProxy), cloud storage or CDN (e.g. AWS S3, Azure Blob Storage, etc) that utilizes many optimizations for this task and gain much better throughput. For example, specialized middleware like nginx embodies direct hooks between the file system and the network card and uses a multi-threaded approach to minimize intervention among multiple requests. -Your optimal solution might wear one of the following forms: -1. A reverse proxy – your static files will be located right next to your Node application, only requests to the static files folder will be served by a proxy that sits in front of your Node app such as nginx. Using this approach, your Node app is responsible deploying the static files but not to serve them. Your frontend’s colleague will love this approach as it prevents cross-origin-requests from the frontend. -2. Cloud storage – your static files will NOT be part of your Node app content, else they will be uploaded to services like AWS S3, Azure BlobStorage, or other similar services that were born for this mission. Using this approach, your Node app is not responsible deploying the static files neither to serve them, hence a complete decoupling is drawn between Node and the Frontend which is anyway handled by different teams. +Your optimal solution might wear one of the following forms: + +1. Using a reverse proxy – your static files will be located right next to your Node application, only requests to the static files folder will be served by a proxy that sits in front of your Node app such as nginx. Using this approach, your Node app is responsible deploying the static files but not to serve them. Your frontend’s colleague will love this approach as it prevents cross-origin-requests from the frontend. + +2. Cloud storage – your static files will NOT be part of your Node app content, they will be uploaded to services like AWS S3, Azure BlobStorage, or other similar services that were born for this mission. Using this approach, your Node app is not responsible deploying the static files neither to serve them, hence a complete decoupling is drawn between Node and the Frontend which is anyway handled by different teams.

-### Code example: typical nginx configuration for serving static files +### Configuration example: typical nginx configuration for serving static files -```javascript +``` +# configure gzip compression gzip on; -#defining gzip compression keepalive 64; -}#defining web server + +# defining web server server { listen 80; -listen 443 ssl;#handling static content +listen 443 ssl; + +# handle static content location ~ ^/(images/|img/|javascript/|js/|css/|stylesheets/|flash/|media/|static/|robots.txt|humans.txt|favicon.ico) { root /usr/local/silly_face_society/node/public; access_log off; diff --git a/sections/production/guardprocess.md b/sections/production/guardprocess.md index 08bf1ad96..ec5dcdfb7 100644 --- a/sections/production/guardprocess.md +++ b/sections/production/guardprocess.md @@ -5,7 +5,7 @@ ### One Paragraph Explainer -At the base level, Node processes must be guarded and restarted upon failures. Simply put, for small apps and those who don’t use containers – tools like [PM2](https://www.npmjs.com/package/pm2-docker) are perfect as they bring simplicity, restarting capabilities and also rich integration with Node. Others with strong Linux skills might use systemd and run Node as a service. Things get more interesting for apps that uses Docker or any container technology since those are usually accompanies by cluster management tools (e.g. [AWS ECS](http://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html), [Kubernetes](https://kubernetes.io/), etc) that deploy, monitor and heal containers. Having all those rich cluster management features including container restart, why mess up with other tools like PM2? There’s no bullet proof answer. There are good reasons to keep PM2 within containers (mostly its containers specific version [pm2-docker](https://www.npmjs.com/package/pm2-docker)) as the first guarding tier – it’s much faster to restart a process and provide Node-specific features like flagging to the code when the hosting container asks to gracefully restart. Other might choose to avoid unnecessary layers. To conclude this write-up, no solution suits them all and getting to know the options is the important thing +At the base level, Node processes must be guarded and restarted upon failures. Simply put, for small apps and those who don’t use containers – tools like [PM2](https://www.npmjs.com/package/pm2-docker) are perfect as they bring simplicity, restarting capabilities and also rich integration with Node. Others with strong Linux skills might use systemd and run Node as a service. Things get more interesting for apps that use Docker or any container technology since those are usually accompanied by cluster management and orchestration tools (e.g. [AWS ECS](http://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html), [Kubernetes](https://kubernetes.io/), etc) that deploy, monitor and heal containers. Having all those rich cluster management features including container restart, why mess up with other tools like PM2? There’s no bullet proof answer. There are good reasons to keep PM2 within containers (mostly its containers specific version [pm2-docker](https://www.npmjs.com/package/pm2-docker)) as the first guarding tier – it’s much faster to restart a process and provide Node-specific features like flagging to the code when the hosting container asks to gracefully restart. Other might choose to avoid unnecessary layers. To conclude this write-up, no solution suits them all and getting to know the options is the important thing

diff --git a/sections/production/lockdependencies.md b/sections/production/lockdependencies.md index 43b925b26..34b201db3 100644 --- a/sections/production/lockdependencies.md +++ b/sections/production/lockdependencies.md @@ -7,7 +7,7 @@ -Your code depends on many external packages, let’s say it ‘requires’ and use momentjs-2.1.4, then by default when you deploy to production NPM might fetch momentjs 2.1.5 which unfortunately brings some new bugs to the table. Using NPM config files and the argument ```–save-exact=true``` instructs NPM to refer to the *exact* same version that was installed so the next time you run ```npm install``` (at production or within a Docker container you plan to ship forward for testing) the same dependent version will be fetched. An alternative popular approach is using a .shrinkwrap file (easily generated using NPM) that states exactly which packages and versions should be installed so no environement can get tempted to fetch newer versions than expected. +Your code depends on many external packages, let’s say it ‘requires’ and use momentjs-2.1.4, then by default when you deploy to production NPM might fetch momentjs 2.1.5 which unfortunately brings some new bugs to the table. Using NPM config files and the argument ```–save-exact=true``` instructs NPM to refer to the *exact* same version that was installed so the next time you run ```npm install``` (in production or within a Docker container you plan to ship forward for testing) the same dependent version will be fetched. An alternative and popular approach is using a .shrinkwrap file (easily generated using NPM) that states exactly which packages and versions should be installed so no environement can get tempted to fetch newer versions than expected. * **Update:** as of NPM 5, dependencies are locked automatically using .shrinkwrap. Yarn, an emerging package manager, also locks down dependencies by default @@ -17,8 +17,8 @@ Your code depends on many external packages, let’s say it ‘requires’ and u ### Code example: .npmrc file that instructs NPM to use exact versions -```javascript -//save this as .npmrc file on the project directory +``` +// save this as .npmrc file on the project directory save-exact:true ``` diff --git a/sections/production/monitoring.md b/sections/production/monitoring.md index 4eb4209a7..65316f40f 100644 --- a/sections/production/monitoring.md +++ b/sections/production/monitoring.md @@ -6,7 +6,7 @@ At the very basic level, monitoring means you can *easily* identify when bad things happen at production. For example, by getting notified by email or Slack. The challenge is to choose the right set of tools that will satisfy your requirements without breaking your bank. May I suggest, start with defining the core set of metrics that must be watched to ensure a healthy state – CPU, server RAM, Node process RAM (less than 1.4GB), the amount of errors in the last minute, number of process restarts, average response time. Then go over some advanced features you might fancy and add to your wish list. Some examples of luxury monitoring feature: DB profiling, cross-service measuring (i.e. measure business transaction), frontend integration, expose raw data to custom BI clients, Slack notifications and many others. -Achieving the advanced features demands lengthy setup or buying a commercial product such as Datadog, newrelic and a like. Unfortunately, achieving even the basics is not a walk in the park as some metrics are hardware-related (CPU) and others live within the node process (internal errors) thus all the straightforward tools require some additional setup. For example, cloud vendor monitoring solutions (e.g. [AWS CloudWatch](https://aws.amazon.com/cloudwatch/), [Google StackDriver](https://cloud.google.com/stackdriver/)) will tell you immediately about the hardware metric but nothing about the internal app behavior. On the other end, Log-based solutions such as ElasticSearch lack the hardware view by default. The solution is to augment your choice with missing metrics, for example, a popular choice is sending application logs to [Elastic stack](https://www.elastic.co/products) and configure some additional agent (e.g. [Beat](https://www.elastic.co/products)) to share hardware-related information to get the full picture. +Achieving the advanced features demands lengthy setup or buying a commercial product such as Datadog, NewRelic and alike. Unfortunately, achieving even the basics is not a walk in the park as some metrics are hardware-related (CPU) and others live within the node process (internal errors) thus all the straightforward tools require some additional setup. For example, cloud vendor monitoring solutions (e.g. [AWS CloudWatch](https://aws.amazon.com/cloudwatch/), [Google StackDriver](https://cloud.google.com/stackdriver/)) will tell you immediately about the hardware metrics but not about the internal app behavior. On the other end, Log-based solutions such as ElasticSearch lack the hardware view by default. The solution is to augment your choice with missing metrics, for example, a popular choice is sending application logs to [Elastic stack](https://www.elastic.co/products) and configure some additional agent (e.g. [Beat](https://www.elastic.co/products)) to share hardware-related information to get the full picture.

diff --git a/sections/production/setnodeenv.md b/sections/production/setnodeenv.md index 3b6605507..b1569770b 100644 --- a/sections/production/setnodeenv.md +++ b/sections/production/setnodeenv.md @@ -13,11 +13,12 @@ Process environment variables is a set of key-value pairs made available to any ### Code example: Setting and reading the NODE_ENV environment variable ```javascript -//Using a command line, initializing node process and setting before environment variables -Set NODE_ENV=development&& set otherVariable=someValue&& node +// Setting environment variables in bash before starting the node process +$ NODE_ENV=development +$ node -//Reading the environment variable using code -If(process.env.NODE_ENV === “production”) +// Reading the environment variable using code +if (process.env.NODE_ENV === “production”) useCaching = true; ``` @@ -29,7 +30,7 @@ From the blog [dynatrace](https://www.dynatrace.com/blog/the-drastic-effects-of- > ...In Node.js there is a convention to use a variable called NODE_ENV to set the current mode. We see that it in fact reads NODE_ENV and defaults to ‘development’ if it isn’t set. We clearly see that by setting NODE_ENV to production the number of requests Node.js can handle jumps by around two-thirds while the CPU usage even drops slightly. *Let me emphasize this: Setting NODE_ENV to production makes your application 3 times faster!* -![Set NODE_ENV = production](/assets/images/setnodeenv1.png "Set NODE_ENV = production") +![NODE_ENV=production](/assets/images/setnodeenv1.png "NODE_ENV=production")