http4k-connect is a set of lightweight API libraries for connecting to popular third-party cloud services and AI
backends using http4k compatible APIs, along with Fake implementations for usage during local
testing. These are all underpinned by a variation on the
uniform Server as a Function model powered by the HttpHandler
interface
exposed by http4k, so you can:
- Take advantage of the simple and powerful SaaF model and APIs used in http4k.
- Plug everything together completely in-memory and take advantage of this powerful model.
- Have access to the underlying HTTP clients (and hence add metrics or logging).
- Run stateful Fake implementations of 3rd party systems locally or in test environments.
Although centered around usage in http4k-based projects, http4k-connect does not require this and the libraries are usable from any JVM application.
Although convenient, many client libraries introduce many heavyweight dependencies or contain a plethora of non-required functionality, which can have a large effect on binary size. As an alternative, http4k-connect provides lightweight versions of popular APIs covering standard use-cases.
Each system client is modelled as a single function with arity 1 (that is it takes only a single parameter) returning a Result4k Success/Failure monad type), which is known as an Action
. The Client is responsible for managing the overall protocol with the remote system. There are also a set of extension methods generated to provide a more traditional function-based version of the same interface.
Action classes are responsible for constructing the HTTP requests and unmarshalling their responses into the http4k-connect types. There are lots of common actions built-in, but you can provide your own by simply implementing the relevant Action interface. The recommended pattern in http4k-connect is to use a Result monad type (we use Result4k) to represent the result type, but you can use anything to suit your programming model.
// Generic system interface
interface Example {
operator fun <R : Any> invoke(request: ExampleAction<R>): Result<R, RemoteFailure>
}
// System-specific action
interface ExampleAction<R> : Action<Result<R, RemoteFailure>>
// Action and response classes
data class Echo(val value: String) : ExampleAction<Echoed>
data class Echoed(val value: String)
// Traditional function helpers
fun Example.echo(value: String): Result<Echoed, RemoteFailure> = this(Echo(value))
// constructing and using the clients
val example = Example.Http(httpHandler)
val echoed: Result<Echoed, RemoteFailure> = example.echo("hello world")
// or...
val alsoEchoed: Result<Echoed, RemoteFailure> = example(Echo("hello world"))
Each module comes with it's own Fake system which implements the remote HTTP interface. In like with the Server as a Function
concept, this Fake class implements HttpHandler
and:
- Can be used in in-memory tests as a swap-out replacement for an HTTP client
- Can be started and bound to a HTTP port - each Fake has it's own unique port
- Can be deployed into test environments as a replacement for the real thing.
- Can be used to simulate Chaotic behaviour using the built in OpenApi interface (see http://localhost:/chaos)
Start the Fake with:
FakeExample().start()
> Started FakeExample on 22375
dependencies {
// install the platform...
implementation platform("org.http4k:http4k-connect-bom:5.20.0.0")
// ...then choose a client
implementation "org.http4k:http4k-connect-amazon-s3"
// ...a fake for testing
testImplementation "org.http4k:http4k-connect-amazon-s3-fake"
// ...and a storage backend (optional)
testImplementation "org.http4k:http4k-connect-storage-redis"
}
- AWS
- AppRunner ->
"org.http4k:http4k-connect-amazon-apprunner"
/"org.http4k:http4k-connect-amazon-apprunner-fake"
- CloudFront ->
"org.http4k:http4k-connect-amazon-cloudfront"
/"org.http4k:http4k-connect-amazon-cloudfront-fake"
- CloudWatchLogs ->
"org.http4k:http4k-connect-amazon-cloudwatchlogs"
/"org.http4k:http4k-connect-amazon-cloudwatchlogs-fake"
- DynamoDb ->
"org.http4k:http4k-connect-amazon-dynamodb"
/"org.http4k:http4k-connect-amazon-dynamodb-fake"
- EventBridge ->
"org.http4k:http4k-connect-amazon-eventbridge"
/"org.http4k:http4k-connect-amazon-eventbridge-fake"
- Evidently ->
"org.http4k:http4k-connect-amazon-evidently"
/"org.http4k:http4k-connect-amazon-evidently-fake"
- Firehose ->
"org.http4k:http4k-connect-amazon-firehose"
/"org.http4k:http4k-connect-amazon-firehose-fake"
- IAM Identity Center ->
"org.http4k:http4k-connect-amazon-iamidentitycenter"
/"org.http4k:http4k-connect-amazon-iamidentitycenter-fake"
- InstanceMetadataService ->
"org.http4k:http4k-connect-amazon-instancemetadata"
/"org.http4k:http4k-connect-amazon-instancemetadata-fake"
- KMS ->
"org.http4k:http4k-connect-amazon-kms"
/"org.http4k:http4k-connect-amazon-kms-fake"
- Lambda ->
"org.http4k:http4k-connect-amazon-lambda"
/"org.http4k:http4k-connect-amazon-lambda-fake"
- S3 ->
"org.http4k:http4k-connect-amazon-s3"
/"org.http4k:http4k-connect-amazon-s3-fake"
- SecretsManager ->
"org.http4k:http4k-connect-amazon-secretsmanager"
/"org.http4k:http4k-connect-amazon-secretsmanager-fake"
- SES ->
"org.http4k:http4k-connect-amazon-ses"
/"org.http4k:http4k-connect-amazon-ses-fake"
- SNS ->
"org.http4k:http4k-connect-amazon-sns"
/"org.http4k:http4k-connect-amazon-sns-fake"
- SQS ->
"org.http4k:http4k-connect-amazon-sqs"
/"org.http4k:http4k-connect-amazon-sqs-fake"
- STS ->
"org.http4k:http4k-connect-amazon-sts"
/"org.http4k:http4k-connect-amazon-sts-fake"
- SystemsManager ->
"org.http4k:http4k-connect-amazon-systemsmanager"
/"org.http4k:http4k-connect-amazon-systemsmanager-fake"
- AppRunner ->
- GitHub V3, App, Callback ->
"org.http4k:http4k-connect-github"
- Google Analytics
- Kafka
- Rest Proxy ->
"org.http4k:http4k-connect-kafka-rest"
/"org.http4k:http4k-connect-kafka-rest-fake"
- Schema Registry ->
"org.http4k:http4k-connect-kafka-schemaregistry"
/"org.http4k:http4k-connect-kafka-schemaregistry-fake"
- Rest Proxy ->
- Mattermost Webhook ->
"org.http4k:http4k-connect-mattermost"
/"org.http4k:http4k-connect-mattermost-fake"
- AI:
- LangChain4J ->
"org.http4k:http4k-connect-ai-langchain"
- LmStudio ->
"org.http4k:http4k-connect-ai-lmstudio"
/"org.http4k:http4k-connect-ai-lmstudio-fake"
- OpenAI ->
"org.http4k:http4k-connect-ai-openai"
/"org.http4k:http4k-connect-ai-openai-plugin"
/"org.http4k:http4k-connect-ai-openai-fake"
- Ollama ->
"org.http4k:http4k-connect-ai-ollama"
/"org.http4k:http4k-connect-ai-ollama-fake"
- LangChain4J ->
- Example Template ->
"org.http4k:http4k-connect-example"
/"org.http4k:http4k-connect-example-fake"
- In-Memory (included with all Fakes)
- File-Based (included with all Fakes)
- JDBC ->
org.http4k:http4k-connect-storage-jdbc
- Redis ->
org.http4k:http4k-connect-storage-redis
- S3 ->
org.http4k:http4k-connect-storage-s3
It is very easy to implement your own adapters to follow the pattern. For the system MySystem
, you would need to:
- Depend on the
http4k-connect-core
artifact - Add an Action interface and implementation:
interface MySystemAction<R> : Action<R>
data class Echo(val value: String) : MySystemAction<Echoed> {
override fun toRequest() = Request(GET, "echo").body(value)
override fun toResult(response: Response) = Echoed(response.bodyString())
}
data class Echoed(val value: String)
- Add your adapter interface and HTTP implementation:
interface MySystem {
operator fun <R : Any> invoke(action: MySystemAction<R>): R
companion object
}
fun MySystem.Companion.Http(http: HttpHandler) = object : MySystem {
override fun <R : Any> invoke(action: MySystemAction<R>) = action.toResult(http(action.toRequest()))
}