AWS Developer Tools Blog

Introducing Support for Java SDK Generation in Amazon API Gateway

We are excited to announce support for generating a Java SDK for services fronted by Amazon API Gateway. The generated Java SDKs are compatible with Java 8 and later. Generated SDKs have first-class support for API keys, custom or AWS Identity and Access Management (IAM) authentication, automatic and configurable retries, exception handling, and more. In this blog post, we’ll walk through how to create a sample API, and generate a Java SDK from that API, and explore various features of the generated SDK. This post assumes you have some familiarity with API Gateway concepts.

Create an Example API

To start, let’s create a sample API by using the API Gateway console. Navigate to the API Gateway console and select your preferred region. Choose Create API, and then choose the Example API option. Choose Import to create the example API.

create-example-api

The example API is pretty simple. It consists of four operations.

  1. A GET on the API root resource that returns HTML describing the API.
  2. A GET on the /pets resource that returns a list of Pets.
  3. A POST on the /pets resource that creates a new Pet
  4. A GET on the /pets/{petId} resource that returns a specific Pet by ID.

Deploy the API

Next, you’ll deploy the API to a stage.

Under Actions, choose Deploy API, name the stage test, and then choose Deploy.

deploy-example-api

After you deploy the API, on the SDK Generation tab, choose Java as the platform. For Service Name, type PetStore. For Java Package Name, type com.petstore.client. Leave the other fields empty. Choose Generate SDK, and then download and unzip the SDK package.

generate-java-sdk

There are several configuration options available for the Java platform. Before proceeding, let’s go over them.

Service Name – Used to name the Java Interface you’ll use to make calls to your API.

Java Package Name – The name of the package your generated SDK code will be placed under. This name is typically named based on your organization.

The following optional parameters are used when publishing the SDK to a remote repository, like Maven Central.

Java Build System – The build system to configure for the generated SDK, either maven or gradle. The default is maven.

Java Group ID – Typically identifies your organization. Defaults to Java Package Name if not provided.

Java Artifact ID – Identifies the library or product. Defaults to Service Name if not provided.

Java Artifact Version – Version identifier for the published SDK. Defaults to 1.0-SNAPSHOT if not provided.

Compile Client

Navigate to the location where you unzipped the SDK package. If you’ve been following the example, the package will be setup as a Maven project. Ensure Maven and a JDK have been installed correctly, and run the following command to install the client package into your local Maven repository This makes it available for other local projects to use.

mvn install

Set Up an Application

Next, you’ll set up an application that depends on the client package you previously installed. Because the client requires Java 8 or later, any application that depends on the client must also be built with Java 8. Here, you’ll use a simple Maven Archetype to generate an empty Java 8 project.

mvn archetype:generate -B -DarchetypeGroupId=pl.org.miki -DarchetypeArtifactId=java8-quickstart-archetype -DarchetypeVersion=1.0.0 \
    -DgroupId=com.petstore.app \
    -DartifactId=petstore-app \
    -Dversion=1.0 \
    -Dpackage=com.petstore.app

Navigate to the newly created project and open the pom.xml file. Add the following snippet to the <dependencies>…</dependencies section of the XML file. If you changed any of SDK export parameters in the console, use those values instead.

<dependency>
    <groupId>com.petstore.client</groupId>
    <artifactId>PetStore</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

Create a file src/main/java/com/petstore/app/AppMain.java with the following contents.

package com.petstore.app;

import com.petstore.client.*;
import com.petstore.client.model.*;
import com.amazonaws.opensdk.*;
import com.amazonaws.opensdk.config.*;

public class AppMain {

    public static void main(String[] args) {

    }
}

Build the application to ensure everything is configured correctly.

mvn install

To run the application, you can use the following Maven command. (As you make changes, be sure to rerun mvn install before running the application.)

mvn exec:java -Dexec.mainClass="com.petstore.app.AppMain"

Exploring the SDK

Creating the Client

The first thing you need to do is construct an instance of the client. You can use the client builder obtained from a static factory method on the client interface. All configuration methods on the builder are optional (except for authorization related configuration).In the following code, you obtain an instance of the builder, override some of the configuration, and construct a client. The following settings are for demonstration only, and are not necessarily the recommended settings for creating service clients.

