Skip to main content

External REST APIs

Overview

The Genesis HTTP Client is designed to simplify integration with external REST services in the Genesis Platform. It provides an intuitive DSL for making HTTP requests, so that you can integrate Request Server and Event Handler with external applications easily and without the need to pull their data into your Genesis application.

The key features and benefits are:

  • Seamless Integration. Designed to work seamlessly with Genesis Request Servers and Event Handlers.
  • Flexible Usage. Supports multiple approaches, including direct client usage, annotation-driven approach, and OpenAPI code generation.
  • Comprehensive HTTP Support. Full support for GET, POST, PUT, DELETE.
  • Customizable. Easily configure headers, query parameters, path parameters, and request bodies.
  • Error Handling. Built-in support for retries and error callbacks.
  • Paginated Support. Pagination support for Request Servers.
important

Ensure your Genesis Platform is updated to at least version 8.1 to use the Genesis HTTP Client.

Basic usage

Here's a quick example of a GET request using the Genesis HTTP Client within a Request Server:

import global.genesis.httpclient.GenesisHttpClient

requestReply<Unit, HelloWorld>("HELLO_WORLD") {
val client = GenesisHttpClient()

replySingle {
client.get<HelloWorld> {
url = "https://my-external-api/hello"
query("name", "John")
}.data
}
}

This example demonstrates how to make a GET request to an external REST API within a Genesis Request Server, showcasing the client's seamless integration with Genesis components. In the following sections, we'll explore the core concepts, different usage approaches, and advanced features of the Genesis HTTP Client.

Example configuration

This section demonstrates practical examples of using the Genesis HTTP Client in common Genesis capabilities Request Server and Event Handler and common use cases.

Example 1: Request Server with external API call

This example shows how to use the Genesis HTTP Client in a Request Server to fetch data from an external API, ensuring errors are handled:

import global.genesis.httpclient.GenesisHttpClient
import global.genesis.httpclient.response.UnexpectedResponseException
import global.genesis.message.core.HttpStatusCode

requestReplies {
requestReply<TradeRequest, TradeDetails>("FETCH_TRADE_DETAILS") {
val client = GenesisHttpClient()

replySingle { request: TradeRequest ->
try {
client.get<TradeDetails> {
url = "https://my-external-api/trades/${request.tradeId}"
}
} catch (e: UnexpectedResponseException) {
when (e.statusCode) {
HttpStatusCode.NotFound -> throw Exception("Trade not found")
else -> throw Exception("Error fetching trade: ${e.message}")
}
}
}
}
}

Example 2: Event Handler with external POST

This example demonstrates how to use the Genesis HTTP Client in an Event Handler to send a post to an external service as part of a TradeInsert event

import global.genesis.httpclient.GenesisHttpClient

eventHandler {
data class TradeNotification(
val tradeId: Long,
val status: String,
val timestamp: Long
)

eventHandler<TradeInsert> {
val client = GenesisHttpClient()

onCommit { event ->
...
val notification = TradeNotification(
tradeId = event.tradeId,
status = event.status,
timestamp = event.timestamp
)

try {
client.post<Unit>(
path = "https://my-external-api/trade-notification",
request = notification,
builder = {
header("Authorization ", "Bearer 123")
}
)
} catch (e: Exception) {
nack("Failed to send notification", e)
}
...
ack()
}
}
}

Example 3: Request Server with OpenAPI-generated client

You can use a type-safe client generated from an OpenAPI spec inside a Request Server. Instantiate the generated API class (from the Genesis OpenAPI Gradle Plugin), register tokens if needed, and call its methods in replySingle or replyList:

val api = MyExternalApi("https://my-external-api")
api.registerApiToken("Authorization", "Basic ABC123=")

requestReplies {
requestReply<Int, Account>("GET_ACCOUNT_BY_ID") {
replySingle {
api.getAccountById(it.toLong())
}
}
}

Example 4: Event Handler with OpenAPI-generated client

Use the same generated API in an Event Handler to call external endpoints (e.g. create or update resources) as part of your event flow:

val api = MyExternalApi("https://my-external-api")

eventHandler {
eventHandler<TradeUpdateEvent> {
onCommit { event ->
val body = NewTrade(symbol = event.symbol, quantity = event.quantity, price = event.price)
try {
api.updateTrade(event.tradeId, body)
ack()
} catch (e: Exception) {
logger.error("Failed to update trade in external system: ${e.message}", e)
nack("Update failed", e)
}
}
}
}

Example 5: Paginated request with OpenAPI-generated client

For list endpoints that support pagination, the OpenAPI plugin can generate a dedicated request type and a single-argument method. Use it in a Request Server with replyList:

val api = AccountControllerApi("https://my-external-api")
api.registerApiToken("Authorization", "Basic ABC123=")

