Hooks
Hooks are a way to add custom code to your SDK to hook into the API invocation lifecycle. This document will guide you through extending the liblab generated SDK with your custom logic using our Hooks framework.
What are Hooks?
In some use cases, you need to extend the SDK created by liblab, and add your own custom code into the SDK at the generation process. Examples of such use cases include custom authentication or auditing requirements.
liblab can extend your SDKs in each of the languages generated, using our Hooks framework. The framework lets you hook in your code into three events in the API invocation lifecycle:
- Before Request - Code implemented in this section will run before the request. You can use the request object and update the request itself before it goes out to your API.
- After Response - Code implemented in this section will run after the call returns from your API. You can use the request objects to get context for the API call made and the response object for the response provided.
- On Error - Code implemented in this section will run in case of an API call error. You can use the request objects to get context for the API call and the exception object for information about the error that accrued.
Happy path
Error path
To implement hooks, you need to implement one or more of the three hooks - before request, after response or on error.
- TypeScript
- Python
- Java
export default class CustomHook implements Hook {
async beforeRequest(request: Request): Promise<void> {
// Your code goes here
}
async afterResponse(request: Request, response: Response): Promise<void> {
// Your code goes here
}
async onError(error: Exception): Promise<void> {
// Your code goes here
}
}
class CustomHook:
def before_request(self, request: Request):
# Your code goes here
def after_response(self, request: Request, response: Response):
# Your code goes here
def on_error(self, error: Exception, request: Request, response: Response):
# Your code goes here
public class CustomHook implements Hook {
@Override
public void beforeRequest(Request request) {
// Your code goes here
}
@Override
public void afterResponse(Request request, Response response) {
// Your code goes here
}
@Override
public void onError(Request request, Exception exception) {
// Your code goes here
}
}
Add hooks to your liblab project
To add the hooks framework, run the following command:
liblab hooks add
This will generate a hooks
folder which you can modify to extend the SDK. This hooks
folder will contain a folder for each of the languages specified in your config file.
hooks
├── java
├── python
└── typescript
Each folder will contain a full code project that you can add your custom hook code to. This code is then sent to liblab when your SDK is generated. The presence of the hooks
folder is all liblab needs to generate hooks in your SDK.
You can also add hooks for a single language by running
liblab hooks add --language=<language>
Implement hooks
In the hooks
folder, you will find a project for each of the languages you have generated.
- TypeScript
- Python
- Java
typescript
├── src
│ └── index.ts
├── tests
│ └── hook.spec.ts
├── package-lock.json
├── package.json
├── prettier.config.js
├── tsconfig.eslint.json
└── tsconfig.json
python
├── src
│ └── hook.py
├── test
│ └── test_hook.py
└── requirements.txt
java
├── src
│ ├── main/java/<namespace>
│ │ └── hook
│ │ ├── CustomHook.java
│ │ └── model
│ │ ├── Hook.java
│ │ ├── Request.java
│ │ └── Response.java
│ └── test/java/<namespace>
│ └── hook
│ └── CustomHookTest.java
└── pom.xml
Hook dependencies
Each hook project has relevant files to add dependencies, for example in a Python hook there is a requirements.txt
file, in Java there is a pom.xml
. If you want to add any dependencies to your hooks code, for example an SDK to add telemetry, you must add them to the relevant file so that they can be picked up during the SDK generation process.
If you do not add dependencies to these files, the SDK generation will not know to add them and your final SDK may not build or run
Before request
The before request hook is called just before the request is sent to your API. The hok receives a request object with the URL being called, the relevant verb (GET, POST etc.), the body as JSON, and the headers.
In this hook, you can modify the request object or the headers, and the changes will be sent to your API.
- TypeScript
- Python
- Java
The Request
class lives in the src/index.ts
file:
export interface Request {
method: string;
url: string;
input?: object;
headers: object;
}
The beforeRequest
function is implemented in the CustomHook
class in the src/index.ts
file:
export default class CustomHook implements Hook {
async beforeRequest(request: Request): Promise<void> {
// Your code goes here
}
}
The Request
class lives in the src/hook.py
file:
class Request:
def __init__(self, method, url, headers, body=""):
self.method = method
self.url = url
self.headers = headers
self.body = body
The before_request
function is implemented in the CustomHook
class in the src/hook.py
file:
class CustomHook:
def before_request(self, request: Request):
# Your code goes here
The Request
class lives in the src/main/<namespace>/hook/model/Request.java
file:
public class Request {
public Request(String method, String url, String body, Map<String, String> headers) {}
public String getMethod() {}
public void setMethod(String method) {}
public String getUrl() {}
public void setUrl(String url) {}
public String getBody() {}
public void setBody(String body) {}
public Map<String, String> getHeaders() {}
public void setHeaders(Map<String, String> headers) {}
}
The beforeRequest
function is implemented in the CustomHook
class in the src/main/<namespace>/hook/CustomHook.java
file:
public class CustomHook implements Hook {
@Override
public void beforeRequest(Request request) {
// Your code goes here
}
}
After response
The after response hook is called once the response has been received from the SDK. The hook receives the request object, and the response object. The response object contains the response body as JSON, the headers, and the status code.
In this hook you can modify the response body or headers, and the changes will be returned to the SDK caller.
- TypeScript
- Python
- Java
The Response
class lives in the src/index.ts
file:
export interface Response {
data: object;
headers: object;
status: number;
}
The afterResponse
function is implemented in the CustomHook
class in the src/index.ts
file:
export default class CustomHook implements Hook {
async afterResponse(request: Request, response: Response): Promise<void> {
// Your code goes here
}
}
The Response
class lives in the src/hook.py
file:
class Response:
def __init__(self, status, headers, body):
self.status = status
self.headers = headers
self.body = body
The after_response
function is implemented in the CustomHook
class in the src/hook.py
file:
class CustomHook:
def after_response(self, request: Request, response: Response):
# Your code goes here
The Response
class lives in the src/main/<namespace>/hook/model/Response.java
file:
public class Response {
public Response(int statusCode, String body, Map<String, String> headers) {}
public int getStatusCode() {}
public void setStatusCode(int statusCode) {}
public String getBody() {}
public void setBody(String body) {}
public Map<String, String> getHeaders() {}
public void setHeaders(Map<String, String> headers) {}
}
The afterResponse
function is implemented in the CustomHook
class in the src/main/<namespace>/hook/CustomHook.java
file:
public class CustomHook implements Hook {
@Override
public void afterResponse(Request request, Response response) {
// Your code goes here
}
}
On error
The on error hook is called if the API returns an error, such as a 4xx status code. This hook passes the request object, along with details of the error.
- TypeScript
- Python
- Java
The Exception
class lives in the src/index.ts
file:
export interface Exception extends Error {
title: string;
type?: string;
detail?: string;
instance?: string;
statusCode: number;
}
The onError
function is implemented in the CustomHook
class in the src/index.ts
file:
export default class CustomHook implements Hook {
async onError(error: Exception): Promise<void> {
// Your code goes here
}
}
The on_error
function is implemented in the CustomHook
class in the src/hook.py
file:
class CustomHook:
def on_error(self, error: Exception, request: Request, response: Response):
# Your code goes here
The error is provided in the error
parameter. For example, if the API call returns a status of 401, this will be a http_exceptions.client_exceptions.UnauthorizedException
.
The original request is passed to this hook in the request
parameter. The response from the call is passed in the response
parameter.
After this hook is called, the SDK code will throw the exception so that the SDK caller can handle it.
The onError
function is implemented in the CustomHook
class in the src/main/<namespace>/hook/CustomHook.java
file:
public class CustomHook implements Hook {
@Override
public void onError(Request request, Exception exception) {
// Your code goes here
}
}
The original request is passed to this hook in the request
parameter.
The error is provided in the exception
parameter. For example, this could be a java.io.IOException
if the request failed due to a network error, or an io.swagger.exceptions.ApiException
if the request failed due to an HTTP error response.
After this hook is called, the SDK code will throw the exception so that the SDK caller can handle it.
Build your SDK with hooks
When you next run the liblab build
command, liblab will send the code in the hooks
folder to our servers, and the resulting SDKs will have your code integrated automatically into the SDKs. Any package dependencies that your hooks have will be merged with the SDK dependencies.
Remember to add the hooks folder to your source control, so that your hooks code is always available when you build your SDKs. It should live in the same folder in the same repo as your liblab config file.
Remove hooks
To remove hooks you can use the command liblab hooks remove
, or manually delete your hooks folder. Once this is done, the next time you run liblab build
, your SDK will be generated without your custom hook code.