diff --git a/sections/errorhandling/asyncerrorhandling.md b/sections/errorhandling/asyncerrorhandling.md
index 4fa9653c..795dcea3 100644
--- a/sections/errorhandling/asyncerrorhandling.md
+++ b/sections/errorhandling/asyncerrorhandling.md
@@ -8,13 +8,14 @@ Callbacks don’t scale well since most programmers are not familiar with them.
```javascript
return functionA()
- .then((valueA) => functionB(valueA))
- .then((valueB) => functionC(valueB))
- .then((valueC) => functionD(valueC))
+ .then(functionB)
+ .then(functionC)
+ .then(functionD)
.catch((err) => logger.error(err))
- .then(alwaysExecuteThisFunction())
+ .then(alwaysExecuteThisFunction)
```
+
### Code Example - using async/await to catch errors
```javascript
@@ -25,7 +26,7 @@ async function executeAsyncTask () {
const valueC = await functionC(valueB);
return await functionD(valueC);
}
- catch(err) {
+ catch (err) {
logger.error(err);
} finally {
await alwaysExecuteThisFunction();
@@ -35,6 +36,9 @@ async function executeAsyncTask () {
### Anti pattern code example – callback style error handling
+
+Javascript
+
```javascript
getData(someParameter, function(err, result) {
if(err !== null) {
@@ -45,7 +49,7 @@ getData(someParameter, function(err, result) {
getMoreData(b, function(c) {
getMoreData(d, function(e) {
if(err !== null ) {
- // you get the idea?
+ // you get the idea?
}
})
});
@@ -54,6 +58,31 @@ getData(someParameter, function(err, result) {
}
});
```
+
+
+
+Typescript
+
+```typescript
+getData(someParameter, function(err: Error | null, resultA: ResultA) {
+ if(err !== null) {
+ // do something like calling the given callback function and pass the error
+ getMoreData(resultA, function(err: Error | null, resultB: ResultB) {
+ if(err !== null) {
+ // do something like calling the given callback function and pass the error
+ getMoreData(resultB, function(resultC: ResultC) {
+ getMoreData(resultC, function(err: Error | null, d: ResultD) {
+ if(err !== null) {
+ // you get the idea?
+ }
+ })
+ });
+ }
+ });
+ }
+});
+```
+
### Blog Quote: "We have a problem with promises"
diff --git a/sections/errorhandling/catchunhandledpromiserejection.md b/sections/errorhandling/catchunhandledpromiserejection.md
index b2e890d0..540aeee5 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. Surprisingly, 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.
+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. Surprisingly, 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.
@@ -13,29 +13,54 @@ Typically, most of modern Node.js/Express application code runs within promises
```javascript
DAL.getUserById(1).then((johnSnow) => {
// this error will just vanish
- if(johnSnow.isAlive == false)
+ if(johnSnow.isAlive === false)
throw new Error('ahhhh');
});
-
```
### Code example: Catching unresolved and rejected promises
+
+Javascript
+
```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
errorManagement.handler.handleError(error);
if (!errorManagement.handler.isTrustedError(error))
process.exit(1);
});
-
```
+
+
+
+Typescript
+
+```typescript
+process.on('unhandledRejection', (reason: string, p: Promise) => {
+ // 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: Error) => {
+ // 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);
+});
+```
+
@@ -46,16 +71,16 @@ process.on('uncaughtException', (error) => {
> Let’s test your understanding. Which of the following would you expect to print an error to the console?
```javascript
-Promise.resolve(‘promised value’).then(() => {
- throw new Error(‘error’);
+Promise.resolve('promised value').then(() => {
+ throw new Error('error');
});
-Promise.reject(‘error value’).catch(() => {
- throw new Error(‘error’);
+Promise.reject('error value').catch(() => {
+ throw new Error('error');
});
new Promise((resolve, reject) => {
- throw new Error(‘error’);
+ throw new Error('error');
});
```
diff --git a/sections/errorhandling/centralizedhandling.md b/sections/errorhandling/centralizedhandling.md
index 097a9922..53d7bf62 100644
--- a/sections/errorhandling/centralizedhandling.md
+++ b/sections/errorhandling/centralizedhandling.md
@@ -6,6 +6,9 @@ Without one dedicated object for error handling, greater are the chances of impo
### Code Example – a typical error flow
+
+Javascript
+
```javascript
// DAL layer, we don't handle errors here
DB.addDocument(newCustomer, (error, result) => {
@@ -33,9 +36,46 @@ app.use(async (err, req, res, next) => {
}
});
```
+
+
+
+Typescript
+
+```typescript
+// DAL layer, we don't handle errors here
+DB.addDocument(newCustomer, (error: Error, result: 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
+try {
+ customerService.addNew(req.body).then((result: Result) => {
+ res.status(200).json(result);
+ }).catch((error: Error) => {
+ next(error)
+ });
+}
+catch (error) {
+ next(error);
+}
+
+// Error handling middleware, we delegate the handling to the centralized error handler
+app.use(async (err: Error, req: Request, res: Response, next: NextFunction) => {
+ const isOperationalError = await errorHandler.handleError(err);
+ if (!isOperationalError) {
+ next(err);
+ }
+});
+```
+
+
### Code example – handling errors within a dedicated object
+
+Javascript
+
```javascript
module.exports.handler = new errorHandler();
@@ -48,9 +88,31 @@ function errorHandler() {
};
}
```
+
+
+
+Typescript
+
+```typescript
+class ErrorHandler {
+ public async handleError(err: Error): Promise {
+ await logger.logError(err);
+ await sendMailToAdminIfCritical();
+ await saveInOpsQueueIfCritical();
+ await determineIfOperationalError();
+ };
+}
+
+export const handler = new ErrorHandler();
+```
+
+
### Code Example – Anti Pattern: handling errors within the middleware
+
+Javascript
+
```javascript
// middleware handling the error directly, who will handle Cron jobs and testing errors?
app.use((err, req, res, next) => {
@@ -63,6 +125,25 @@ app.use((err, req, res, next) => {
}
});
```
+
+
+
+
+Typescript
+
+```typescript
+// middleware handling the error directly, who will handle Cron jobs and testing errors?
+app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
+ logger.logError(err);
+ if (err.severity == errors.high) {
+ mailer.sendMail(configuration.adminMail, 'Critical error occured', err);
+ }
+ if (!err.isOperational) {
+ next(err);
+ }
+});
+```
+
### Blog Quote: "Sometimes lower levels can’t do anything useful except propagate the error to their caller"
diff --git a/sections/errorhandling/failfast.md b/sections/errorhandling/failfast.md
index b2f89b02..805d7ac9 100644
--- a/sections/errorhandling/failfast.md
+++ b/sections/errorhandling/failfast.md
@@ -22,11 +22,15 @@ function addNewMember(newMember) {
Joi.assert(newMember, memberSchema); //throws if validation fails
// other logic here
}
-
```
+
+
### Anti-pattern: no validation yields nasty bugs
+
+Javascript
+
```javascript
// if the discount is positive let's then redirect the user to print his discount coupons
function redirectToPrintDiscount(httpResponse, member, discount) {
@@ -37,8 +41,24 @@ 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?
-
```
+
+
+
+Typescript
+
+```typescript
+// if the discount is positive let's then redirect the user to print his discount coupons
+function redirectToPrintDiscount(httpResponse: Response, member: Member, discount: number) {
+ if (discount != 0) {
+ httpResponse.redirect(`/discountPrintView/${member.id}`);
+ }
+}
+
+redirectToPrintDiscount(httpResponse, someMember, -12);
+// We passed a negative parameter discount, why the heck was the user redirected to the discount screen?
+```
+
### Blog Quote: "You should throw these errors immediately"
diff --git a/sections/errorhandling/operationalvsprogrammererror.md b/sections/errorhandling/operationalvsprogrammererror.md
index a6175c23..fa98057b 100644
--- a/sections/errorhandling/operationalvsprogrammererror.md
+++ b/sections/errorhandling/operationalvsprogrammererror.md
@@ -6,6 +6,9 @@ Distinguishing the following two error types will minimize your app downtime and
### Code Example – marking an error as operational (trusted)
+
+Javascript
+
```javascript
// marking an error object as operational
const myError = new Error("How can I add new product when no value provided?");
@@ -25,6 +28,34 @@ class AppError {
throw new AppError(errorManagement.commonErrors.InvalidInput, "Describe here what happened", true);
```
+
+
+
+Typescript
+
+```typescript
+// some centralized error factory (see other examples at the bullet "Use only the built-in Error object")
+export class AppError extends Error {
+ public readonly commonType: string;
+ public readonly isOperational: boolean;
+
+ constructor(commonType: string, description: string, isOperational: boolean) {
+ super(description);
+
+ Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain
+
+ this.commonType = commonType;
+ this.isOperational = isOperational;
+
+ Error.captureStackTrace(this);
+ }
+}
+
+// marking an error object as operational (true)
+throw new AppError(errorManagement.commonErrors.InvalidInput, "Describe here what happened", true);
+
+```
+
### Blog Quote: "Programmer errors are bugs in the program"
diff --git a/sections/errorhandling/shuttingtheprocess.md b/sections/errorhandling/shuttingtheprocess.md
index 647eadcb..9062ec48 100644
--- a/sections/errorhandling/shuttingtheprocess.md
+++ b/sections/errorhandling/shuttingtheprocess.md
@@ -6,12 +6,15 @@ Somewhere within your code, an error handler object is responsible for deciding
### Code example: deciding whether to crash
+
+Javascript
+
```javascript
// 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))
- process.exit(1)
+ process.exit(1)
});
// centralized error handler encapsulates error-handling related logic
@@ -28,6 +31,51 @@ function errorHandler() {
}
}
```
+
+
+
+Typescript
+
+```typescript
+// Assuming developers mark known operational errors with error.isOperational=true, read best practice #3
+process.on('uncaughtException', (error: Error) => {
+ errorManagement.handler.handleError(error);
+ if(!errorManagement.handler.isTrustedError(error))
+ process.exit(1)
+});
+
+// centralized error object that derives from Node’s Error
+export class AppError extends Error {
+ public readonly isOperational: boolean;
+
+ constructor(description: string, isOperational: boolean) {
+ super(description);
+ Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain
+ this.isOperational = isOperational;
+ Error.captureStackTrace(this);
+ }
+}
+
+// centralized error handler encapsulates error-handling related logic
+class ErrorHandler {
+ public async handleError(err: Error): Promise {
+ await logger.logError(err);
+ await sendMailToAdminIfCritical();
+ await saveInOpsQueueIfCritical();
+ await determineIfOperationalError();
+ };
+
+ public isTrustedError(error: Error) {
+ if (error instanceof AppError) {
+ return error.isOperational;
+ }
+ return false;
+ }
+}
+
+export const handler = new ErrorHandler();
+```
+
### Blog Quote: "The best way is to crash"
diff --git a/sections/errorhandling/testingerrorflows.md b/sections/errorhandling/testingerrorflows.md
index 86ac61e7..83908200 100644
--- a/sections/errorhandling/testingerrorflows.md
+++ b/sections/errorhandling/testingerrorflows.md
@@ -6,6 +6,9 @@ Testing ‘happy’ paths is no better than testing failures. Good testing code
### Code example: ensuring the right exception is thrown using Mocha & Chai
+
+Javascript
+
```javascript
describe("Facebook chat", () => {
it("Notifies on new chat message", () => {
@@ -14,13 +17,30 @@ describe("Facebook chat", () => {
expect(chatService.sendMessage.bind({ message: "Hi" })).to.throw(ConnectionError);
});
});
-
```
+
+
+
+Typescript
+
+```typescript
+describe("Facebook chat", () => {
+ it("Notifies on new chat message", () => {
+ const chatService = new chatService();
+ chatService.participants = getDisconnectedParticipants();
+ expect(chatService.sendMessage.bind({ message: "Hi" })).to.throw(ConnectionError);
+ });
+});
+```
+
### Code example: ensuring API returns the right HTTP error code
+
+Javascript
+
```javascript
-it("Creates new Facebook group", function (done) {
+it("Creates new Facebook group", (done) => {
var invalidGroupInfo = {};
httpRequest({
method: 'POST',
@@ -30,9 +50,33 @@ it("Creates new Facebook group", function (done) {
json: true
}).then((response) => {
// if we were to execute the code in this block, no error was thrown in the operation above
- }).catch(function (response) {
+ }).catch((response) => {
expect(400).to.equal(response.statusCode);
done();
});
});
```
+
+
+
+Typescript
+
+```typescript
+it("Creates new Facebook group", async () => {
+ let invalidGroupInfo = {};
+ try {
+ const response = await httpRequest({
+ method: 'POST',
+ uri: "facebook.com/api/groups",
+ resolveWithFullResponse: true,
+ body: invalidGroupInfo,
+ json: true
+ })
+ // if we were to execute the code in this block, no error was thrown in the operation above
+ expect.fail('The request should have failed')
+ } catch(response) {
+ expect(400).to.equal(response.statusCode);
+ }
+});
+```
+
\ No newline at end of file
diff --git a/sections/errorhandling/usematurelogger.md b/sections/errorhandling/usematurelogger.md
index 4069d04d..286f2c24 100644
--- a/sections/errorhandling/usematurelogger.md
+++ b/sections/errorhandling/usematurelogger.md
@@ -11,7 +11,7 @@ We all love console.log but obviously, a reputable and persistent logger like [W
```javascript
// your centralized logger object
-var logger = new winston.Logger({
+const logger = new winston.Logger({
level: 'info',
transports: [
new (winston.transports.Console)()
@@ -20,22 +20,20 @@ var logger = new winston.Logger({
// custom code somewhere using the logger
logger.log('info', 'Test Log Message with some parameter %s', 'some parameter', { anything: 'This is metadata' });
-
```
### Code Example – Querying the log folder (searching for entries)
```javascript
-var options = {
- from: new Date - 24 * 60 * 60 * 1000,
- until: new Date,
+const options = {
+ from: Date.now() - 24 * 60 * 60 * 1000,
+ until: new Date(),
limit: 10,
start: 0,
order: 'desc',
fields: ['message']
};
-
// Find items logged between today and yesterday.
winston.query(options, function (err, results) {
// execute callback with results
diff --git a/sections/errorhandling/useonlythebuiltinerror.md b/sections/errorhandling/useonlythebuiltinerror.md
index 3d83c6de..8c59285a 100644
--- a/sections/errorhandling/useonlythebuiltinerror.md
+++ b/sections/errorhandling/useonlythebuiltinerror.md
@@ -38,6 +38,9 @@ if(!productToAdd)
### Code example – doing it even better
+
+Javascript
+
```javascript
// centralized error object that derives from Node’s Error
function AppError(name, httpCode, description, isOperational) {
@@ -55,6 +58,38 @@ module.exports.AppError = AppError;
if(user == null)
throw new AppError(commonErrors.resourceNotFound, commonHTTPErrors.notFound, "further explanation", true)
```
+
+
+
+Typescript
+
+```typescript
+// centralized error object that derives from Node’s Error
+export class AppError extends Error {
+ public readonly name: string;
+ public readonly httpCode: HttpCode;
+ public readonly isOperational: boolean;
+
+ constructor(name: string, httpCode: HttpCode, description: string, isOperational: boolean) {
+ super(description);
+
+ Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain
+
+ this.name = name;
+ this.httpCode = httpCode;
+ this.isOperational = isOperational;
+
+ Error.captureStackTrace(this);
+ }
+}
+
+// client throwing an exception
+if(user == null)
+ throw new AppError(commonErrors.resourceNotFound, commonHTTPErrors.notFound, "further explanation", true)
+```
+
+
+*Explanation about the `Object.setPrototypeOf` in Typescript: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget*
### Blog Quote: "I don’t see the value in having lots of different types"
diff --git a/sections/projectstructre/separateexpress.md b/sections/projectstructre/separateexpress.md
index c0f88679..07c6ea10 100644
--- a/sections/projectstructre/separateexpress.md
+++ b/sections/projectstructre/separateexpress.md
@@ -8,39 +8,54 @@ The latest Express generator comes with a great practice that is worth to keep -
-### Code example: API declaration, should reside in app.js
+### Code example: API declaration, should reside in app.js/app.ts
```javascript
-var app = express();
+const app = express();
app.use(bodyParser.json());
app.use("/api/events", events.API);
app.use("/api/forms", forms);
```
-
-
### Code example: Server network declaration, should reside in /bin/www
+
+Javascript
+
```javascript
var app = require('../app');
var http = require('http');
-/**
- * Get port from environment and store in Express.
- */
-
+// Get port from environment and store in Express.
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
-/**
- * Create HTTP server.
- */
-
+// Create HTTP server.
var server = http.createServer(app);
```
+
+
+
+Typescript
+
+```typescript
+import app from '../app';
+import http from 'http';
+
+// Get port from environment and store in Express.
+const port = normalizePort(process.env.PORT || '3000');
+app.set('port', port);
+
+// Create HTTP server.
+const server = http.createServer(app);
+```
+
### Example: test your API in-process using supertest (popular testing package)
+
+Javascript
+
```javascript
const app = express();
@@ -56,4 +71,28 @@ request(app)
.end(function(err, res) {
if (err) throw err;
});
-````
+```
+
+
+
+
+Typescript
+
+```typescript
+const app = express();
+
+app.get('/user', (req: Request, res: Response) => {
+ res.status(200).json({ name: 'tobi' });
+});
+
+request(app)
+ .get('/user')
+ .expect('Content-Type', /json/)
+ .expect('Content-Length', '15')
+ .expect(200)
+ .end((err: Error) => {
+ if (err) throw err;
+ });
+
+```
+
\ No newline at end of file