You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

90 lines
24 KiB

import { addBanner, addArticle, addTitle, addHeader, addParagraph, addClassParagraph, addCaption, addSubHeader, addOrderedList, addUnorderedList, addBlockquote, addQuote, addInset, addInsetList, addInsetCodeListing, addInsetBulletList, addImageWithCaption, addButtonGroup, addSidebar, addSyntax, menu, global_menu } from '/scripts/import.js';
import { local_menu } from '/scripts/webdev.js';
const heading = document.querySelector(".heading");
const global = document.querySelector(".global_menu");
const local = document.querySelector(".local_menu");
const sidebar = document.querySelector(".sidebar");
const main = document.querySelector(".main_content");
heading.append(addTitle("Learning REST APIs"));
heading.append(addParagraph("Morten Rand-Hendriksen - LinkedIn Learning - February 2018"));
heading.append(addParagraph("Chapter 4 - Request/Response Pairs"));
main.append(addHeader("Request/Response Pairs"));
main.append(addParagraph("For this section, I will keep things simple by demonstrating concepts using screenshots from the course with requests and responses. You may recall that the API used in the course is from a locally installed copy of WordPress and since I don't have an installed copy, I can't send the same requests as shown here. The point is to see some examples of requests being sent and the sort of responses you might expect to see when sending such requests."));
main.append(addParagraph("We will start with the OPTIONS method and a URL of"));
main.append(addSyntax("http://restful.dev.wp-json/wp/v2/posts/"));
main.append(addParagraph("This request and its response are shown in figures 15 and 16."));
main.append(addImageWithCaption("./images/options1.png", "Figure 15 - The response when sent with the OPTIONS method."));
main.append(addImageWithCaption("./images/options2.png", "Figure 16 - The response when sent with the OPTIONS method showing some of the parameters."));
main.append(addParagraph("Notice in figure 15, that both the GET and POST methods are allowed based on the basic authorisation credentials provided. It might be worth pointing out here that sending credentials in the form of plain text like this is not something you should normally do, but it is being done that way here for demonstration purposes only."));
main.append(addParagraph("In figure 16, we are seeing some additional details found in the response. Specifically, we ae seeing some of the parameters that can be used with a request. A good example is the per_page parameter which limits the number of items that can be returned in a result set. Additional details about this parameter are the type which is an integer, the default value which is 10 and the required status which is false. So this is an optional parameter."));
main.append(addParagraph("I would assume that a result set in this context refers to a page of the results returned. For example, if 45 items are returned and the default value of 10 is used, we would see 5 pages of results (or 5 result sets) with 10 items in each of the first 4 pages and 5 in the last page. That makes sense given the name of the parameter but I think that the definition of result set is being contorted a little in order to make it fit more neatly with the parameter's name. In reality, I would expect that results set would define the whole set of items returned regardless of page size and the course video states that this does restrict the number of items being returned. It mentions that there are around 100 posts in the API and that this parameter is used to restrict the results returned so that you don't get too many posts in a response so I - and this is just a personal opinion - think that the parameter is badly named here and doesn't properly make it clear to the user what it represents."));
main.append(addParagraph("I would suggest that the key point to take from that is that you may need to be careful with parameter definitions and experiment in order to determine exactly how they affect the results you are seeing returned."));
main.append(addParagraph("In theory, OPTIONS will provide all of the options available for a particular resource but you should be careful to use those options intelligently and where possible, experiment with them to ensure you understand their usage as fully as possible."));
main.append(addHeader("GET"));
main.append(addParagraph("This time, we are going to send the same request as we did in the previous section, but we will use the GET method. Whereas the OPTIONS method is used to request details about the resource and the options available for using it, the GET method is requesting that the API sends us the data associated with that resource. In this case, the resource is a collection of posts and so with the GET request, we would expect that those posts would be returned to us. The request and response are shown in figures 17 and 18."));
main.append(addImageWithCaption("./images/get.png", "Figure 17 - The response when sent with the GET method - headers."));
main.append(addImageWithCaption("./images/get1.png", "Figure 18 - The response when sent with the GET method - data."));
main.append(addParagraph("In figure 17, we can see some of the headers returned as part of the response to the GET request. Notice that we can also see some of the information we would get from using the OPTIONS method such as the methods (GET and POST) permitted for use with this resource and again, remember that this is a request from an authenticated user. There is too much data to display in a single screenshot like this, but the method has returned 10 posts since this is the default option and we didn't override this by sending a page-size parameter."));
main.append(addParagraph("The actual data, the posts, are presented as nested JSON objects and we can see this object representing the first returned post in figure 18. The data presented for each post includes the id of the post, the publication and modification dates, the slug, the link and the title and so on. The slug (or URL slug) is an identifier for the resource and the link is a human readable link to the resource (the resource's full URL)."));
main.append(addParagraph("Figure 18 doesn't show the actual post but it is included in the response."));
main.append(addParagraph("In the previous section, we did see that there are a lot of optional parameters for for both GET and POST and we can add these to our query by tagging them on to the end of the URL. For example, if we only want to see 1 post being returned, we need to send the request with a per_page parameter and a value of 1. As a result, the URL is modified to become"));
main.append(addSyntax("http://restful.dev.wp-json/wp/v2/posts?per_page=1"));
main.append(addParagraph("The response is the same as the one we saw previously in the sense that we didn't scroll past the first post and so we will see pretty much the same thing agian. The differebce is that without the parameter, if we did scroll past the first post, we would see another post and if we continue to scroll, we will see that there are 10 posts. Bear in mind that if we do leave the default value of per_page as 10, this in itself doesn't guarantee that we will see 10 posts being returned. If the resource contains data for less than 10 posts, we would obviously see only those posts beig returned. You might say that the request would return the number of posts specified by the per_page paramater or all posts if total number is less than the specified per_page value."));
main.append(addParagraph("If you scroll down far enough, you will also see a _links property which provides a link to a specific post which might look something like"));
main.append(addSyntax("http://restful.dev.wp-json/wp/v2/posts/4"));
main.append(addParagraph("This is a very specific link to the actual post so it is, in effct, a link to the singleton resource - note that this is included as self in the json and its value is another JSON object vith an href attribute whose value that link to the singleton resource. There is also a link to the collection and this is, in fact, the same URL we used before which did not link to a sinelton resource. That is"));
main.append(addSyntax("http://restful.dev.wp-json/wp/v2/posts/"));
main.append(addParagraph("In theory, we could copy the link and use it as the address in a browser but that assumes the API is avaiable over the internet. In theory, all APIs are because the API itself can notmally be accessed over the internet but in this case, it isn'tand so we can't really access it."));
main.append(addHeader("POST"));
main.append(addParagraph("In most cases, an API will not allow a POST request to be sent unless the user is authorised so not everyone will be able to post on a particular API and if you are able to, you will most likely have to be authenticated in some way. Aside from that, the major difference you will see with a POST request is that the request itself is more complex since in addition to using the POST method to tell the API this request is providing new data to the API - in the context of the WordPress site, we are adding a new post - so we need to provide the data for that new post and this is shown in figure 19."));
main.append(addImageWithCaption("./images/post.png", "Figure 19 - The POST request."));
main.append(addParagraph("The Authorization header was optional with a GET request, but it has to be included with the POST request. Notice that we also have an additional header telling the API that te data we are providing is in JSON format."));
main.append(addParagraph("We then have our JSON and we have provided four fields with an appropriate value for each. Going back to the OPTIONS method, remember that this shows us the possible parameters when posting to this particular resource (the resource being the collection of posts) and it also tells us which fields are required and which are optional. In this example, title and content are required and the other two parameters are optional. Both of the optional parameters have a default value and so you would provide values for them here if you want to override those defaults. For status, the default is draft, so we provide a status of publish to ensure that the post is immediately available to the public. The author field will default to the site-owner so you would likely want to provide a value for that if that isn't you."));
main.append(addParagraph("We are not necessarily too interested in the reponse here beyond the fact that we do want to see a HTTP reponse status of 201 indicate that the post has been successfully created. However, with a POST request, we do get the entire resource returned and this is so that the client can perform any error-checking that it wants to and also so that it has access to the resource. In this case, the resource is being used in a display of WordPress posts and we can see that in figure 20."));
main.append(addImageWithCaption("./images/wordpress.png", "Figure 19 - The posts displayed in WordPress including the one added by the request shown in figure 19."));
main.append(addParagraph("Note that in figure 20, we can't see all of the posts, just the posts for this specific user (Morten) but this does include the new post that was added by the request shown in figure 19. So we don't necessarily need to see this in the response, but the client (WordPress) does need this data to be returned so that it can update its display to show the new POST."));
main.append(addParagraph("Back in the first chapter, we looked at the constraints required for a REST API and one of these was 'Hypermedia as the Engine of Application' which may have been the most difficult to understand. With the POST request, we can possibly help to understand that a little bit better. With the data returned by the POST request, we have a resource link to the new post, but since this request was used to create that resource, we also have a direct link to it. We can grab that link and use it in a GET request and this will return, specifically, the new post as shown in figure 21."));
main.append(addImageWithCaption("./images/post1.png", "Figure 21 - A GET request using the direct link to the resource returned in the response to the POST request."));
main.append(addParagraph("We don't need the JSON data for this request so it has been commented out. Notice that the URL for the request is slightly obscured but we can see it highlighted in the response. We haven't sent the GET request yet so this is the response to the previous POST request and we can see this on the first line of the response. This shows a response status of 201 which we would not see with a GET request."));
main.append(addParagraph("When this GET request is sent, we will see the response status is now 200 and the data provided in this response is the singelton resource. In other words, it is just the single post that we wanted and this is shown in figure 22."));
main.append(addImageWithCaption("./images/post2.png", "Figure 22 - The response to the GET request shown in figure 21."));
main.append(addHeader("PUT/PATCH"));
main.append(addParagraph("Let's say that we want to change the title for the post we created in the previous section. To do that, we would use a POST, PUT or PATCH method. we did mention these in chapter 2 where we said that the POST method is used to create a resource, the PUT method is used to update a resource by replacing the entire content for that resource and the PATCH method is used to update a resource by providing a new value for the specific field to be updated, so it doesn't replace the whole resource and doesn't need to provide all of the data for that resource. In terms of the post, it means that we only need to send the new title, we don't need to send the content again, for example. This suggests that we should use the PATCH method here, but you must also remember that the API designers can also place some constraints on this in that they may not allow all of these methods and they may not always work as expected."));
main.append(addParagraph("This is one of the reasons why we need to do some research in advance, particularly for an API we might not familiar with to find out that options are permitted, what values are required or optional, what the default value are and so on."));
main.append(addParagraph("Let's just recall the GET method we used in the previous section to return the singleton resource for the post we had created. This was"));
main.append(addSyntax("GET http://restful.dev.wp-json/wp/v2/posts/15"));
main.append(addParagraph("To get some information on the options available specifically for this request, we can resend the request using the OPTIONS method."));
main.append(addSyntax("OPTIONS http://restful.dev.wp-json/wp/v2/posts/15"));
main.append(addParagraph("Part of the response is shown in figure 23."));
main.append(addImageWithCaption("./images/options3.png", "Figure 23 - The response to the OPTIONS request showing the possible methods we can use with the post."));
main.append(addParagraph("This shows that we can use five different methods on the post, including POST, PUT and PATCH. We can search through the response in VS Code to find the method definitions so let's search for PUT. The result is shown in figure 24."));
main.append(addImageWithCaption("./images/options4.png", "Figure 24 - The PUT method."));
main.append(addParagraph("The interesting thing here is that all three methods share the same definition so it actually doesn't matter which of the methods we use, they will all have the same effect. Note that id is an optional field and this means that we can send an id with a POST request so we can use it to update an existing record. In this case, we will use the PUT request shown in figure 25."));
main.append(addImageWithCaption("./images/put.png", "Figure 25 - The PUT request."));
main.append(addParagraph("Not surprisingly, this looks pretty similar to the POST request. We have the same headers for Authorization and Content-Type. In terms of the data, we are only providing the new title since this is the only field in the post that we want to update."));
main.append(addParagraph("When we send this request, if it is successful, we should see a HTTP response status of 200 (we didn't create a new resource so we wouldn't expect to see a 201 response). We can then view the post in WordPress again to see the updated title as shown in figure 26."));
main.append(addImageWithCaption("./images/wordpress1.png", "Figure 26 - The posts displayed in WordPress showing the title updated with the PUT request shown in figure 25."));
main.append(addParagraph("Again, we see the resource being returned by the PUT request and we can see that the slug and the links are the same so this is the same resource with just the title having been changed so this confirms that the PUT request has done exactly as we expected to."));
main.append(addParagraph("You should bear in mind that in this example, POST, PUT and PATCH do exactly the same thing so it wouldn't have mattered if we had used the POST or PATCH methods for the request in figure 25. Any of these would achieve the same result."));
main.append(addParagraph("But you should also remember that these are different methods in terms of how they are defined. It just so happens that the WordPress API developers have chosen to ignore the differences and have all three methods do the same thing which also means, of course, that we could have used either a PUT or a PATCH method rather than a POST method to create the post in the first place."));
main.append(addParagraph("This does mean that if you wanted to, you could just choose one of the method and use it for creating or updating posts, so why would we need to have all three? I think the answer to that lies in how your code is perceived by someone reading it. For example, let's say that you only use the POST method. If you have written requests that other people may be using, they would have to look at each POST request to work out whether the request is creating a new post, replacing it with a new post or editing part of the post only. Your code will be much easier to read if you write POST requests to create new posts, PUT requests to update posts by replacing the entire resource or PATCH requests to update part of the record."));
main.append(addParagraph("Of course, this is only helpful if the person reading the code is aware of that fact but the person reading your code may be you, so you can make things easier for yourself by using the methods consistently and appropriately. Someone reading your code would probably quite quickly realise that you were doing that in any case. I should just reiterate that as well as applying the methods appropriately (by which I mean, for example, you should use a PATCH method if its definition suggests it is the most appropriate which it actually would be for the request shown in figure 25), it is also important to do this consistently. You won't get so much benefit from using the most appropriate method if you occassionally throw in a PUT request, for instance, when a POST request would be more approriate."));
main.append(addHeader("DELETE"));
main.append(addParagraph("The DELETE method is reasonably straightforward to use, you just need to send a DELETE request with the proper authorisation to the specific resource you want to delete, as shown in figure 27."));
main.append(addImageWithCaption("./images/delete.png", "Figure 27 - The DELETE request."));
main.append(addParagraph("Notice that we don't have to provide anything to identify the post we want to delete because we are sending the request directly to the post. You might recall that the delete method only works on a singleton resource and this is the reason why although I would guess that an API developer might decide to implement in a way that allows it to be used on a collection in which case an id would be required."));
main.append(addParagraph("In any case, if we send this request, we should see a 200 response if the request is successful. Interestingly, you also get the resource - the post - returned in the response and part of that response is shown in figure 28."));
main.append(addImageWithCaption("./images/delete1.png", "Figure 28 - The response from the DELETE request."));
main.append(addParagraph("You might be surprised to see the post itself included in the response but you can scroll through and you will see that the post's status has been changed to 'trash', this is highlighted in figure 28. In fact, the post has not actually been deleted, it has simply been moved to trash and this is consistent with the WordPress interface seen in figures 19 and 26. In both of these inages, the mouse pointer is hovering over the new post and this shows the options available via the interface and this includes Trash. So there is no option to delete the post outright in the interface, it can only be moved to trash and the DELETE method replicates this."));
main.append(addParagraph("If we want to find out how to completely delete a post, we need to look at the options again so we can send the OPTIONS request to the post so this is the same request as the one shown in figure 27 but using the OPTIONS rather than the DELETE method. We saw earlier that DELETE was one of the permitted methods on a post, but we have also seen that the default behaviour of this method is to move the post to trash by changing it's status rather than deleting it outright. It therefore makes sense to look at the optional parameters for this method and some of these are shown in figure 29."));
main.append(addImageWithCaption("./images/delete2.png", "Figure 28 - Some of the optional paramaters for the DELETE method."));
main.append(addParagraph("You may have noticed that the DELETE request in figure 27 doesn't use any parameters and we can infer from that the fact that all of its parameters are optional - there are no required parameters. One of these optional parameters is force which is set to false by default. If this parameter has a value of true, the post will be completely deleted, bypassing the trash can (as noted in the parameter description)."));
main.append(addParagraph("As before, we can append the parameter onto the end of the URL so that gives us"));
main.append(addSyntax("http://restful.dev.wp-json/wp/v2/posts/15?force=true"));
main.append(addParagraph("We will also need the Authorization header for this requrest and when we send it, we should see a response status of 200. We still get the post returned as part of the response and it actually still shows a status of trash which implies that it is still in the trash can, but this is actually just included in case you want to make a backup of the post after deleting it. We can send a GET request to the singleton resoource and we should get a 404 response as well as a message in the response telling us that this post ID is invalid. If we had access to the WordPress interface, we can also confirm that the post has been completely deleted by checking the posts to see that it is not present and checking the posts in trash to confirm that it is also not there."));
main.append(addParagraph("Knowing what these HTTP methods (or HTTP verbs as they are sometimes called) makes it relatively easy to interact with an API. In addition, the REST constraints we looked at in the first chapter, particularly 6.3 - Self-descriptive messages and 6.4 - Hypermedia as the engine of application, mean that even if you are familiar with an API, you should be able to find out what you need to know to send valid and meaningful requests either by examining responses or by sending a request using the OPTIONS method to a resource to find out more about it. In many cases, you may find that there is sufficient documentation online in order for you to use the API, but it is always useful to know that if the API does follow all of the constraints for REST and is properly documented, you can use the API itself to find out whatever you need to know."));
addSidebar("webdev");