• Tidak ada hasil yang ditemukan

Chapter 5 ■ express rest apIs

88

We moved the entire success path processing to within an if (response.ok) check. The else part just shows an error message as an alert. In both cases, we had to parse the response body. In the success case, the response body represents the updated Issue object, and in the error case, the response body contains the error object with the message.

Another way to handle this could be to throw an error if response.ok indicates a failure, so that you can handle all this in a single catch block. But this complicates things, especially since you want to show the error message that’s within the response body, as parsing the response is another asynchronous call. If you did not care about the response message, you could have just thrown an error in the first step, where you only pass on the status text message instead of the application error message. Like this:

...

}).then(response => { if (!response.ok) {

throw new Error(response.statusText);

} else {

return response.json();

} ...

To simulate the errors and test the new code, create an issue via the UI, skipping the mandatory field. An error message should show. For an invalid status, you need to test via a direct API call, or hardcode an invalid status to send to the server, because there’s no UI for setting the status yet.

Chapter 5 ■ express rest apIs

Answers to Exercises

Exercise: The List API

1. Express uses ETags to identify the version of a resource. This is a kind of hash, which will change if the resource changes.

If you look at the request headers, you will see that Chrome is sending an ETag header in the request. This means that Chrome already has a version of the resource, and if it matches the version that the server has, you are OK to use the cached version.

No, it will not be a problem if the issues list changes; see the next answer.

2. Express does not cache the response, as you may have initially suspected. Instead, the response is always generated (as seen by the log statement). It calculates the hash again, and if it matches what the browser has, it sends a 304 status code instead of the response. This is a far more reliable way of avoiding unnecessary network traffic compared to conventional caching mechanisms such as Cache-Control, Max-Age, and If-Modified-Since headers.

But note that this does not avoid processing, since the response is generated anyway. This optimization only saves some network traffic.

3. The pipe to json_pp (stands for JSON pretty-print) formats and pretty-prints the JSON output, which otherwise would have been in a single line. To always return a formatted JSON, you could replace res.json with res.send and construct the JSON string yourself using JSON.stringify(). This would allow you to pass parameters that give you a formatted string.

Or, you could set the application settings json spaces using app.set() so that all responses are pretty printed.

4. Link headers are useful if all you are doing is adding Next/

Previous links in the UI. It is more standard than a wrapper, so it needs less documentation. On the other hand, a wrapper is easier to parse for the client, and allows for programmatically generating related actions. Also, the wrapper can include other useful information such as count, which can be used to show a total count of results in the UI.

5. The difference between the array and the JSON string is in the dates. Since JSON allows only primitive types, dates are encoded as an ISO 8601 string in the JSON. If the client needs to manipulate or do calculations on dates, it must convert the string to a Date object.

Chapter 5 ■ express rest apIs

90

Exercise: Create API

1. On testing with a malformed JSON, an error and stack trace are printed in the server’s console. Also, the output of curl is an HTML response of the same stack trace. This means that somewhere along the response chain, the error was caught and sent as an HTML response to the request.

Ideally, you should catch such errors and return a JSON indicating an error code, and hide the stack trace. This involves installing your own error handling middleware in Express, which is an advanced topic. Since our API is internal, such errors will be caused by our own programming errors on the client, so, for the moment, we’ll live with this.

2. The middleware bodyParser looks at the Content-Type header and determines if and how the body can be parsed.

For JSON, the default content type is application/json. In the absence of this header, bodyParser does not parse the request body, and the variable req.body ends up as an empty object. Thus, a new issue is created without a title or owner. If you want to handle both content types, you need to instantiate the JSON parser by supplying an option. This option is an array that has all the content types that you want to accept as JSON and trigger the JSON parser.

3. I said that it’s OK to not handle malformed JSON errors because the API is for internal consumption, and they are programming errors. Getting invalid objects is also very likely a programming error, but there is a difference. No harm is done if malformed JSON errors are not handled; the request just fails. But without validation, you may end up with invalid objects in the database, and that can cause havoc later. The application must handle validation errors to protect data integrity. I’ll address this validation later in the book.

Exercise: Using the List API

1. Doing the date conversions from strings to actual dates at the point of usage is also a valid strategy. In this case, you think of the model as always containing strings. This will work well when you need to do only formatting conversions.

Chapter 5 ■ express rest apIs On the other hand, if you expect to manipulate dates (e.g.,

to show the number of days from today left for completion, or the age of the issue from the date created), it’s more convenient to convert them to dates right after the fetch.

Otherwise, you’ll be doing the same conversion multiple times.

Exercise: Using the Create API

1. We are just appending the newly created issue. If the order is important (for example, the list is sorted on some field), appending may ruin the order. In this case, it is better to refetch the entire list in the correct order from the server.

Exercise: Error Handling

1. The validations are not exhaustive. Ideally, you need to validate the type of every field, for example, effort has to be a number and dates have to be proper dates. Further, there could also be validations such as completion date that cannot be lesser than the created or current date. You could define a more elaborate schema, or use something like JSON Schema, or even use mongoose rather than invent your own.

We will not implement exhaustive validations as part of this book, because that exercise will not give new insights into the MERN stack. But remember that in a real-life application, having such validations is essential.

You should also introduce front-end validations for more instant user-friendly error messages. I’ll leave them for later chapters, as you have explored how to return errors, and that was the objective of this section.

2. Only network-related errors are thrown by fetch, such as an unreachable server. Try shutting down the server before creating an issue to simulate a network error.

3. A popular alternative is to always wrap the result in an object that contains the success status, error message, and the result.

This means that the status checking will have to be inside the second stage, post response parsing to JSON. This way, clients don’t have to look at the HTTP Status code to find out about application-level error messages.

93

© Vasan Subramanian 2017

V. Subramanian, Pro MERN Stack, DOI 10.1007/978-1-4842-2653-7_6

CHAPTER 6