-
Notifications
You must be signed in to change notification settings - Fork 3
Using Components
Almost any useful API will need to use external components to do its job, for example a data store or a message publisher. The obvious place to put these components so they are available to the handlers might seem to be properties in package containing the API definition. However, there is a reason that this will not work.
An Api is created in two different places. An Api instance is used when the application is running, but an API must also be created when deploying to API Gateway, as the Api instance describes the API that is being deployed.
If the components were top-level properties in the API definition package then components such as database connections and message publishers would be created when deploying from your development machine.
The solution to this problem is provided by the ComponentsProvider interface. When defining an API using the api function, the caller must provide type parameter representing a subtype of ComponentsProvider. At runtime an instance of this class is created and its properties (the components) are available in the handlers. But at deployment time no instance is required as the handlers are not executed.
This is best demonstrated with an example:
// Interface that implements ComponentsProvider and
// exposes the components used by the handlers
interface MyComponents : ComponentsProvider {
val dataStore: DataStore
val messagePublisher: MessagePublisher
}
// Create an API whose handlers can access the
// properties of the MyComponents class
val api = api<MyComponents> {
get("/orders/{orderId}") { req ->
val orderId = req.pathParams["orderId"]
// Use the property MyComponents.dataStore without qualifying it
dataStore.loadOrderDetails(orderId)
}
post("/orders") { req ->
val order = createOrder(req.body)
// Use the properties MyComponents.dataStore and
// MyComponents.messagePublisher without qualification
dataStore.saveOrderDetails(order)
messagePublisher.publish(order)
}
}In technical terms, the components provider is the implicit receiver of the lambda expression that defines a handler.
The components provider must be created by a public top-level function taking no arguments. this function must be called createComponents() and must be in the core package. A function is generated when the project is created (in the file ApiDefinition.kt). This function should be modified to return your ComponentsProvider.
The createComponents() function is invoked when the application is started. The object it returns is passed to the handlers when they are executed.
The components should be created when the createComponents() is invoked. The components should not be created eagerly (for example in top-level properties) and just returned from the createComponents(). This would mean the components would be created on the build machine during deployment.