Three Ways to Call External APIs in Spring Boot: HttpClient, RestTemplate, and Feign

This article explains how to invoke external services from a Spring Boot application using three approaches—raw HttpClient, Spring's RestTemplate, and Feign—providing code examples, configuration steps, and tips for handling headers and tokens.

Architect's Guide
Architect's Guide
Architect's Guide
Three Ways to Call External APIs in Spring Boot: HttpClient, RestTemplate, and Feign

Overview

Spring Boot provides several ways to call external HTTP services without using Dubbo. The three most common approaches are:

Direct use of Apache HttpClient for full control.

Spring RestTemplate for a balance of simplicity and flexibility.

OpenFeign for declarative, type‑safe HTTP clients with built‑in support for interceptors and header injection.

1. Apache HttpClient (raw)

Use the Apache HttpClient library to construct and execute a POST request manually. This gives the highest degree of control over request creation, header handling and response processing.

/**
 * Controller method that prepares the request payload and delegates to the HTTP client.
 */
@RequestMapping("/submit/{documentId}")
public String submit1(@PathVariable String documentId) throws ParseException {
    Map<String, Object> map = task2Service.getMap(documentId);
    String jsonStr = JSON.toJSONString(map, SerializerFeature.WRITE_MAP_NULL_FEATURES,
            SerializerFeature.QuoteFieldNames);
    JSONObject jsonObject = JSON.parseObject(jsonStr);
    JSONObject sr = task2Service.doPost(jsonObject);
    return sr.toString();
}

/**
 * Sends a POST request to an external endpoint using Apache HttpClient.
 */
public static JSONObject doPost(JSONObject body) {
    String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9..."; // JWT token (truncated)
    String url = "http://39.103.201.110:30661/xdap-open/open/process/v1/submit";
    try (CloseableHttpClient client = HttpClients.createDefault()) {
        HttpPost post = new HttpPost(url);
        StringEntity entity = new StringEntity(body.toString(), "UTF-8");
        entity.setContentType("application/json");
        post.setEntity(entity);
        post.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token);
        HttpResponse response = client.execute(post);
        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            String result = EntityUtils.toString(response.getEntity());
            return JSONObject.parseObject(result);
        }
        throw new RuntimeException("Request failed with status " +
                response.getStatusLine().getStatusCode());
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

2. RestTemplate

Spring’s RestTemplate abstracts the low‑level client details while still allowing fine‑grained control over headers, request bodies and URI variables.

GET requests

Two common overloads of getForEntity:

RestTemplate restTemplate = new RestTemplate();
// Using a UriComponentsBuilder to encode parameters
UriComponents uriComponents = UriComponentsBuilder
        .fromUriString("http://USER-SERVICE/user?name={name}")
        .build()
        .expand("dodo")
        .encode();
URI uri = uriComponents.toUri();
ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class);
String body = response.getBody();

Or passing a map of variables directly:

RestTemplate restTemplate = new RestTemplate();
Map<String, Object> vars = new HashMap<>();
vars.put("name", "dada");
ResponseEntity<String> response = restTemplate.getForEntity(
        "http://USER-SERVICE/user?name={name}",
        String.class,
        vars);
getForObject

works similarly but returns the response body directly.

POST requests

Typical usage of postForEntity with a JSON payload and custom headers:

@PostMapping("/submit2")
public Object insertFinanceCompensation(@RequestBody JSONObject json) {
    String documentId = json.getString("documentId");
    return task2Service.submit(documentId);
}

public String submit(String documentId) {
    String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9..."; // JWT token (truncated)
    RestTemplate restTemplate = new RestTemplate();
    HttpHeaders headers = new HttpHeaders();
    headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + token);
    headers.setContentType(MediaType.APPLICATION_JSON);
    Map<String, Object> payload = getMap(documentId);
    HttpEntity<Map<String, Object>> request = new HttpEntity<>(payload, headers);
    String url = "http://39.103.201.110:30661/xdap-open/open/process/v1/submit";
    ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);
    return response.getBody();
}

3. OpenFeign

Feign provides a declarative way to define HTTP clients as Java interfaces. Add the starter dependency, enable scanning, and optionally configure a request interceptor to inject tokens.

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    <version>1.2.2.RELEASE</version>
</dependency>

Enable Feign in the main application class:

@SpringBootApplication
@EnableFeignClients
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Define a Feign client interface for the external service:

@FeignClient(name = "service2", url = "${outSide.url}")
public interface FeignService2 {
    @PostMapping("/custom/outSide")
    String getMessage(@Valid @RequestBody TestDto testDto);
}

Inject the client into a controller and invoke it:

@RestController
public class DemoController {
    @Autowired
    private FeignService2 feignService2;

    @PostMapping("/test2")
    public String test2(@RequestBody TestDto dto) {
        return feignService2.getMessage(dto);
    }
}

To add a token (or any header) to every Feign request, implement RequestInterceptor and reference it in the client configuration.

@Configuration
public class FeignConfig implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        template.header("token", "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9..."); // JWT token (truncated)
    }
}

Use the configuration on a client that requires the token:

@FeignClient(name = "feignServer", url = "${outSide.url}", configuration = FeignConfig.class)
public interface TokenDemoClient {
    @PostMapping("/custom/outSideAddToken")
    String getMessage(@Valid @RequestBody TestDto testDto);
}

@RestController
public class TokenDemoController {
    @Autowired
    private TokenDemoClient tokenDemoClient;

    @PostMapping("/testToken")
    public String testToken(@RequestBody TestDto dto) {
        return tokenDemoClient.getMessage(dto);
    }
}

Choosing the right approach

• Apache HttpClient – use when you need complete control over the HTTP request lifecycle, custom connection management, or non‑standard authentication flows.

• RestTemplate – suitable for most typical GET/POST scenarios where you want a simple, programmatic API with easy header and body handling.

• OpenFeign – ideal for declarative clients, especially when you want to share interfaces across services, automatically map JSON to POJOs, and inject cross‑cutting concerns (e.g., authentication tokens) via interceptors.

backendJavafeignSpringBootresttemplateHttpClient
Architect's Guide
Written by

Architect's Guide

Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.