SDK Hooks

SDK Hooks is a feature in early access for our Go, Java, Python & Typescript SDKs that allows you to add hooks into the SDK to add custom logic to the SDK & Request lifecycle. These hooks allow you to hook code into various events within the SDK, allowing you to add things like transformations, tracing, logging, validation, error handling etc.

The current lifecycle events covered are:

  • On SDK Initialization - allowing to modify the base server url or wrap or override the HTTP client used by the SDKs, providing capabilities like adding tracing, injecting global headers, managing authentication etc
  • Before Request - Allowing you to cancel an outgoing request, transform the contents of the request or just add tracing etc
  • After Success - Called when a successful response is received (as defined by your OpenAPI document), allows you to add tracing/logging, validate the response and return an error or even transform the raw response before deserialization by the SDK.
  • After Error - Called on various errors such as connection errors or unsuccessful responses (as defined by your OpenAPI document), allows you to add tracing/logging or transform the returned error.

Adding a Hook

To get started if you have generated a Go, Python or Typescript SDK off our latest version of the Speakeasy CLI, you will now see some new code generated into a hooks folder in the SDK.

  • For Go you will find this in internal/hooks
  • For Python you will find this in src/{sdk_name}/hooks
  • For Typescript you will find this in src/hooks
  • For Java you will find this in src/main/java/{package_path}/hooks

To add your own hooks you will go through this process:

Create a hook implementation in a new file within the hooks folder. Any files added to the SDK repo will be retained and won't be overridden by the generator. Below are example hook files to get you started that implement the 4 hook events (you only need to implement what you use but I have provided examples of all 4 hooks in one class).

To register your new hook your need to reference it and register it with the registration file for each language:

  • For Go this is internal/hooks/registration.go
  • For Python this is src/{sdk_name}/hooks/registration.py
  • For Typescript this is src/hooks/registration.ts
  • For Java this is src/main/java/{package_path}/hooks/SDKHooks.java

Within the registration file you will find a initHooks/init_hooks/initialize method that contains a hooks argument that has registration methods on it. Instantiate your hook in this method (it is called once per SDK instantiation) and register your hook for each event it implements.

This file will never be overwritten, it is generated once and then ownership of it is yours.

If you need to add any new external/3rd party dependencies to the SDK to power logic in your hooks, follow the instructions below.

Example Hooks:


package hooks
import (
"net/http"
)
type ExampleHook struct{}
var (
_ sdkInitHook = (*ExampleHook)(nil)
_ beforeRequestHook = (*ExampleHook)(nil)
_ afterSuccessHook = (*ExampleHook)(nil)
_ afterErrorHook = (*ExampleHook)(nil)
)
func (i *ExampleHook) SDKInit(baseURL string, client HTTPClient) (string, HTTPClient) {
// modify the baseURL or wrap the client used by the SDK here and return the updated values
return baseURL, client
}
func (i *ExampleHook) BeforeRequest(hookCtx BeforeRequestContext, req *http.Request) (*http.Request, error) {
// modify the request object before it is sent, such as adding headers or query parameters or return an error to stop the request from being sent
return req, nil
}
func (i *ExampleHook) AfterSuccess(hookCtx AfterSuccessContext, res *http.Response) (*http.Response, error) {
// modify the response object before deserialization or return an error to stop the response from being deserialized
return res, nil
}
func (i *ExampleHook) AfterError(hookCtx AfterErrorContext, res *http.Response, err error) (*http.Response, error) {
// modify the response before it is deserialized as a custom error or the error object before it is returned or return an error wrapped in the FailEarly error in this package to exit from the hook chain early
return res, err
}

Adding Additional Dependencies

For the languages that support SDK Hooks they also support adding additional dependencies to the SDK. These dependencies are then templated into the SDK and pulled on compilation.

This is done by configuring additionalDependencies in your gen.yaml file, below are various examples for each language on what is possible:

Pass a map of Go package names to the version to be added to the go.mod file of the SDK.


configVersion: 2.0.0
go:
additionalDependencies:
"github.com/google/uuid": v1.6.0