Making GET Requests and Handling Responses

Welcome to the second lesson in our journey of interacting with APIs in Dart. In the previous lesson, we laid a strong foundation by understanding RESTful APIs and how HTTP requests facilitate interactions with them. We used curl to manually craft and send a GET request to an endpoint. Now, we are transitioning to automating this process using Dart. This lesson introduces the http package, which allows us to send HTTP requests effortlessly in our scripts, making it an invaluable tool in the realm of web development and API integration.

Setting Up the Environment

To make HTTP requests in Dart, we use the http package, a user-friendly and powerful library for handling HTTP requests. To set up your environment, you need to add the http package to your pubspec.yaml file. Here’s how you can do it:

YAML
1dependencies: 2 http: ^0.13.3

After adding the package, run the following command in your terminal to install it:

Bash
1pub get

Once installed, you can import the http package in your Dart code, making its functionalities available for sending HTTP requests in our script.

Dart
1import 'package:http/http.dart' as http;

With this setup, you'll be equipped to automate requests, saving time and boosting efficiency in your development workflow.

Creating the Main Function

Before we proceed with making HTTP requests, it's essential to understand how Dart handles asynchronous operations. In Dart, the main function serves as the entry point of the application, where the execution begins. To perform asynchronous tasks, such as making HTTP requests, we need to mark the main function as async. This allows us to use the await keyword to pause execution until a future completes, ensuring our code runs smoothly without blocking the main thread. This approach ensures that our application remains responsive, as it can continue executing other code while waiting for the HTTP request to complete. Here's how you can set up an asynchronous main function:

Here's how you can set up an empty main function:

Dart
1void main() async { 2 // This is where we'll perform our HTTP requests 3}

By marking the main function as async, we prepare our script to handle asynchronous operations, setting the stage for making HTTP requests and processing their responses efficiently.

Defining the Base URL

When interacting with an API, it's helpful to define a base URL for the service you're communicating with. This makes your code more modular and easy to maintain, allowing you to change the root of your API URL in one place without modifying each request.

Dart
1// Base URL for the API 2final String baseUrl = "http://localhost:8000";

By setting this base URL, we can easily concatenate endpoints for different services within the API, making our code cleaner and more adaptable.

Performing a Basic GET Request

Let's dive into the process of fetching data from an API using Dart's http package. Our goal is to retrieve a list of to-do items from the /todos endpoint using the GET method.

Dart
1// Fetch all todos using the get method 2final response = await http.get(Uri.parse('$baseUrl/todos')); 3 4// Print raw response 5print("Raw Response:"); 6print(response.body);

By using the http.get() method, we send a GET request to the constructed full URL. The response from the server, stored in the variable response, is then printed using response.body, which gives us the raw response body as a string.

Here’s an example of what the raw response might look like:

Plain text
1Raw Response: 2[ 3 { 4 "description": "Milk, eggs, bread, and coffee", 5 "done": false, 6 "id": 1, 7 "title": "Buy groceries" 8 }, 9 { 10 "description": "Check in and catch up", 11 "done": true, 12 "id": 2, 13 "title": "Call mom" 14 }, 15 { 16 "description": "Summarize Q4 performance metrics", 17 "done": false, 18 "id": 3, 19 "title": "Finish project report" 20 }, 21 { 22 "description": "30 minutes of cardio", 23 "done": true, 24 "id": 4, 25 "title": "Workout" 26 } 27]

This raw output allows us to see the immediate result returned by the server, serving as a starting point for further processing of the data.

Handling Successful Requests (Status Code: 200)

To determine the outcome of an HTTP request, we use the statusCode attribute of the response object to check the status code returned by the server. When the server responds with a status code of 200, it signifies a successful interaction, meaning the server has correctly processed the request and returned the expected data. In such cases, we can confidently parse the body in the desired format, which is JSON in our case, using the jsonDecode() function from the dart:convert library. This allows us to access and display each to-do item.

First, ensure you import the dart:convert library:

Dart
1import 'dart:convert';

Then, you can handle the successful response as follows:

Dart
1if (response.statusCode == 200) { 2 print("Todos retrieved successfully:"); 3 List todos = jsonDecode(response.body); // Parsing JSON content from the response 4 for (var todo in todos) { 5 print("- ${todo['id']}: ${todo['title']} (Done: ${todo['done']})"); 6 } 7}

Here is an example of what you might see when the request is successful:

Plain text
1Todos retrieved successfully: 2- 1: Buy groceries (Done: false) 3- 2: Call mom (Done: true) 4- 3: Finish project report (Done: false) 5- 4: Workout (Done: true)

By leveraging the jsonDecode() function from the dart:convert library, we can efficiently extract and work with the data returned by the server, making our scripts more intuitive and powerful.

Handling Bad Requests (Status Code: 400)

Errors can happen on either the client or server side, so it's important to handle them properly. A 400 status code means there was a mistake in the request, often due to incorrect syntax from the client side. To understand these errors better, you can print the response body, which provides more details about what went wrong and can help you fix the issue.

Dart
1else if (response.statusCode == 400) { 2 print("\nBad Request. The server could not understand the request due to invalid syntax."); 3 print("Error Details: ${response.body}"); 4}
Handling Unauthorized Requests (Status Code: 401)

A 401 status code indicates an unauthorized request, often due to missing or invalid credentials. This situation requires the user to address authentication problems to proceed.

Dart
1else if (response.statusCode == 401) { 2 print("\nUnauthorized. Access is denied due to invalid credentials."); 3 print("Error Details: ${response.body}"); 4}
Handling Not Found Errors (Status Code: 404)

When encountering a 404 status code, it means the requested resource is not found, often pointing to a missing resource or incorrect endpoint.

Dart
1else if (response.statusCode == 404) { 2 print("\nNot Found. The requested resource could not be found on the server."); 3 print("Error Details: ${response.body}"); 4}
Handling Internal Server Errors (Status Code: 500)

A 500 status code reflects an internal server error, indicating the server encountered an unexpected situation. Such cases usually require investigation on the server side to resolve the issue.

Dart
1else if (response.statusCode == 500) { 2 print("\nInternal Server Error. The server has encountered a situation it doesn't know how to handle."); 3 print("Error Details: ${response.body}"); 4}
Handling Unexpected Status Codes

For responses falling outside the common codes, a generic approach captures these cases, ensuring all responses are analyzed for potential issues.

Dart
1else { 2 print("\nUnexpected Status Code: ${response.statusCode}"); 3 print("Error Details: ${response.body}"); 4}

By handling these diverse status codes, we ensure robust API interactions and better understand the server's communication.

Conclusion, Key Takeaways, and Next Steps

In this lesson, we've explored the powerful capability of Dart's http package to make GET requests to an API. We've seen how to retrieve and handle responses effectively, interpreting HTTP status codes to understand the server's communication. This knowledge is crucial for creating reliable interactions with APIs. As you move on to the practice exercises, focus on experimenting with the code snippets and handling various status codes to solidify your understanding. In future lessons, we will build on this foundation, unlocking the potential to perform more complex tasks like updating and manipulating API data. Keep up the great work as you advance through the course!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal