Master HTTP methods and RESTful API concepts to build and interact with web services.
HTTP methods define the type of operation being performed on a resource in a RESTful API. Each method has specific semantics and use cases:
Used to retrieve resources without modifying them. GET requests are:
Example of a GET request using Java's HttpClient:
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/songs/imagine-dragons/radioactive"))
.header("Accept", "application/json")
.GET() // This is the default, so it could be omitted
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Status code: " + response.statusCode());
System.out.println("Response body: " + response.body());
Used to create new resources. POST requests are:
Example of a POST request:
String jsonBody = """
{
"artist": "Imagine Dragons",
"songTitle": "Believer",
"albumTitle": "Evolve",
"yearReleased": 2017,
"genres": ["Alternative Rock", "Pop Rock"]
}
""";
HttpRequest postRequest = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/songs"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse<String> postResponse = client.send(postRequest, HttpResponse.BodyHandlers.ofString());
// Expect a 201 Created status code for successful creation
System.out.println("Status code: " + postResponse.statusCode());
System.out.println("Response body: " + postResponse.body());
Used to update existing resources or create them if they don't exist. PUT requests are:
Example of a PUT request:
String updateJson = """
{
"artist": "Imagine Dragons",
"songTitle": "Believer",
"albumTitle": "Evolve",
"yearReleased": 2017,
"genres": ["Alternative Rock", "Pop Rock", "Electronic"]
}
""";
HttpRequest putRequest = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/songs/imagine-dragons/believer"))
.header("Content-Type", "application/json")
.PUT(HttpRequest.BodyPublishers.ofString(updateJson))
.build();
HttpResponse<String> putResponse = client.send(putRequest, HttpResponse.BodyHandlers.ofString());
// Expect a 200 OK or 204 No Content for successful update
System.out.println("Status code: " + putResponse.statusCode());
Used to remove resources. DELETE requests are:
Example of a DELETE request:
HttpRequest deleteRequest = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/songs/imagine-dragons/believer"))
.DELETE()
.build();
HttpResponse<String> deleteResponse = client.send(deleteRequest, HttpResponse.BodyHandlers.ofString());
// Expect a 204 No Content for successful deletion
System.out.println("Status code: " + deleteResponse.statusCode());
HTTP status codes indicate the result of the request:
When consuming RESTful APIs, you need to properly handle and process the responses:
RESTful APIs typically return data in JSON or XML format. JSON is the most common format due to its simplicity and compatibility with JavaScript:
// Example JSON response for a GET request to /songs/imagine-dragons/radioactive
{
"artist": "Imagine Dragons",
"songTitle": "Radioactive",
"albumTitle": "Night Visions",
"yearReleased": 2012,
"genres": ["Alternative Rock", "Indie Rock"],
"playCount": 10452687
}
In Java, you can parse JSON responses using libraries like Jackson or Gson:
// Using Jackson for JSON processing
import com.fasterxml.jackson.databind.ObjectMapper;
// Send the GET request
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// Check for successful response
if (response.statusCode() == 200) {
// Parse JSON to Java object
ObjectMapper mapper = new ObjectMapper();
MusicItem song = mapper.readValue(response.body(), MusicItem.class);
System.out.println("Loaded song: " + song.getSongTitle());
System.out.println("Album: " + song.getAlbumTitle());
System.out.println("Year: " + song.getYearReleased());
} else {
System.out.println("Error: " + response.statusCode());
// Handle error based on status code
}
Proper error handling is crucial when working with APIs:
try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
switch (response.statusCode() / 100) {
case 2: // 2xx Success
// Process successful response
break;
case 4: // 4xx Client Error
if (response.statusCode() == 404) {
System.out.println("Resource not found");
} else if (response.statusCode() == 401 || response.statusCode() == 403) {
System.out.println("Authentication or authorization error");
} else {
System.out.println("Client error: " + response.statusCode());
}
break;
case 5: // 5xx Server Error
System.out.println("Server error: " + response.statusCode());
// Implement retry logic for 5xx errors
break;
}
} catch (IOException e) {
System.out.println("Network error: " + e.getMessage());
} catch (InterruptedException e) {
System.out.println("Request interrupted: " + e.getMessage());
Thread.currentThread().interrupt();
}
cURL is a command-line tool for making HTTP requests, useful for testing APIs:
# GET request
curl -X GET https://api.example.com/songs/imagine-dragons/radioactive
# POST request with JSON body
curl -X POST https://api.example.com/songs \
-H "Content-Type: application/json" \
-d '{"artist":"Imagine Dragons","songTitle":"Believer","albumTitle":"Evolve","yearReleased":2017}'
# PUT request
curl -X PUT https://api.example.com/songs/imagine-dragons/believer \
-H "Content-Type: application/json" \
-d '{"artist":"Imagine Dragons","songTitle":"Believer","albumTitle":"Evolve","yearReleased":2017,"genres":["Alternative Rock","Pop Rock"]}'
# DELETE request
curl -X DELETE https://api.example.com/songs/imagine-dragons/believer
When designing or working with RESTful APIs, keep these best practices in mind:
# Good examples of RESTful URL patterns
/artists # List all artists
/artists/123 # Get a specific artist
/artists/123/albums # List all albums by artist 123
/artists/123/albums/456 # Get a specific album by artist 123
/artists/123/albums/456/songs # List all songs in album 456 by artist 123
Secure your API with HTTPS to protect data in transit. This is not optional in production environments.
Include API versioning to make changes without breaking existing clients:
# Versioning in URL path
/api/v1/songs
# Versioning in header
curl -H "Accept: application/vnd.example.v1+json" https://api.example.com/songs
Return appropriate HTTP status codes for different scenarios:
For collections, support pagination, filtering, and sorting via query parameters:
# Pagination
/songs?page=2&limit=10
# Filtering
/songs?genre=rock&year=2020
# Sorting
/songs?sort=yearReleased:desc
Return descriptive error responses with appropriate status codes:
# Example error response
{
"status": 400,
"error": "Bad Request",
"message": "Validation failed",
"errors": [
{
"field": "yearReleased",
"message": "Year must be between 1900 and current year"
}
],
"timestamp": "2023-06-01T12:34:56Z",
"path": "/songs"
}
Understand and respect idempotency:
This is especially important for error handling and retries. If a request fails due to a network issue, idempotent operations can be safely retried without side effects.