requestReplies {
requestReply<FindAllUsingGETRequest, AccountWithRecordId>("ACCOUNTS_API") {
maxRetries = 5
replyList { request ->
try {
val response = api.findAllUsingGET(request)
response.accounts.map { account ->
AccountWithRecordId(
account.accountNumber,
account.balance,
account.brokerId,
account.customerId,
account.owner
)
}
} catch (e: Exception) {
LOG.warn("Error sending request for accounts to external API", e)
emptyList()
}
}
}
}

Configuration options

GenesisHttpClient

The GenesisHttpClient is the main class you interact with. It provides methods for making HTTP requests.

import global.genesis.httpclient.GenesisHttpClient

val client = GenesisHttpClient()

You can optionally configure a Ktor HttpClient object if needed. An example using Apache5 is provided below:

import global.genesis.httpclient.GenesisHttpClient
import io.ktor.client.HttpClient
import io.ktor.client.engine.apache5.Apache5

val apacheClient = HttpClient(Apache5) {
engine {
followRedirects = true
socketTimeout = 10_000
connectTimeout = 10_000
connectionRequestTimeout = 20_000
}
}

val client = GenesisHttpClient(apacheClient)

You can follow the documented examples on the ktor documentation site as needed, for example configuring SSL if required in your setup.

Request methods

The client supports all standard HTTP methods:

  • GET: client.get<T>()
  • POST: client.post<T>()
  • PUT: client.put<T>()
  • DELETE: client.delete<T>()

Each method above returns a TypedHttpResponse<T> object found in global.genesis.httpclient.response.TypedHttpResponse, where T is the expected response body type.

Configuring requests

You can configure requests using a DSL within a lambda function. Common configuration options include:

  • url: the endpoint URL
  • header: HTTP headers
  • query: query parameters

Example:

import global.genesis.httpclient.GenesisHttpClient

val httpClient = GenesisHttpClient()
...
eventHandler<Account>("API_ACCOUNT_INSERT") {
onCommit {
val data = httpClient.get<String> {
url = "https://my-external-api/accounts"
header("Authorization", "Basic ABCDEFG")
query("accountId", event.details.accountId)
}.data
...
ack()
}
}
...

It is important to highlight that if a payload needs to be provided as part of the HTTP request body (very common for POST requests), then it must be provided as a request parameter with a target path as shown below:

import global.genesis.httpclient.GenesisHttpClient

val client = GenesisHttpClient()
...
eventHandler<Trade>("API_TRADE_INSERT") {
onCommit { event ->
...
client.post<String>(
path = "https://my-external-api/trades",
request = event.details
) {
header("Authorization", "Basic ABCDEFG")
query("tradeId", event.details.tradeId)
}.data
...
ack()
}
}

OpenAPI-generated type-safe clients

For external APIs that expose an OpenAPI 3.x (or Swagger) specification, you can generate a type-safe Kotlin client at build time. The client extends the Genesis HTTP client and provides typed methods for each endpoint, request/response DTOs, and support for authentication and pagination.

Setup and usage: Apply the Genesis OpenAPI Gradle Plugin in your module, point it at your spec file(s), and add the genesis-http-client dependency. The plugin runs before compilation and adds the generated API class and related types to your source set. You then instantiate the generated API (e.g. TradeServiceApi("https://my-external-api")), optionally call registerApiToken or registerRetryCallback, and use it in Request Servers or Event Handlers as in Example 3, Example 4, and Example 5 above.

Full documentation: Configuration options, pagination, generated package layout, and usage examples are described in the Genesis OpenAPI Gradle Plugin section of the Build & Deploy documentation.

Response handling

The TypedHttpResponse<T> object found in global.genesis.httpclient.response.TypedHttpResponse contains:

  • statusCode: the HTTP status code
  • headers: response headers
  • data: the response body, deserialized to type T

Example:

import global.genesis.httpclient.GenesisHttpClient

val client = GenesisHttpClient()
...
eventHandler<Trade>("API_TRADE_INSERT") {
onCommit { event ->
...
client.post<String>(
path = "https://my-external-api/trades",
request = event.details
) {
header("Authorization", "Basic ABCDEFG")
query("tradeId", event.details.tradeId)
}.data
...
ack()
}
}

Client API

There is no specific Client API for the Genesis HTTP Client as it is a client itself. Links are provided below for the common services which use the Genesis HTTP Client:

Runtime configuration

The Genesis HTTP Client was introduced in version 8.1 of the Genesis Platform. It is compatible with all Genesis applications running on version 8.1 or higher.

The Genesis HTTP Client is available to use in Genesis applications running platform version 8.1+.

To use it, add the following dependency to your project's server/<your-app>/build.gradle.kts file:

dependencies {
...
implementation("global.genesis:genesis-http-client")
}

If you would like to configure a different HTTP client engine, additional Ktor dependencies can be added like this:

val ktor_version = "3.2.2" // use the Genesis platform ktor version for best compatibility
dependencies {
...
implementation("io.ktor:ktor-client-apache5:$ktor_version")
}