PetStore client = PetStore.builder()
        .timeoutConfiguration(new TimeoutConfiguration()
                                      .httpRequestTimeout(20_000)
                                      .totalExecutionTimeout(30_000))
		.connectionConfiguration(new ConnectionConfiguration()
                                      .maxConnections(100)
                                      .connectionMaxIdleMillis(120))
        .build();

The builder exposes a ton of useful configuration methods for timeouts, connection management, proxy settings, custom endpoints, and authorization. Consult the Javadocs for full details on what is configurable.

Making API Calls

Once you’ve built a client, you’re ready to make an API call.

Call the GET /pets API to list the current pets. The following code prints out each pet to STDOUT. For each API in the service, a method is generated on the client interface. That method’s name will be based on a combination of the HTTP method and resource path, although this can be overridden (more on that later in this post).

client.getPets(new GetPetsRequest())
        .getPets()
        .forEach(p -> System.out.printf("Pet: %s\n", p));

The GET /pets operation exposes a query parameter named type that can be used to filter the pets that are returned. You can set modeled query parameters and headers on the request object.

client.getPets(new GetPetsRequest().type("dog"))
        .getPets()
        .forEach(p -> System.out.printf("Dog: %s\n", p));

Let’s try creating a Pet and inspecting the result from the service. Here you call the POST /pets operation, supplying information about the new Pet. The CreatePetResult contains the unmarshalled service response (as modeled in the Method Response) and additional HTTP-level metadata that’s available via the sdkResponseMetadata() method.

final CreatePetResult result = client.createPet(
        new CreatePetRequest().newPet(new NewPet()
                                              .type(PetType.Bird)
                                              .price(123.45)));
System.out.printf("Response message: %s \n", result.getNewPetResponse().getMessage());
System.out.println(result.sdkResponseMetadata().header("Content-Type"));
System.out.println(result.sdkResponseMetadata().requestId());
System.out.println(result.sdkResponseMetadata().httpStatusCode());

The GET /pets/{petId} operation uses a path placeholder to get a specific Pet, identified by its ID. When making a call with the SDK, all you need to do is supply the ID. The SDK handles the rest.

GetPetResult pet = client.getPet(new GetPetRequest().petId("1"));
System.out.printf("Pet by ID: %s\n", pet);

Overriding Configuration at the Request Level

In addition to the client-level configuration you supply when creating the client (by using the client builder), you can also override certain configurations at the request level. This “request config” is scoped only to calls made with that request object, and takes precedence over any configuration in the client.

client.getPets(new GetPetsRequest()
                       .sdkRequestConfig(SdkRequestConfig.builder()
                                                 .httpRequestTimeout(1000).build()))
        .getPets()
        .forEach(p -> System.out.printf("Pet: %s\n", p));

You can also set custom headers or query parameters via the request config. This is useful for adding headers or query parameters that are not modeled by your API. The parameters are scoped to calls made with that request object.

client.getPets(new GetPetsRequest()
                       .sdkRequestConfig(SdkRequestConfig.builder()
                                                 .customHeader("x-my-custom-header", "foo")
                                                 .customQueryParam("MyCustomQueryParam", "bar")
                                                 .build()))
        .getPets()
        .forEach(p -> System.out.printf("Pet: %s\n", p));

Naming Operations

It’s possible to override the default names given to operations through the API Gateway console or during an import from a Swagger file. Let’s rename the GetPet operation (GET /pets/{petId}) to GetPetById by using the console. First, navigate to the GET method on the /pets/{petId} resource.

change-operation-name

Choose Method Request, and then expand the SDK Settings section.

sdk-settings

Edit the Operation Name field and enter GetPetById. Save the change and deploy the API to the stage you created previously. Regenerate a Java SDK, and it should have the updated naming for that operation.

GetPetByIdResult pet = client.getPetById(new GetPetByIdRequest().petId("1"));
System.out.printf("Pet by ID: %s\n", pet);

If you are importing an API from a Swagger file, you can customize the operation name by using the operationId field. The following snippet is from the example API, and shows how the operationId field is used.

...
    "/pets/{petId}": {
      "get": {
        "tags": [
          "pets"
        ],
        "summary": "Info for a specific pet",
        "operationId": "GetPet",
        "produces": [
          "application/json"
        ],
...

Final Thoughts

This post highlights how to generate the Java SDK of an API in API Gateway, and how to call the API using the SDK in an application. For more information about how to build the SDK package, initiate a client with other configuration properties, make raw requests, configure authorization, handle exceptions, and configure retry behavior, see the README.html file in the uncompressed SDK project folder.