Making HTTP Requests to the Flask API using curl and httpie
Now, we are going to use the httpie and curl commands to test our API endpoints. We will test the functions for getting all the recipes back from the server and create/update/delete, publish, and unpublish the recipes. The best way to learn this is to complete a hands-on exercise. Let's get started!
Exercise 11: Testing the Endpoints Using curl and httpie
In this exercise, we are going to use the httpie and curl commands to send requests to the endpoints so that we can create our first recipe. We want you to get comfortable using the httpie and curl command-line testing tool. Let's get started:
- Open the Terminal in PyCharm and type in the following commands. You can use either the httpie or curl command. The following is the httpie command (= is for string and := is for non-string):
http POST localhost:5000/recipes name="Cheese Pizza" description="This is a lovely cheese pizza" num_of_servings:=2 cook_time:=30 directions="This is how you make it"
The following is the curl command. The -H argument is used to specify the header in the client request. We will set Content-Type: application/json as the header here. The -d argument is used for HTTP POST data, that is, the recipe in JSON format:
curl -i -X POST localhost:5000/recipes -H "Content-Type: application/json" -d '{"name":"Cheese Pizza", "description":"This is a lovely cheese pizza", "num_of_servings":2, "cook_time":30, "directions":"This is how you make it" }'
- Examine the response, you should see the following. Carefully examine it, it should be the same recipe as the one that was used in our request in Step 1:
HTTP/1.0 201 CREATED
Content-Type: application/json
Content-Length: 188
Server: Werkzeug/0.16.0 Python/3.7.0
Date: Sun, 03 Nov 2019 03:19:00 GMT
{
"id": 1,
"name": "Cheese Pizza",
"description": "This is a lovely cheese pizza",
"num_of_servings": 2,
"cook_time": 30,
"directions": "This is how you make it"
}
Note
Once the client request has been sent to the server using the HTTP POST method, the post method in RecipeResource will pick up the request and save the recipe in the request to the application memory. The new recipe will be appended in recipe_list. Once everything is done, it will return HTTP 201 CREATED and the newly created recipe in JSON format.
We have successfully created our first recipe on the platform. This recipe is stored on the server-side and we already have the API to retrieve it. Let's continue by creating our second recipe and retrieving all our recipes in one go.
Exercise 12: Testing the Auto-Incremented Recipe ID
Now that we have implemented the auto-incremented ID in our Smilecook application, let's see how it works in practice. In this exercise, we will create the second recipe using the httpie and curl commands. Note that the ID is auto- incremented for our second recipe. Let's get started:
- Create a second recipe and note that the ID is automatically incremented. Send the following client request using httpie:
http POST localhost:5000/recipes name="Tomato Pasta" description="This is a lovely tomato pasta recipe" num_of_servings:=3 cook_time:=20 directions="This is how you make it"
Alternatively, send the request using curl. Again, the -H argument is used to specify the header in the client request. We will set "Content-Type: application/json" as the header here. The -d argument is used for HTTP POST data, meaning that the recipe is in JSON format:
curl -i -X POST localhost:5000/recipes -H "Content-Type: application/json" -d '{"name":"Tomato Pasta", "description":"This is a lovely tomato pasta recipe", "num_of_servings":3, "cook_time":20, "directions":"This is how you make it"}'
- You should see the following response. Examine it carefully, it should be the same recipe as the one that was used in our request in Step 1:
HTTP/1.0 201 CREATED
Content-Type: application/json
Content-Length: 195
Server: Werkzeug/0.16.0 Python/3.7.0
Date: Sun, 03 Nov 2019 03:23:37 GMT
{
"id": 2,
"name": "Tomato Pasta",
"description": "This is a lovely tomato pasta recipe",
"num_of_servings": 3,
"cook_time": 20,
"directions": "This is how you make it"
}
Once the preceding client request has been sent to the server using the HTTP POST method, the post method in RecipeResource will pick up the request and save the recipe in the request to the application memory. The new recipe will be appended in recipe_list. This time, the ID will be automatically assigned to 2.
Exercise 13: Getting All the Recipes Back
In this exercise, we will be using the httpie and curl commands to get back all the recipes that we have created. We are doing this to ensure that our recipes are there in the backend server. Let's get started:
- Retrieve all the recipes by sending the following client request using httpie:
http GET localhost:5000/recipes
Alternatively, send the following request using curl. The -i argument is used to state that we want to see the response header. -X GET means that we are sending the client request using the HTTP GET method:
curl -i -X GET localhost:5000/recipes
- You should see the following response. Please examine it carefully:
HTTP/1.0 200 OK
Content-Length: 19
Content-Type: application/json
Date: Sun, 03 Nov 2019 03:24:53 GMT
Server: Werkzeug/0.16.0 Python/3.7.0
{
"data": []
}
Once the preceding client request has been sent to the server using the HTTP GET method, the get method in RecipeResource will pick up the request and retrieve all the published recipes from recipe_list in the application memory.
Note
We should see an empty list in the HTTP response because all the recipes we have created in the previous steps are in draft form (not published).
Exercise 14: Testing the Recipe Resources
We have already tested the endpoints we built around the recipe resources. In this exercise, we will continue to use the httpie and curl commands to test the recipe publishing API. We can test it by sending requests asking to publish our recipes on the API endpoint. Let's get started:
- Modify the publish status of the recipe with ID 1. We can send the following client request using the httpie command:
http PUT localhost:5000/recipes/1/publish
Alternatively, we can use the following curl command:
curl -i -X PUT localhost:5000/recipes/1/publish
Note
Once the preceding client request has been sent to the server using the HTTP PUT method, the put method in RecipePublishResource will pick up the request and assign recipe_id to be 1. The application will look for the recipe with ID = 1 and update its publish status to True.
- You should see the following response. Please examine it carefully:
HTTP/1.0 204 NO CONTENT
Content-Type: application/json
Date: Sun, 03 Nov 2019 03:25:48 GMT
Server: Werkzeug/0.16.0 Python/3.7.0
- Now, retrieve all the published recipes and examine them. Then, send the following client request using httpie:
http GET localhost:5000/recipes
Alternatively, send the following request using curl. The -i argument is used to say that we want to see the response header. -X GET means that we are sending the client request using the HTTP GET method:
curl -i -X GET localhost:5000/recipes
- You should see the following response. Please examine it carefully:
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 276
Server: Werkzeug/0.16.0 Python/3.7.0
Date: Sun, 03 Nov 2019 03:26:43 GMT
{
"data": [
{
"id": 1,
"name": "Cheese Pizza",
"description": "This is a lovely cheese pizza",
"num_of_servings": 2,
"cook_time": 30,
"directions": "This is how you make it"
}
]
}
Once the preceding client request has been sent to the server using the HTTP GET method, the get method in RecipeResource will pick up the request and retrieve all the published recipes from recipe_list in the application memory. This time, because the recipe with ID 1 has been set to publish, we shall see it in the HTTP response.
Exercise 15: Negative Testing
In the previous exercise, we successfully published our recipe. This is good because it shows us that the APIs that we've developed work. But the whole point of testing is to discover potential issues if any. We can perform so-called negative testing here. This is the process of deliberately testing the scenario with unwanted input. This exercise is going to test a request with an HTTP VERB that has no corresponding method defined in the resource. Let's get started:
- Send the following request to the server-side. This HTTP method has not been defined; let's see what happens:
http DELETE localhost:5000/recipes
The following is the curl command, which does the same thing:
curl -i -X DELETE localhost:5000/recipes
- You should see the following response. Please examine it carefully:
HTTP/1.0 405 METHOD NOT ALLOWED
Content-Type: application/json
Content-Length: 70
Allow: POST, GET, HEAD, OPTIONS
Server: Werkzeug/0.16.0 Python/3.7.0
Date: Sun, 03 Nov 2019 03:27:37 GMT
{
"message": "The method is not allowed for the requested URL."
}
We should see a response with an HTTP status of 405, which means that the method is not allowed for the requested URL. This makes sense because we have not defined a delete method in RecipeListResource.
Negative testing is important. We always want our testing to be more complete and covers more scenarios.
Exercise 16: Modifying the Recipes
In our Smilecook application, authors are allowed to update their recipes. It is like a blogging platform, where the authors can take their time to perfect their work, even after it has been published. Since we have already built the API, we would like to test it using Postman. Let's get started:
- Use the PUT method to send the request to localhost:5000/recipes/1, along with the new recipe details:
http PUT localhost:5000/recipes/1 name="Lovely Cheese Pizza" description="This is a lovely cheese pizza recipe" num_of_servings:=3 cook_time:=60 directions="This is how you make it"
Alternatively, send the following request using curl. The -H argument is used to specify the header in the client request. We will set "Content-Type: application/json" as the header here. The -d argument is used for HTTP POST data, meaning that the recipe will be in JSON format:
curl -i -X PUT localhost:5000/recipes/1 -H "Content-Type: application/json" -d '{"name":"Lovely Cheese Pizza", "description":"This is a lovely cheese pizza recipe", "num_of_servings":3, "cook_time":60, "directions":"This is how you make it"}'
- You should see the following response. Please examine it carefully:
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 202
Server: Werkzeug/0.16.0 Python/3.7.0
Date: Sun, 03 Nov 2019 03:28:57 GMT
{
"id": 1,
"name": "Lovely Cheese Pizza",
"description": "This is a lovely cheese pizza recipe",
"num_of_servings": 3,
"cook_time": 60,
"directions": "This is how you make it"
}
Once the preceding client request has been sent to the server using the HTTP PUT method, the put method in RecipeResource will pick up the request and assign recipe_id to be 1. The application will look for the recipe with id = 1 and update its details with those in the client request. The preceding response shows that the recipe with ID 1 is modified.
We just finished testing another important feature. You have been doing great. Let's keep going!
Exercise 17: Getting Back Specific Recipes with a Certain ID
So far, we have tested getting all the recipes back. But in the real world, a user will want to only get the recipes that they want to see. They can do this by using the recipe ID. This exercise will show you how to get a particular recipe with a certain ID. Let's get started:
- Send the following client request using httpie:
http GET localhost:5000/recipes/1
Alternatively, use the following curl command, which does the same thing:
curl -i -X GET localhost:5000/recipes/1
You should see the following response. Please examine it carefully:
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 202
Server: Werkzeug/0.16.0 Python/3.7.0
Date: Sun, 03 Nov 2019 03:29:59 GMT
{
"id": 1,
"name": "Lovely Cheese Pizza",
"description": "This is a lovely cheese pizza recipe",
"num_of_servings": 3,
"cook_time": 60,
"directions": "This is how you make it"
}
Once the preceding client request has been sent to the server using the HTTP GET method, the get method in RecipeResource will pick up the request and assign recipe_id to be 1. It will retrieve all the published recipes from recipe_list in the application memory with an HTTP status of HTTP 200.
We have just tested our Smilecook application and confirmed that it can give us back the recipe we want.
Activity 3: Testing the APIs Using Postman
We added quite a few functions in the previous exercise. Now, we need to make sure that they work properly before we move on and develop other functions. In this activity, instead of using httpie/curl, we will be testing our API using Postman. Please follow these high-level steps:
- Create the first recipe using Postman.
- Create the second recipe using Postman.
- Retrieve all the recipes using Postman.
- Set the recipes to published using Postman.
- Retrieve all the recipes using Postman again.
- Modify the recipe using Postman.
- Get a specific recipe back using Postman.
Note
The solution to this activity can be found on page 293.
Activity 4: Implementing the Delete Recipe Function
In this activity, you will implement the delete recipe function in the Smilecook application yourself. Do this by adding a delete function to RecipeResource, similar to what we did in the previous exercises. Then, we will follow the standard software development life cycle flow, which is used to test our implementation, using Postman. Follow these steps to complete this activity:
- Add the delete function to RecipeResource.
- Start up the Flask server for testing.
- Create the first recipe using Postman.
- Delete the recipe using Postman.
Note
The solution to this activity can be found on page 299.