# Migrating from `RestTemplate` `RestTemplate` is permitted only inside `…httpclient.migration`; `RestTemplateBoundaryTest` enforces that. New retry, Dynamic Target, and HTTP/3 capabilities are deliberately unreachable from the migration path — a caller that wants them moves to a Named Client Profile. ## 1. Audit before changing anything ```java RestTemplateInventory inventory = new RestTemplateInventoryScanner().scan(existingTemplate); ``` The inventory reports the request factory, message converters, interceptors, error handler, and URI template handler, plus findings: | Code | Severity | Meaning | |---|---|---| | `SIMPLE_REQUEST_FACTORY` | blocking | no connection pool; unsupported in production | | `NO_MESSAGE_CONVERTERS` | blocking | the template cannot encode or decode a body | | `NO_INTERCEPTORS` | warning | confirm where correlation and timeouts are applied | | `TIMEOUTS_NOT_INTROSPECTABLE` | informational | declare timeouts explicitly on the target profile | ## 2. Bridge without changing behaviour ```java RestClient client = new RestTemplateToRestClientAdapter().adaptChecked(existingTemplate); ``` `adaptChecked` refuses to migrate a template with a blocking finding. The bridge carries the existing converters, interceptors, error handler, and URI handler across, so this step changes the API and nothing else. ## 3. Move to a Named Client Profile Turn the platform on with `APP_HTTPCLIENT_ENABLED=true` — it ships off, and while it is off none of the settings below are bound — then declare the upstream as `app.httpclient.clients[N]` with its `name` and an explicit base URL, transport, timeouts, pool, body limits, authentication, retry policy, redirect policy, and TLS profile. Startup validation will tell you exactly which of those is missing. See `docs/httpclient/configuration-reference.md` for the environment form. ## 4. Move to a typed client ```java @HttpClientProfile("payment") @HttpExchange("/payments") public interface PaymentClient { @PostExchange @HttpOperationPolicy( name = "create-payment", idempotency = OperationIdempotency.IDEMPOTENCY_KEY_REQUIRED, retryPolicy = "payment-write") PaymentResponse create( @RequestHeader("Idempotency-Key") String idempotencyKey, @RequestBody PaymentRequest request); } ``` The interface fails startup validation unless it declares a profile, gives every method a stable operation name and an explicit idempotency, supplies a key parameter when the operation requires one, keeps a single execution model, and does not enable retry on a non-idempotent write. ## 5. Retire the template Once no production package references `RestTemplate`, `RestTemplateBoundaryTest` keeps it that way.