In this comprehensive tutorial, we’ll learn how to consume a REST API using Java entirely from the command line. We’ll build a complete project covering all CRUD operations using the free JSONPlaceholder API. Let’s dive in!
Project Overview
Folder Structure
java-api-client/ │ ├── lib/ │ └── gson-2.10.1.jar # External JSON library │ ├── src/ │ ├── Post.java # Model class │ ├── ApiClient.java # HTTP client utility │ ├── JsonProcessor.java # JSON handling │ └── ApiConsumerApp.java # Main application │ └── compile-and-run.sh # Script to compile and run
Prerequisites
- Java 11 or higher
- Basic Java knowledge
- Command line/terminal access
- Text editor (VS Code, Vim, Nano, etc.)
Step 1: Project Setup
First, create the project structure:
# Create main project directory mkdir java-api-client cd java-api-client # Create all necessary directories mkdir -p src lib # Create a compilation script touch compile-and-run.sh chmod +x compile-and-run.sh
Step 2: Download Required Library
We’ll use Google’s Gson library for JSON processing. Download it to the lib folder:
cd lib curl -L -o gson-2.10.1.jar https://repo1.maven.org/maven2/com/google/code/gson/gson/2.10.1.jar cd ..
Step 3: Create the Model Class (Post.java)
Create src/Post.java:
package com.apiclient;
public class Post {
private int userId;
private int id;
private String title;
private String body;
// Default constructor
public Post() {}
// Constructor with parameters
public Post(int userId, String title, String body) {
this.userId = userId;
this.title = title;
this.body = body;
}
// Getters and setters
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
@Override
public String toString() {
return String.format(
"Post #%d\nUser ID: %d\nTitle: %s\nBody: %s\n%s\n",
id, userId, title, body, "-".repeat(50)
);
}
}
Step 4: Create the HTTP Client (ApiClient.java)
Create src/ApiClient.java:
package com.apiclient;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class ApiClient {
private static final String BASE_URL = "https://jsonplaceholder.typicode.com";
private static final HttpClient httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.connectTimeout(Duration.ofSeconds(10))
.build();
/**
* Send a GET request to the specified endpoint
*/
public static String get(String endpoint) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.GET()
.uri(URI.create(BASE_URL + endpoint))
.header("Accept", "application/json")
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
logRequest("GET", endpoint, response.statusCode());
return response.body();
}
/**
* Send a POST request with JSON body
*/
public static String post(String endpoint, String jsonBody) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.uri(URI.create(BASE_URL + endpoint))
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
logRequest("POST", endpoint, response.statusCode());
return response.body();
}
/**
* Send a PUT request with JSON body
*/
public static String put(String endpoint, String jsonBody) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.PUT(HttpRequest.BodyPublishers.ofString(jsonBody))
.uri(URI.create(BASE_URL + endpoint))
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
logRequest("PUT", endpoint, response.statusCode());
return response.body();
}
/**
* Send a PATCH request with JSON body
*/
public static String patch(String endpoint, String jsonBody) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.method("PATCH", HttpRequest.BodyPublishers.ofString(jsonBody))
.uri(URI.create(BASE_URL + endpoint))
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
logRequest("PATCH", endpoint, response.statusCode());
return response.body();
}
/**
* Send a DELETE request
*/
public static String delete(String endpoint) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.DELETE()
.uri(URI.create(BASE_URL + endpoint))
.header("Accept", "application/json")
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
logRequest("DELETE", endpoint, response.statusCode());
return response.body();
}
private static void logRequest(String method, String endpoint, int statusCode) {
System.out.printf("[%s] %s -> Status: %d%n", method, endpoint, statusCode);
}
}
Step 5: Create JSON Processor (JsonProcessor.java)
Create src/JsonProcessor.java:
package com.apiclient;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;
import java.util.ArrayList;
public class JsonProcessor {
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
/**
* Convert a single Post object to JSON string
*/
public static String toJson(Post post) {
return gson.toJson(post);
}
/**
* Convert JSON string to a single Post object
*/
public static Post fromJson(String json) {
return gson.fromJson(json, Post.class);
}
/**
* Convert JSON array string to List of Post objects
*/
public static List<Post> fromJsonArray(String json) {
Type listType = new TypeToken<ArrayList<Post>>(){}.getType();
return gson.fromJson(json, listType);
}
/**
* Convert List of Post objects to JSON array string
*/
public static String toJsonArray(List<Post> posts) {
return gson.toJson(posts);
}
/**
* Pretty print JSON string
*/
public static String prettyPrint(String json) {
Object obj = gson.fromJson(json, Object.class);
return gson.toJson(obj);
}
}
Step 6: Create Main Application (ApiConsumerApp.java)
Create src/ApiConsumerApp.java:
package com.apiclient;
import java.io.IOException;
import java.util.List;
import java.util.Scanner;
public class ApiConsumerApp {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("=========================================");
System.out.println(" JSONPlaceholder API Client");
System.out.println("=========================================");
while (true) {
displayMenu();
int choice = getIntInput(scanner, "Choose an option (1-7): ");
try {
switch (choice) {
case 1:
getAllPosts();
break;
case 2:
int getPostId = getIntInput(scanner, "Enter post ID to retrieve: ");
getPostById(getPostId);
break;
case 3:
createNewPost(scanner);
break;
case 4:
int putPostId = getIntInput(scanner, "Enter post ID to update: ");
updatePost(putPostId, scanner);
break;
case 5:
int patchPostId = getIntInput(scanner, "Enter post ID to partially update: ");
partiallyUpdatePost(patchPostId, scanner);
break;
case 6:
int deletePostId = getIntInput(scanner, "Enter post ID to delete: ");
deletePost(deletePostId);
break;
case 7:
System.out.println("\nThank you for using the API Client. Goodbye!");
scanner.close();
return;
default:
System.out.println("Invalid choice. Please enter a number between 1 and 7.");
}
} catch (IOException | InterruptedException e) {
System.err.println("Error: " + e.getMessage());
e.printStackTrace();
}
System.out.println("\nPress Enter to continue...");
scanner.nextLine();
}
}
private static void displayMenu() {
System.out.println("\n=== MAIN MENU ===");
System.out.println("1. Get all posts");
System.out.println("2. Get a specific post");
System.out.println("3. Create a new post");
System.out.println("4. Update a post (PUT - full update)");
System.out.println("5. Partially update a post (PATCH)");
System.out.println("6. Delete a post");
System.out.println("7. Exit");
}
private static int getIntInput(Scanner scanner, String prompt) {
while (true) {
System.out.print(prompt);
try {
int value = scanner.nextInt();
scanner.nextLine(); // Consume newline
return value;
} catch (Exception e) {
System.out.println("Invalid input. Please enter a valid number.");
scanner.nextLine(); // Clear invalid input
}
}
}
private static String getStringInput(Scanner scanner, String prompt) {
System.out.print(prompt);
return scanner.nextLine();
}
// CRUD Operations Implementation
/**
* 1. GET all posts (Read operation)
*/
private static void getAllPosts() throws IOException, InterruptedException {
System.out.println("\n--- Fetching all posts ---");
String response = ApiClient.get("/posts");
List<Post> posts = JsonProcessor.fromJsonArray(response);
System.out.println("\nTotal posts retrieved: " + posts.size());
if (!posts.isEmpty()) {
System.out.println("\nFirst 5 posts:");
for (int i = 0; i < Math.min(5, posts.size()); i++) {
System.out.println(posts.get(i));
}
if (posts.size() > 5) {
System.out.println("... and " + (posts.size() - 5) + " more posts.");
}
}
}
/**
* 2. GET a specific post by ID (Read operation)
*/
private static void getPostById(int id) throws IOException, InterruptedException {
System.out.println("\n--- Fetching post #" + id + " ---");
String response = ApiClient.get("/posts/" + id);
if (response.contains("\"id\":")) {
Post post = JsonProcessor.fromJson(response);
System.out.println("\n" + post);
} else {
System.out.println("Post not found or error occurred.");
System.out.println("Response: " + JsonProcessor.prettyPrint(response));
}
}
/**
* 3. POST a new post (Create operation)
*/
private static void createNewPost(Scanner scanner) throws IOException, InterruptedException {
System.out.println("\n--- Creating a new post ---");
int userId = getIntInput(scanner, "Enter user ID: ");
String title = getStringInput(scanner, "Enter title: ");
String body = getStringInput(scanner, "Enter body: ");
Post newPost = new Post(userId, title, body);
String jsonBody = JsonProcessor.toJson(newPost);
System.out.println("\nSending POST request with data:");
System.out.println(JsonProcessor.prettyPrint(jsonBody));
String response = ApiClient.post("/posts", jsonBody);
System.out.println("\nResponse from server:");
System.out.println(JsonProcessor.prettyPrint(response));
Post createdPost = JsonProcessor.fromJson(response);
System.out.println("\nCreated post (with generated ID):");
System.out.println(createdPost);
}
/**
* 4. PUT to update an entire post (Update operation)
*/
private static void updatePost(int id, Scanner scanner) throws IOException, InterruptedException {
System.out.println("\n--- Fully updating post #" + id + " ---");
// First, get the existing post to show what we're updating
try {
String existing = ApiClient.get("/posts/" + id);
Post existingPost = JsonProcessor.fromJson(existing);
System.out.println("\nCurrent post data:");
System.out.println(existingPost);
} catch (Exception e) {
System.out.println("Could not retrieve existing post. Continuing with update...");
}
int userId = getIntInput(scanner, "Enter new user ID: ");
String title = getStringInput(scanner, "Enter new title: ");
String body = getStringInput(scanner, "Enter new body: ");
Post updatedPost = new Post(userId, title, body);
updatedPost.setId(id); // Set the ID for the update
String jsonBody = JsonProcessor.toJson(updatedPost);
System.out.println("\nSending PUT request with data:");
System.out.println(JsonProcessor.prettyPrint(jsonBody));
String response = ApiClient.put("/posts/" + id, jsonBody);
System.out.println("\nResponse from server:");
System.out.println(JsonProcessor.prettyPrint(response));
Post resultPost = JsonProcessor.fromJson(response);
System.out.println("\nUpdated post:");
System.out.println(resultPost);
}
/**
* 5. PATCH to partially update a post (Update operation)
*/
private static void partiallyUpdatePost(int id, Scanner scanner) throws IOException, InterruptedException {
System.out.println("\n--- Partially updating post #" + id + " ---");
// Show what fields we can update
System.out.println("Which fields would you like to update?");
System.out.println("1. Title only");
System.out.println("2. Body only");
System.out.println("3. Both title and body");
int fieldChoice = getIntInput(scanner, "Enter choice (1-3): ");
String jsonBody = "";
switch (fieldChoice) {
case 1:
String title = getStringInput(scanner, "Enter new title: ");
jsonBody = String.format("{\"title\": \"%s\"}", title);
break;
case 2:
String body = getStringInput(scanner, "Enter new body: ");
jsonBody = String.format("{\"body\": \"%s\"}", body);
break;
case 3:
title = getStringInput(scanner, "Enter new title: ");
body = getStringInput(scanner, "Enter new body: ");
jsonBody = String.format("{\"title\": \"%s\", \"body\": \"%s\"}", title, body);
break;
default:
System.out.println("Invalid choice. Returning to menu.");
return;
}
System.out.println("\nSending PATCH request with data:");
System.out.println(JsonProcessor.prettyPrint(jsonBody));
String response = ApiClient.patch("/posts/" + id, jsonBody);
System.out.println("\nResponse from server:");
System.out.println(JsonProcessor.prettyPrint(response));
Post resultPost = JsonProcessor.fromJson(response);
System.out.println("\nPartially updated post:");
System.out.println(resultPost);
}
/**
* 6. DELETE a post (Delete operation)
*/
private static void deletePost(int id) throws IOException, InterruptedException {
System.out.println("\n--- Deleting post #" + id + " ---");
// Confirm deletion
System.out.print("Are you sure you want to delete post #" + id + "? (yes/no): ");
Scanner tempScanner = new Scanner(System.in);
String confirmation = tempScanner.nextLine().toLowerCase();
if (!confirmation.equals("yes") && !confirmation.equals("y")) {
System.out.println("Deletion cancelled.");
return;
}
String response = ApiClient.delete("/posts/" + id);
System.out.println("\nResponse from server:");
System.out.println(JsonProcessor.prettyPrint(response));
System.out.println("\nNote: JSONPlaceholder is a fake API, so the post won't be actually deleted.");
System.out.println("But the DELETE operation was successful (status code 200).");
}
}
Step 7: Create Compilation and Run Script
Create compile-and-run.sh in the project root:
#!/bin/bash
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
echo -e "${BLUE}=== Java API Client Compilation Script ===${NC}"
# Check if Java is installed
if ! command -v javac &> /dev/null; then
echo -e "${RED}Error: javac is not installed. Please install Java Development Kit (JDK).${NC}"
exit 1
fi
# Create output directory if it doesn't exist
mkdir -p out
# Compile all Java files with classpath
echo -e "\n${GREEN}Compiling Java files...${NC}"
javac -cp "lib/*" -d out src/*.java
if [ $? -eq 0 ]; then
echo -e "${GREEN}Compilation successful!${NC}"
# Run the application
echo -e "\n${BLUE}Running API Client...${NC}"
echo -e "${BLUE}=========================================${NC}"
java -cp "out:lib/*" com.apiclient.ApiConsumerApp
else
echo -e "${RED}Compilation failed. Please check the errors above.${NC}"
exit 1
fi
Step 8: Running the Application
Now let’s run our complete application:
# Make sure you're in the java-api-client directory ./compile-and-run.sh
Step 9: Testing All CRUD Operations
When you run the application, you’ll see a menu with these options:
1. GET all posts (Read)
- Demonstrates retrieving a collection of resources
- Shows how to parse JSON arrays
2. GET a specific post (Read)
- Shows retrieving a single resource by ID
- Demonstrates error handling for non-existent resources
3. POST a new post (Create)
- Shows how to send data to create a new resource
- Demonstrates JSON serialization
- Example request body:
{
"userId": 1,
"title": "My New Post",
"body": "This is the content of my new post."
}
4. PUT to update a post (Update)
- Shows full resource replacement
- Demonstrates the difference between PUT and PATCH
5. PATCH to partially update (Update)
- Shows partial resource updates
- More efficient than PUT when only changing some fields
6. DELETE a post (Delete)
- Shows resource deletion
- Includes confirmation prompt
Understanding the Code Structure
Key Components:
- Post.java – The model/entity class representing our data
- ApiClient.java – Handles all HTTP communication using Java 11+ HttpClient
- JsonProcessor.java – Manages JSON serialization/deserialization using Gson
- ApiConsumerApp.java – Main application with user interface and business logic
HTTP Methods Explained:
- GET: Retrieve data (safe, idempotent)
- POST: Create new resource (not idempotent)
- PUT: Replace entire resource (idempotent)
- PATCH: Partially update resource (not always idempotent)
- DELETE: Remove resource (idempotent)
Common Issues and Troubleshooting
- Connection Errors: Ensure you have internet access
- Compilation Errors: Verify Java version (need 11+ for HttpClient)
- JSON Parsing Errors: Check Gson library is in lib/ folder
- API Limits: JSONPlaceholder has no rate limits for educational use
Advanced Exercises
- Add Error Handling: Implement retry logic for failed requests
- Add Logging: Use a logging framework like SLF4J
- Add Configuration: Make API URL configurable via properties file
- Add Unit Tests: Test each CRUD operation with JUnit
- Add More Endpoints: Extend to other resources (users, comments, todos)
Conclusion
You’ve successfully built a complete Java command-line application that performs all CRUD operations against a REST API! This project demonstrates:
- Java 11+ HttpClient usage
- JSON processing with Gson
- Clean separation of concerns
- Comprehensive error handling
- Interactive command-line interface
This foundation can be extended to work with any REST API by modifying the model classes and endpoints. The skills learned here are transferable to real-world API integrations.
Final Project Structure Recap:
java-api-client/ ├── lib/gson-2.10.1.jar ├── src/Post.java ├── src/ApiClient.java ├── src/JsonProcessor.java ├── src/ApiConsumerApp.java └── compile-and-run.sh
Happy coding! You can now integrate any REST API into your Java applications.

























Leave a Comment
Your email address will not be published. Required fields are marked with *