Customize your SDK with hooks
This tutorial includes the following SDK languages and versions:
TypeScript v1 TypeScript v2 Java v1 Java v2 Python v1 Python v2 C# Go PHP ✅ ✅ ✅ ✅ ✅ ✅ ✅ ✅ ✅
Hooks are a powerful way to customize your SDK. They allow you to hook into the API calls and modify the request or response. You can also use them to add custom functionality to your SDK.
Some examples of ways you can use hooks:
- Add custom headers to all requests
- Add an API version as a header or a query string parameter
- Customize authentication, such as copying an API key from a header to a URL parameter
- Add telemetry
- Add logging to API errors
In this tutorial, we'll show you how to use hooks to add API versions to your SDK using the Accept-version
header.
Prerequisites
This tutorial assumes you already have:
- The liblab CLI installed and you are logged in
- An API spec
- A liblab config file created using
liblab init
Steps
In this tutorial you'll:
Create the hooks
First we'll create the hooks code. Run the following command in the same folder as your config file:
liblab hooks add
This will create a hooks
folder with hooks for each SDK language defined in your config file:
hooks
├── csharp
├── go
├── java
├── php
├── python
└── typescript
You may see a different set of hooks folders depending on the languages you have defined in your config file.
For each language, you may need to install some necessary dependencies.
- TypeScript v1
- TypeScript v2
- Java v1
- Java v2
- Python v1
- Python v2
- C#
- Go
- PHP
To install the TypeScript dependencies, run the following command from the hooks/typescript
folder:
npm install
To install the TypeScript dependencies, run the following command from the hooks/typescript
folder:
npm install
The Java hooks project is set up to use Maven. You can install the dependencies with:
mvn install
The Java hooks project is set up to use Maven. You can install the dependencies with:
mvn install
The hooks folder includes a requirements.txt
file with the relevant dependencies for the hooks code. You should install this in a virtual environment.
-
Create the virtual environment in the
hooks/python
folder:Terminalpython -m venv .venv
Use
python3
if you have both Python 2 and Python 3 installed. -
Activate the virtual environment. On linux or macOS run:
Terminalsource .venv/bin/activate
On Windows run:
cmd.venv\Scripts\activate.bat
-
Install the requirements:
Terminalpip install -r requirements.txt
When you first create hooks this file is empty. When you add dependencies to your hooks, they need to be added here to be picked up by the SDK generation code and added to your SDK.
The hooks folder includes a requirements.txt
file with the relevant dependencies for the hooks code. You should install this in a virtual environment.
-
Create the virtual environment in the
hooks/python
folder:Terminalpython -m venv .venv
Use
python3
if you have both Python 2 and Python 3 installed. -
Activate the virtual environment. On linux or macOS run:
Terminalsource .venv/bin/activate
On Windows run:
cmd.venv\Scripts\activate.bat
-
Install the requirements:
Terminalpip install -r requirements.txt
When you first create hooks this file is empty. When you add dependencies to your hooks, they need to be added here to be picked up by the SDK generation code and added to your SDK.
The C# hooks code has no additional dependencies.
The Go hooks code has no additional dependencies.
The hooks folder includes a composer.json
file with the relevant dependencies for the hooks code.
-
Install the dependencies:
Terminalcomposer install
Add the hook code
In the hooks
folder, you will find a project for each of the languages you have generated. The API version will be added to a header in the before request hook.
Locate the before request hook
Open the relevant file and inspect the hook code.
- TypeScript v1
- TypeScript v2
- Java v1
- Java v2
- Python v1
- Python v2
- C#
- Go
- PHP
The beforeRequest
method 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 beforeRequest
method 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 type used is an instance of okhttp3.Request.
The beforeRequest
method is implemented in the CustomHook
class in the src/main/<namespace>/hook/CustomHook.java
file:
package com.liblab.hook;
import java.util.Map;
import okhttp3.Request;
public class CustomHook implements Hook {
@Override
public Request beforeRequest(Request request, Map<String, String> additionalParameters) {
// Your code goes here
return request;
}
}
The request type used is an instance of okhttp3.Request.
The beforeRequest
method is implemented in the CustomHook
class in the src/main/<namespace>/hook/CustomHook.java
file:
package com.liblab.hook;
import java.util.Map;
import okhttp3.Request;
public class CustomHook implements Hook {
@Override
public Request beforeRequest(Request request, Map<String, String> additionalParameters) {
// Your code goes here
return request;
}
}
The before_request
method is implemented in the CustomHook
class in the src/hook.py
file:
class CustomHook:
def before_request(self, request: Request, **kwargs):
# Your code goes here
The before_request
method is implemented in the CustomHook
class in the src/hook.py
file:
class CustomHook:
def before_request(self, request: Request, **kwargs):
# Your code goes here
The BeforeRequestAsync
method is implemented in the CustomHook
class in the CustomHook.cs
file:
public async Task<HttpRequestMessage> BeforeRequestAsync(HttpRequestMessage request)
{
// Your code goes here
}
The BeforeRequest
method is implemented in the hooks
package in the custom_hook.go
file:
type CustomHook struct{}
func (h *CustomHook) BeforeRequest(req Request) Request {
// Your code goes here
return req
}
The beforeRequest
method is implemented in the CustomHook
class in the CustomHook.php
file:
class CustomHook implements HookInterface
{
public function beforeRequest(RequestInterface &$request): void
{
// Your code goes here
}
}
We can modify the request object by adding or removing headers, tampering with the request body, changing the URL, etc.
- TypeScript v1
- TypeScript v2
- Java v1
- Java v2
- Python v1
- Python v2
- C#
- Go
- PHP
The Request
class lives in the src/index.ts
file:
export interface Request {
method: string;
url: string;
input?: object;
headers: object;
}
The Request
class lives in the src/index.ts
file:
export interface Request {
method: string;
url: string;
input?: object;
headers: object;
}
The request
type used is an instance of okhttp3.Request
.
These request objects are immutable, but you can modify the request by creating a new builder, and returning the new request:
@Override
public Request beforeRequest(Request request, Map<String, String> additionalParameters) {
Request newRequest =
request.newBuilder()
// Modify the request here
.build();
return newRequest;
}
The request
type used is an instance of okhttp3.Request
.
These request objects are immutable, but you can modify the request by creating a new builder, and returning the new request:
@Override
public Request beforeRequest(Request request, Map<String, String> additionalParameters) {
Request newRequest =
request.newBuilder()
// Modify the request here
.build();
return newRequest;
}
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 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 request
type used is an instance of System.Net.Http.HttpRequestMessage
.
The Request
type lives in the hooks/hook.go
file:
type Request interface {
GetMethod() string
SetMethod(method string)
GetBaseUrl() string
SetBaseUrl(baseUrl string)
GetPath() string
SetPath(path string)
GetHeader(header string) string
SetHeader(header string, value string)
GetPathParam(param string) string
SetPathParam(param string, value any)
GetQueryParam(param string) string
SetQueryParam(param string, value any)
GetBody() any
SetBody(body any)
}
The request
type used is an instance of Psr\Http\Message\RequestInterface
.
Any changes you make to the request will be used by the SDK when making the API call.
Add the version header
To add the version header, make the following code changes to the before request hooks:
- TypeScript v1
- TypeScript v2
- Java v1
- Java v2
- Python v1
- Python v2
- C#
- Go
- PHP
async beforeRequest(request: Request): Promise<void> {
// Add the Accept-version header to the request with version 1.0
request.headers = {
...request.headers,
"Accept-version": "1.0"
};
}
async beforeRequest(request: Request): Promise<void> {
// Add the Accept-version header to the request with version 1.0
request.headers = {
...request.headers,
"Accept-version": "1.0"
};
}
@Override
public Request beforeRequest(Request request, Map<String, String> additionalParameters) {
Request newRequest =
request.newBuilder()
.addHeader("Accept-version", "1.0")
.build();
return newRequest;
}
@Override
public Request beforeRequest(Request request, Map<String, String> additionalParameters) {
Request newRequest =
request.newBuilder()
.addHeader("Accept-version", "1.0")
.build();
return newRequest;
}
def before_request(self, request: Request, **kwargs):
# Add the Accept-version header to the request with version 1.0
request.headers["Accept-version"] = "1.0"
def before_request(self, request: Request, **kwargs):
# Add the Accept-version header to the request with version 1.0
request.headers["Accept-version"] = "1.0"
public async Task<HttpRequestMessage> BeforeRequestAsync(HttpRequestMessage request)
{
request.Headers.Add("Accept-version", "1.0");
return request;
}
func (h *CustomHook) BeforeRequest(req Request) Request {
req.SetHeader("X-Request-Id", "123")
return req
}
public function beforeRequest(RequestInterface &$request): void
{
// Add the Accept-version header to the request with version 1.0
$request = $request->withHeader('Accept-version', '1.0');
}
This will add the new header into the request, and this will then be used by the SDK when making the API call.
Build the SDK
Now that your hook code is ready, you can build the SDK with the following command:
liblab build
Packaging hooks...
✓ C# built
✓ Go built
✓ Java built
✓ Python built
✓ TypeScript built
Successfully generated SDKs downloaded. You can find them inside the "output" folder
This will send your hook code along with your spec and config to liblab to use in the SDK generation. You can see the results in the SDKs that are generated and downloaded to the output
folder.
- TypeScript v1
- TypeScript v2
- Java v1
- Java v2
- Python v1
- Python v2
- C#
- Go
- PHP
The hooks can be found in the src/hooks
folder:
hooks
├── CustomHook.ts
└── Hook.ts
Your hook code is in the Hook.ts
file.
The hooks can be found in the src/hooks
folder:
hooks
├── CustomHook.ts
└── Hook.ts
Your hook code is in the Hook.ts
file.
The hooks can be found in the src/main/java/<namespace>/hook
folder:
hook
├── CustomHook.java
└── Hook.java
Your hook code is in the CustomHook.java
file.
The hooks can be found in the src/main/java/<namespace>/hook
folder:
hook
├── CustomHook.java
└── Hook.java
Your hook code is in the CustomHook.java
file.
The hooks can be found in the src/<project>/hooks
folder:
hooks
├── __init__.py
└── hook.py
Your hook code is in the hook.py
file.
The hooks can be found in the src/<project>/hooks
folder:
hooks
├── __init__.py
└── hook.py
Your hook code is in the hook.py
file.
The hooks can be found in the <namespace>/Hooks
folder:
Hooks
├── CustomHook.cs
└── IHook.cs
Your hook code is in the CustomHook.cs
file.
The hooks can be found in the internal/clients/rest/hooks
folder:
Hooks
├── custom_hook.go
└── hook.go
Your hook code is in the custom_hook.go
file.
The hooks can be found in the src/hooks
folder:
hooks
├── CustomHook.php
└── HookInterface.php
Your hook code is in the CustomHook.php
file.