Şuayb's BlogŞuayb's Blog
Home
Categories
Games
MediumAboutContact
Language
Theme
    1. Blog
    2. Programming
    3. Spring Boot HTTP Interface Client

Spring Boot HTTP Interface Client

PublishedApril 27, 2025
UpdatedApril 28, 2025
Reading time3 min read
JavaKotlinSpring BootREST APIRest Client
XLinkedInFacebook
Spring Boot HTTP Interface Client

Loading likes...

Spring Boot 3.2+ introduces the HTTP Interface Client, enabling you to define declarative, type-safe HTTP clients with simple Java or Kotlin interfaces. In this article, you’ll learn to set it up from scratch using the JSONPlaceholder API.


Last updatedApril 28, 2025

Total viewsLoading hits...

Previous articleSpring Boot Async Tasks with Virtual ThreadNext articleSpring Boot JWE Authentication
Şuayb Şimşek

Written by

Şuayb Şimşek

Backend-focused fullstack developer sharing practical notes on Spring Boot, security, microservices, and cloud-native architecture.

Expertise

  • Spring Boot
  • Go
  • Microservices
  • Next.js
  • Cloud Native

Connect

GitHubLinkedInMedium

Related posts

Building a REST API with Spring Boot
Programming

Building a REST API with Spring Boot

Learn how to create a REST API using Spring Boot. Includes HTTP methods, JSON handling, Lombok integration, and curl for testing.

December 11, 20243 min read
JavaKotlinSpring BootREST API
Spring Boot Configuration Properties
Programming

Spring Boot Configuration Properties

Learn how to use @ConfigurationProperties for type-safe configuration, validate settings with @Validated, and manage environment-specific values with profile-specific application-{profile}.yml files.

February 4, 20263 min read
JavaKotlinSpring BootConfiguration
Spring Boot GraphQL JWE Authentication
Programming

Spring Boot GraphQL JWE Authentication

Learn how to secure your Spring Boot GraphQL APIs with stateless encrypted JWTs (JWE) while persisting user identities and roles in a JPA-backed database.

May 17, 20256 min read
JavaKotlinSpring BootSecurityJWTJWEGraphQL

About

Articles on Spring Boot, microservices, security, and more.

ContactStart here

Latest posts

  • Captain Tsubasa 2: World Fighters
  • Captain Tsubasa: Rise of New Champions
  • Spring Boot Configuration Properties
  • Spring Boot GraphQL JWE Authentication
  • Spring Boot JWE Authentication with JPA

Top topics

JavaKotlinSpring BootJWEJWTMicroservice

Subscribe

Get practical backend + fullstack notes when new articles are published.

Social

© 2024-2026 Şuayb's Blog. All rights reserved.

🌟 Why HTTP Interface Client?

In this section, we clarify Why HTTP Interface Client? and summarize the key points you will apply in implementation.

  • Declarative: Define an interface and annotate methods for HTTP calls.
  • Type-safe: Compiler catches mismatched signatures or misconfigured paths.
  • Minimal boilerplate: One bean definition, no manual proxy or template code.
  • Spring-friendly: Leverages Spring Framework 6.2’s @HttpExchange, @GetExchange, and WebClientAdapter.

📋 Prerequisites

In this section, we clarify Prerequisites and summarize the key points you will apply in implementation.

  • ☕ Java Development Kit (JDK) 21 or higher
  • 📦 Spring Boot 3.2+
  • 🔤 IDE (IntelliJ IDEA, Eclipse, etc.)

🛠️ Step 1: Add Dependencies

Add the Web starter to your build:

Maven:

XMLpom.xml
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Gradle:

GROOVYbuild.gradle
implementation 'org.springframework.boot:spring-boot-starter-web'

🛠️ Step 2: Configure Base URL

Add to application.yml or application.properties:

YAMLconfig.yml
jsonplaceholder:
  base-url: https://jsonplaceholder.typicode.com
PROPERTIESconfig.properties
jsonplaceholder.base-url=https://jsonplaceholder.typicode.com

🛠️ Step 3: Define DTO and Client Interface


🛠️ Step 4: Configure the Client Bean


🛠️ Step 5: Implement Service and Controller


▶️ Run the Application

BASH
./mvnw spring-boot:run
# or
gradle bootRun

🧪 Test Endpoints

BASH
curl http://localhost:8080/posts
curl http://localhost:8080/posts/1

🏁 Conclusion

You now have a practical Spring Boot HTTP Interface Client implementation with a clear, production-friendly Spring Boot structure. As a next step, adapt configuration and tests to your own domain, then validate behavior under realistic traffic and failure scenarios.

JAVAPostDTO.java
// src/main/java/com/example/client/dto/PostDTO.java
package com.example.client.dto;

public record PostDTO(
    Integer userId,
    Integer id,
    String title,
    String body
) {}

// src/main/java/com/example/client/JsonPlaceholderClient.java
package com.example.client;

