Skip to main content

REST Proxy Basics

One of the more powerful features of the REST client class is the ability to produce Java interface proxies against arbitrary 3rd party REST resources.

The methods to retrieve remote interfaces are:

RestClientgetRemote(Class)getRemote(Class,Object)getRemote(Class,Object,Serializer,Parser)

Annotations are used on the interface and interface methods to specify how to convert input and output to HTTP headers, query parameters, form post parameters, or request/response bodies:

Remote - Applied to interface class.RemoteOp - Applied to interface methods.ContentHeaderFormDataQueryPathRequestResponse
Example
@Remote(path="/petstore")
public interface PetStore {

@RemotePost("/pets")
Pet addPet(
@Content CreatePet createPet,
@Header("E-Tag") UUID etag,
@Query("debug") boolean debug
);
}
// Use a RestClient with default JSON 5 support.
RestClient client = RestClient.create().json5().build();

// Instantiate our proxy interface.
PetStore store = client.getRemote(PetStore.class, "http://localhost:10000");

// Use it to create a pet.
CreatePet createPet = new CreatePet("Fluffy", 9.99);
Pet pet = store.addPet(createPet, UUID.randomUUID(), true);

The call above translates to the following REST call:

POST http://localhost:10000/petstore/pets?debug=true HTTP/1.1
Accept: application/json
Content-Type: application/json
E-Tag: 475588d4-0b27-4f56-9296-cc683251d314

{
name: 'Fluffy',
price: 9.99
}

The @RemoteOp annotations can be eliminated if you use specific naming conventions on your method names to identify the HTTP method and path.

Example
@Remote(path="/petstore")
public interface PetStore {

// @RemoteOp optional since method and path is inferred from method name.
String postPets(@Content CreatePet pet);
}

Default Values

As of Juneau 9.2.0, you can specify default values for method parameters using the def attribute. Defaults can be specified either at the method level (on the method itself) or at the parameter level (on individual parameters). Parameter-level defaults take precedence when both are present.

Basic Usage - Parameter-Level Defaults

The most straightforward approach is to specify defaults directly on the parameters:

Example
@Remote(path="/petstore")
public interface PetStore {

@RemoteGet("/pets")
Pet[] getPets(
@Header(name="Accept-Language", def="en-US") String language,
@Query(name="limit", def="10") Integer limit
);
}
PetStore store = client.getRemote(PetStore.class, "http://localhost:10000");

// Uses default language="en-US" and limit=10
Pet[] pets1 = store.getPets(null, null);

// Uses custom language, default limit=10
Pet[] pets2 = store.getPets("fr-FR", null);

// Uses default language="en-US", custom limit
Pet[] pets3 = store.getPets(null, 25);

The above examples translate to the following REST calls:

GET http://localhost:10000/petstore/pets?limit=10 HTTP/1.1
Accept-Language: en-US

GET http://localhost:10000/petstore/pets?limit=10 HTTP/1.1
Accept-Language: fr-FR

GET http://localhost:10000/petstore/pets?limit=25 HTTP/1.1
Accept-Language: en-US

Supported Annotations

Default values are supported on the following annotations:

  • @Header(name="...", def="...") - HTTP request headers
  • @Query(name="...", def="...") - Query string parameters
  • @FormData(name="...", def="...") - Form post parameters
  • @Path(name="...", def="...") - Path variables
  • @Content(def="...") - Request body (new in 9.2.0)

Method-Level Defaults (Alternative Approach)

You can also specify defaults at the method level, which can be useful for interface-level configuration:

Example
@Remote(path="/api")
public interface MyApi {

@RemotePost("/resource")
@Header(name="X-API-Key", def="default-key")
@Header(name="X-Client-Version", def="1.0")
@Query(name="format", def="json")
@Content(def="{}")
String createResource(
@Header("X-API-Key") String apiKey,
@Header("X-Client-Version") String clientVersion,
@Query("format") String format,
@Content String data
);
}
// All parameters null - all defaults applied
String result = api.createResource(null, null, null, null);
// POST /api/resource?format=json
// X-API-Key: default-key
// X-Client-Version: 1.0
// Content: {}

// Mix of provided and null values
String result = api.createResource("my-key", null, "xml", "{data:true}");
// POST /api/resource?format=xml
// X-API-Key: my-key
// X-Client-Version: 1.0
// Content: {data:true}

Precedence: Parameter vs. Method Level

When defaults are specified at both the parameter and method level, the parameter-level default takes precedence:

Example
@Remote(path="/api")
public interface MyApi {

@RemoteGet("/data")
@Query(name="format", def="xml") // Method-level default
String getData(
@Query(name="format", def="json") String format // Parameter-level default (takes precedence)
);
}
// Uses parameter-level default: format=json
String result = api.getData(null);

This precedence allows you to:

  • Define common defaults at the method level for multiple parameters
  • Override specific parameters with more specific defaults at the parameter level
  • Keep your interface clean by placing defaults where they're most relevant

Content Body Defaults

The @Content annotation now supports a def attribute for specifying a default request body:

Example
@Remote(path="/petstore")
public interface PetStore {

@RemotePost("/pets")
@Content(def="{name:'Unknown',price:0}")
Pet addPet(@Content CreatePet pet);
}
// When pet is null, sends default JSON
Pet result = store.addPet(null);
// POST /petstore/pets
// Content-Type: application/json
// Content: {name:'Unknown',price:0}

Use Cases

Default values are particularly useful for:

  1. API Keys and Authentication: Provide default credentials that can be overridden per call
  2. Versioning: Specify default API versions
  3. Pagination: Set default page sizes and limits
  4. Content Negotiation: Specify default content types and languages
  5. Feature Flags: Enable/disable features with default values
note

Default values are only applied when the parameter value is null. Empty strings, zero values, and empty collections are considered valid values and will not trigger the default.

Discussion

Share feedback or follow-up questions for this page directly through GitHub.