Skip to main content
Version: Current

Different ways of making requests

The Genesis HTTP Client offers many approaches to making HTTP requests, catering for different use cases and preferences. This section covers the three main approaches:

  1. Direct client usage
  2. Annotation-driven requests
  3. OpenAPI code generation

Direct client usage

This approach provides the most flexibility and control over your HTTP requests, using the GenesisHttpClient directly within Genesis structures.

You can map the response of the call to a known structure, as you can see in the following example TradeResponse:


data class TradeResponse(
val tradeId: Int,
val amount: Double,
val currency: String,
val status: String,
)

requestReply<Unit, TradeResponse>("TRADE_API_CALL") {
val client = GenesisHttpClient()

replySingle {
client.get<TradeResponse> {
url = "https://api.example.com/trades"
header("Authorization", "Bearer token123")
query("tradeId", "123")
}.data
}
}

Annotation-driven requests

You can use annotated data classes to define request parameters. This gives you a more structured way to make requests. For example:


data class SampleRequest(
@QueryParameter
val brokerId: String,
@PathParameter
val tradeId: Int,
@HeaderParameter
val authorization: String
)

requestReply<SampleRequest,Trade>("SAMPLEREQ") {
val client = GenesisHttpClient()

replySingle {
client
.get<Trade>(
path = "http://localhost:8080/trades/{tradeId}",
request = it
).data
}
}

In this particular example, the SampleRequest data class is annotated with @QueryParameter, @PathParameter, and @HeaderParameter to map its properties to the corresponding request parameters.

When made from a Request Server requestReply, the request automatically populates the request parameters based on the annotated properties. For instance, the request

localhost:9064/req_SAMPLEREQ?REQUEST.BROKER_ID=1&REQUEST.TRADE_ID=2&REQUEST.AUTHORIZATION=Basic YWRtaW46YWRtaW4=

will be made with the brokerId, tradeId, and authorization values from the SampleRequest object.

As a result, the external API gets:

GET /trades/2?brokerId=1,
headers=[host:"localhost:8080",
authorization:"Basic YWRtaW46YWRtaW4=",
accept:"application/json",
accept-charset:"UTF-8",
user-agent:"Ktor client"]]`

OpenAPI code generation

This approach uses code generated from an OpenAPI/Swagger specification, providing the simplest and most type-safe way to make API calls.

To use this approach, you need to generate the code from the OpenAPI specification file using the genesis-openapi-codegen tool. The generated code will include API interfaces and data classes that map to the API endpoints and request/response bodies.

You must also include the following set-up in your module's build.gradle.kts file:


plugins {
id("global.genesis.openapi") version "8.2.0" // Genesis Version
}

tasks {
jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
processResources {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
genesisOpenApi {
packageName = "global.genesis.api.trades"
specification = project.layout.projectDirectory.file("src/main/resources/trades.json")
}
}

In the example above, we are generating the code from the trades.json, which is an open-api specification file. The generated code will be placed in the global.genesis.api.trades package.

YOu can generate the code either by running the genesis-openapi-codegen task or by building the project. You can then use the generated code to make API calls.

val tradeApi = TradeServiceApi("http://localhost:8080")
tradeApi.registerApiToken("Authorization", "Basic YWRtaW46YWRtaW4=")
val accountsApi = AccountsControllerApi("http://localhost:8080")
accountsApi.registerRetryCallback(HttpStatusCode.Unauthorized) {
registerApiToken("Authorization", "Basic YWRtaW46YWRtaW4=")
}

The example below can be used inside a Genesis Request Server or Event Handler, for example:

eventHandler<Account>("API_ACCOUNT_EH_INSERT") {
onCommit {
val createdAccount = accountsApi.createAccount(it.details)
ack(listOf(mapOf("ACCOUNT_NUMBER" to createdAccount.accountNumber)))
}
}

Paginated requests with requestReply

Pagination support requires a bit of extra work, but it is possible to integrate with the requestReply function. The following example demonstrates how to make a paginated request using the Genesis HTTP Client:

To generate the proper paginated requests and use paginated requests:

  1. Define a file pagination-config.yaml in your project, with the following content:
paginated_requests:
- operation_id: findAllUsingGET // Your operation id, unique and found in the open-api specification
index_param: pageIndex // The name of the index parameter in the request (page, index, offset, etc)
limit_param: limit // The name of the limit parameter in the request (size, limit, etc)
  1. Add a pagination config to the config block:
genesisOpenApi {
packageName = "global.genesis.api.trades"
specification = project.layout.projectDirectory.file("src/main/resources/trades.json")
paginationConfig = project.layout.projectDirectory.file("src/main/resources/pagination-config.yaml") // path to the pagination config
}

Once you have done this, the Genesis Openapi Codegen plugin generates the proper classes and functions for use in paginated requests.

requestReply<FindAllPaginatedRequest, Account>("ACCOUNTS_API") {
maxRetries = 5
replyList { request ->
try {
accountsApi.findAllPaginated(request).accounts
} catch (e: Exception) {
emptyList()
}
}
}

In this example:

  • FindAllPaginatedRequest is a class generated from the open-api specification that contains the index and limit parameters.
  • accountsApi.findAllPaginated(request).accounts makes the request to the external API and returns the list of accounts.

The requestReply handles the pagination automatically.

In the next section, we shall look at some more examples.