import com.example.client.dto.PostDTO;
import org.springframework.web.service.annotation.GetExchange;
import org.springframework.web.service.annotation.HttpExchange;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.List;

@HttpExchange(url = "${jsonplaceholder.base-url}", accept = "application/json")
public interface JsonPlaceholderClient {

    @GetExchange("/posts")
    List<PostDTO> getPosts();

    @GetExchange("/posts/{id}")
    PostDTO getPost(@PathVariable("id") Integer id);
}
KOTLINPostDTO.kt
// src/main/kotlin/com/example/client/dto/PostDTO.kt
package com.example.client.dto

data class PostDTO(
    val userId: Int,
    val id: Int,
    val title: String,
    val body: String
)

// src/main/kotlin/com/example/client/JsonPlaceholderClient.kt
package com.example.client

import com.example.client.dto.PostDTO
import org.springframework.web.service.annotation.GetExchange
import org.springframework.web.service.annotation.HttpExchange
import org.springframework.web.bind.annotation.PathVariable

@HttpExchange(url = "${jsonplaceholder.base-url}", accept = "application/json")
interface JsonPlaceholderClient {

    @GetExchange("/posts")
    fun getPosts(): List<PostDTO>

    @GetExchange("/posts/{id}")
    fun getPost(@PathVariable("id") id: Int): PostDTO
}
JAVAHttpClientConfig.java
// src/main/java/com/example/config/HttpClientConfig.java
package com.example.config;

import com.example.client.JsonPlaceholderClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.support.RestClientAdapter;
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
import org.springframework.web.service.invoker.RestClient;

@Configuration
public class HttpClientConfig {

    @Bean
    public JsonPlaceholderClient jsonPlaceholderClient(RestClient.Builder restClientBuilder) {
        RestClient restClient = restClientBuilder
            .baseUrl("https://jsonplaceholder.typicode.com")
            .build();

        var factory = HttpServiceProxyFactory
            .builderFor(RestClientAdapter.create(restClient))
            .build();

        return factory.createClient(JsonPlaceholderClient.class);
    }
}
KOTLINHttpClientConfig.kt
// src/main/kotlin/com/example/config/HttpClientConfig.kt
package com.example.config

import com.example.client.JsonPlaceholderClient
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.client.support.RestClientAdapter
import org.springframework.web.service.invoker.HttpServiceProxyFactory
import org.springframework.web.service.invoker.RestClient

@Configuration
class HttpClientConfig {

    @Bean
    fun jsonPlaceholderClient(restClientBuilder: RestClient.Builder): JsonPlaceholderClient {
        val restClient = restClientBuilder
            .baseUrl("https://jsonplaceholder.typicode.com")
            .build()

        val factory = HttpServiceProxyFactory
            .builderFor(RestClientAdapter.create(restClient))
            .build()

        return factory.createClient(JsonPlaceholderClient::class.java)
    }
}
JAVAPostService.java
// src/main/java/com/example/service/PostService.java
package com.example.service;

import com.example.client.JsonPlaceholderClient;
import com.example.client.dto.PostDTO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
@RequiredArgsConstructor
public class PostService {
    private final JsonPlaceholderClient client;

    public List<PostDTO> getAllPosts() {
        return client.getPosts();
    }

    public PostDTO getPostById(Integer id) {
        return client.getPost(id);
    }
}

// src/main/java/com/example/controller/PostController.java
package com.example.controller;

import com.example.client.dto.PostDTO;
import com.example.service.PostService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
@RequestMapping("/posts")
@RequiredArgsConstructor
public class PostController {
    private final PostService postService;

    @GetMapping
    public List<PostDTO> getAllPosts() {
        return postService.getAllPosts();
    }

    @GetMapping("/{id}")
    public ResponseEntity<PostDTO> getPostById(@PathVariable Integer id) {
        PostDTO post = postService.getPostById(id);
        return post != null ? ResponseEntity.ok(post) : ResponseEntity.notFound().build();
    }
}
KOTLINPostService.kt
// src/main/kotlin/com/example/service/PostService.kt
package com.example.service

import com.example.client.JsonPlaceholderClient
import com.example.client.dto.PostDTO
import org.springframework.stereotype.Service

@Service
class PostService(private val client: JsonPlaceholderClient) {
    fun getAllPosts(): List<PostDTO> = client.getPosts()
    fun getPostById(id: Int): PostDTO = client.getPost(id)
}

// src/main/kotlin/com/example/controller/PostController.kt
package com.example.controller

import com.example.client.dto.PostDTO
import com.example.service.PostService
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/posts")
class PostController(private val postService: PostService) {
    @GetMapping
    fun getAllPosts(): List<PostDTO> = postService.getAllPosts()

    @GetMapping("/{id}")
    fun getPostById(@PathVariable id: Int): ResponseEntity<PostDTO> =
        ResponseEntity.ok(postService.getPostById(id))
}