mirror of
https://github.com/goldbergyoni/nodebestpractices.git
synced 2025-10-27 19:17:13 +08:00
Merge pull request #584 from kevynb/typescript-examples
Add typescript examples for error handling and project structure
This commit is contained in:
@ -8,13 +8,14 @@ Callbacks don’t scale well since most programmers are not familiar with them.
|
|||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
return functionA()
|
return functionA()
|
||||||
.then((valueA) => functionB(valueA))
|
.then(functionB)
|
||||||
.then((valueB) => functionC(valueB))
|
.then(functionC)
|
||||||
.then((valueC) => functionD(valueC))
|
.then(functionD)
|
||||||
.catch((err) => logger.error(err))
|
.catch((err) => logger.error(err))
|
||||||
.then(alwaysExecuteThisFunction())
|
.then(alwaysExecuteThisFunction)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Code Example - using async/await to catch errors
|
### Code Example - using async/await to catch errors
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
@ -25,7 +26,7 @@ async function executeAsyncTask () {
|
|||||||
const valueC = await functionC(valueB);
|
const valueC = await functionC(valueB);
|
||||||
return await functionD(valueC);
|
return await functionD(valueC);
|
||||||
}
|
}
|
||||||
catch(err) {
|
catch (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
} finally {
|
} finally {
|
||||||
await alwaysExecuteThisFunction();
|
await alwaysExecuteThisFunction();
|
||||||
@ -35,6 +36,9 @@ async function executeAsyncTask () {
|
|||||||
|
|
||||||
### Anti pattern code example – callback style error handling
|
### Anti pattern code example – callback style error handling
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Javascript</strong></summary>
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
getData(someParameter, function(err, result) {
|
getData(someParameter, function(err, result) {
|
||||||
if(err !== null) {
|
if(err !== null) {
|
||||||
@ -45,7 +49,7 @@ getData(someParameter, function(err, result) {
|
|||||||
getMoreData(b, function(c) {
|
getMoreData(b, function(c) {
|
||||||
getMoreData(d, function(e) {
|
getMoreData(d, function(e) {
|
||||||
if(err !== null ) {
|
if(err !== null ) {
|
||||||
// you get the idea?
|
// you get the idea?
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@ -54,6 +58,31 @@ getData(someParameter, function(err, result) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Typescript</strong></summary>
|
||||||
|
|
||||||
|
```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?
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
### Blog Quote: "We have a problem with promises"
|
### Blog Quote: "We have a problem with promises"
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
### One Paragraph Explainer
|
### 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.
|
||||||
|
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
|
|
||||||
@ -13,29 +13,54 @@ Typically, most of modern Node.js/Express application code runs within promises
|
|||||||
```javascript
|
```javascript
|
||||||
DAL.getUserById(1).then((johnSnow) => {
|
DAL.getUserById(1).then((johnSnow) => {
|
||||||
// this error will just vanish
|
// this error will just vanish
|
||||||
if(johnSnow.isAlive == false)
|
if(johnSnow.isAlive === false)
|
||||||
throw new Error('ahhhh');
|
throw new Error('ahhhh');
|
||||||
});
|
});
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
|
|
||||||
### Code example: Catching unresolved and rejected promises
|
### Code example: Catching unresolved and rejected promises
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Javascript</strong></summary>
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
process.on('unhandledRejection', (reason, p) => {
|
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;
|
throw reason;
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('uncaughtException', (error) => {
|
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);
|
errorManagement.handler.handleError(error);
|
||||||
if (!errorManagement.handler.isTrustedError(error))
|
if (!errorManagement.handler.isTrustedError(error))
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Typescript</strong></summary>
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
process.on('unhandledRejection', (reason: string, p: Promise<any>) => {
|
||||||
|
// 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);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
|
|
||||||
@ -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?
|
> Let’s test your understanding. Which of the following would you expect to print an error to the console?
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
Promise.resolve(‘promised value’).then(() => {
|
Promise.resolve('promised value').then(() => {
|
||||||
throw new Error(‘error’);
|
throw new Error('error');
|
||||||
});
|
});
|
||||||
|
|
||||||
Promise.reject(‘error value’).catch(() => {
|
Promise.reject('error value').catch(() => {
|
||||||
throw new Error(‘error’);
|
throw new Error('error');
|
||||||
});
|
});
|
||||||
|
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
throw new Error(‘error’);
|
throw new Error('error');
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,9 @@ Without one dedicated object for error handling, greater are the chances of impo
|
|||||||
|
|
||||||
### Code Example – a typical error flow
|
### Code Example – a typical error flow
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Javascript</strong></summary>
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// DAL layer, we don't handle errors here
|
// DAL layer, we don't handle errors here
|
||||||
DB.addDocument(newCustomer, (error, result) => {
|
DB.addDocument(newCustomer, (error, result) => {
|
||||||
@ -33,9 +36,46 @@ app.use(async (err, req, res, next) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Typescript</strong></summary>
|
||||||
|
|
||||||
|
```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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
### Code example – handling errors within a dedicated object
|
### Code example – handling errors within a dedicated object
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Javascript</strong></summary>
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
module.exports.handler = new errorHandler();
|
module.exports.handler = new errorHandler();
|
||||||
|
|
||||||
@ -48,9 +88,31 @@ function errorHandler() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Typescript</strong></summary>
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
class ErrorHandler {
|
||||||
|
public async handleError(err: Error): Promise<void> {
|
||||||
|
await logger.logError(err);
|
||||||
|
await sendMailToAdminIfCritical();
|
||||||
|
await saveInOpsQueueIfCritical();
|
||||||
|
await determineIfOperationalError();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handler = new ErrorHandler();
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
### Code Example – Anti Pattern: handling errors within the middleware
|
### Code Example – Anti Pattern: handling errors within the middleware
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Javascript</strong></summary>
|
||||||
|
|
||||||
```javascript
|
```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) => {
|
app.use((err, req, res, next) => {
|
||||||
@ -63,6 +125,25 @@ app.use((err, req, res, next) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Typescript</strong></summary>
|
||||||
|
|
||||||
|
```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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
### Blog Quote: "Sometimes lower levels can’t do anything useful except propagate the error to their caller"
|
### Blog Quote: "Sometimes lower levels can’t do anything useful except propagate the error to their caller"
|
||||||
|
|
||||||
|
|||||||
@ -22,11 +22,15 @@ function addNewMember(newMember) {
|
|||||||
Joi.assert(newMember, memberSchema); //throws if validation fails
|
Joi.assert(newMember, memberSchema); //throws if validation fails
|
||||||
// other logic here
|
// other logic here
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Anti-pattern: no validation yields nasty bugs
|
### Anti-pattern: no validation yields nasty bugs
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Javascript</strong></summary>
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// if the discount is positive let's then redirect the user to print his discount coupons
|
// if the discount is positive let's then redirect the user to print his discount coupons
|
||||||
function redirectToPrintDiscount(httpResponse, member, discount) {
|
function redirectToPrintDiscount(httpResponse, member, discount) {
|
||||||
@ -37,8 +41,24 @@ function redirectToPrintDiscount(httpResponse, member, discount) {
|
|||||||
|
|
||||||
redirectToPrintDiscount(httpResponse, someMember);
|
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?
|
||||||
|
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Typescript</strong></summary>
|
||||||
|
|
||||||
|
```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?
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
### Blog Quote: "You should throw these errors immediately"
|
### Blog Quote: "You should throw these errors immediately"
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,9 @@ Distinguishing the following two error types will minimize your app downtime and
|
|||||||
|
|
||||||
### Code Example – marking an error as operational (trusted)
|
### Code Example – marking an error as operational (trusted)
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Javascript</strong></summary>
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// marking an error object as operational
|
// marking an error object as operational
|
||||||
const myError = new Error("How can I add new product when no value provided?");
|
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);
|
throw new AppError(errorManagement.commonErrors.InvalidInput, "Describe here what happened", true);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Typescript</strong></summary>
|
||||||
|
|
||||||
|
```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);
|
||||||
|
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
### Blog Quote: "Programmer errors are bugs in the program"
|
### Blog Quote: "Programmer errors are bugs in the program"
|
||||||
|
|
||||||
|
|||||||
@ -6,12 +6,15 @@ Somewhere within your code, an error handler object is responsible for deciding
|
|||||||
|
|
||||||
### Code example: deciding whether to crash
|
### Code example: deciding whether to crash
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Javascript</strong></summary>
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// 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) {
|
process.on('uncaughtException', function(error) {
|
||||||
errorManagement.handler.handleError(error);
|
errorManagement.handler.handleError(error);
|
||||||
if(!errorManagement.handler.isTrustedError(error))
|
if(!errorManagement.handler.isTrustedError(error))
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
});
|
});
|
||||||
|
|
||||||
// centralized error handler encapsulates error-handling related logic
|
// centralized error handler encapsulates error-handling related logic
|
||||||
@ -28,6 +31,51 @@ function errorHandler() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Typescript</strong></summary>
|
||||||
|
|
||||||
|
```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<void> {
|
||||||
|
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();
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
### Blog Quote: "The best way is to crash"
|
### Blog Quote: "The best way is to crash"
|
||||||
|
|
||||||
|
|||||||
@ -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
|
### Code example: ensuring the right exception is thrown using Mocha & Chai
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Javascript</strong></summary>
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
describe("Facebook chat", () => {
|
describe("Facebook chat", () => {
|
||||||
it("Notifies on new chat message", () => {
|
it("Notifies on new chat message", () => {
|
||||||
@ -14,13 +17,30 @@ describe("Facebook chat", () => {
|
|||||||
expect(chatService.sendMessage.bind({ message: "Hi" })).to.throw(ConnectionError);
|
expect(chatService.sendMessage.bind({ message: "Hi" })).to.throw(ConnectionError);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Typescript</strong></summary>
|
||||||
|
|
||||||
|
```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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
### Code example: ensuring API returns the right HTTP error code
|
### Code example: ensuring API returns the right HTTP error code
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Javascript</strong></summary>
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
it("Creates new Facebook group", function (done) {
|
it("Creates new Facebook group", (done) => {
|
||||||
var invalidGroupInfo = {};
|
var invalidGroupInfo = {};
|
||||||
httpRequest({
|
httpRequest({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -30,9 +50,33 @@ it("Creates new Facebook group", function (done) {
|
|||||||
json: true
|
json: true
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
// if we were to execute the code in this block, no error was thrown in the operation above
|
// 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);
|
expect(400).to.equal(response.statusCode);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Typescript</strong></summary>
|
||||||
|
|
||||||
|
```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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
</details>
|
||||||
@ -11,7 +11,7 @@ We all love console.log but obviously, a reputable and persistent logger like [W
|
|||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// your centralized logger object
|
// your centralized logger object
|
||||||
var logger = new winston.Logger({
|
const logger = new winston.Logger({
|
||||||
level: 'info',
|
level: 'info',
|
||||||
transports: [
|
transports: [
|
||||||
new (winston.transports.Console)()
|
new (winston.transports.Console)()
|
||||||
@ -20,22 +20,20 @@ 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' });
|
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)
|
### Code Example – Querying the log folder (searching for entries)
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var options = {
|
const options = {
|
||||||
from: new Date - 24 * 60 * 60 * 1000,
|
from: Date.now() - 24 * 60 * 60 * 1000,
|
||||||
until: new Date,
|
until: new Date(),
|
||||||
limit: 10,
|
limit: 10,
|
||||||
start: 0,
|
start: 0,
|
||||||
order: 'desc',
|
order: 'desc',
|
||||||
fields: ['message']
|
fields: ['message']
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Find items logged between today and yesterday.
|
// Find items logged between today and yesterday.
|
||||||
winston.query(options, function (err, results) {
|
winston.query(options, function (err, results) {
|
||||||
// execute callback with results
|
// execute callback with results
|
||||||
|
|||||||
@ -38,6 +38,9 @@ if(!productToAdd)
|
|||||||
|
|
||||||
### Code example – doing it even better
|
### Code example – doing it even better
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Javascript</strong></summary>
|
||||||
|
|
||||||
```javascript
|
```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) {
|
function AppError(name, httpCode, description, isOperational) {
|
||||||
@ -55,6 +58,38 @@ module.exports.AppError = AppError;
|
|||||||
if(user == null)
|
if(user == null)
|
||||||
throw new AppError(commonErrors.resourceNotFound, commonHTTPErrors.notFound, "further explanation", true)
|
throw new AppError(commonErrors.resourceNotFound, commonHTTPErrors.notFound, "further explanation", true)
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Typescript</strong></summary>
|
||||||
|
|
||||||
|
```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)
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
*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"
|
### Blog Quote: "I don’t see the value in having lots of different types"
|
||||||
|
|
||||||
|
|||||||
@ -8,39 +8,54 @@ The latest Express generator comes with a great practice that is worth to keep -
|
|||||||
|
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
|
|
||||||
### Code example: API declaration, should reside in app.js
|
### Code example: API declaration, should reside in app.js/app.ts
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var app = express();
|
const app = express();
|
||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json());
|
||||||
app.use("/api/events", events.API);
|
app.use("/api/events", events.API);
|
||||||
app.use("/api/forms", forms);
|
app.use("/api/forms", forms);
|
||||||
```
|
```
|
||||||
|
|
||||||
<br/><br/>
|
|
||||||
|
|
||||||
### Code example: Server network declaration, should reside in /bin/www
|
### Code example: Server network declaration, should reside in /bin/www
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Javascript</strong></summary>
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var app = require('../app');
|
var app = require('../app');
|
||||||
var http = require('http');
|
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');
|
var port = normalizePort(process.env.PORT || '3000');
|
||||||
app.set('port', port);
|
app.set('port', port);
|
||||||
|
|
||||||
/**
|
// Create HTTP server.
|
||||||
* Create HTTP server.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var server = http.createServer(app);
|
var server = http.createServer(app);
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Typescript</strong></summary>
|
||||||
|
|
||||||
|
```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);
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
### Example: test your API in-process using supertest (popular testing package)
|
### Example: test your API in-process using supertest (popular testing package)
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Javascript</strong></summary>
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
@ -56,4 +71,28 @@ request(app)
|
|||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
});
|
});
|
||||||
````
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Typescript</strong></summary>
|
||||||
|
|
||||||
|
```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;
|
||||||
|
});
|
||||||
|
|
||||||
|
```
|
||||||
|
</details>
|
||||||
Reference in New Issue
Block a user