SDK customization options
The customizations
section is used to customize your SDKs.
Setting | Type | Required | Default | Description |
---|---|---|---|---|
authentication | object | ❌ | N/A | Customization the API authentication settings |
generateEnv | bool | ❌ | true | Should example .env files be generated? |
license | object | ❌ | N/A | What license should the SDKs be covered under? |
retry | object | ❌ | N/A | Retry functionality customization |
injectedModels | array | ❌ | N/A | A list of models to inject into their parent models |
customQueries | object | ❌ | N/A | A list of files containing custom queries to use when working with a GraphQL spec |
environmentVariables | array | ❌ | N/A | A list of environment variables to inject into the SDKs |
refreshToken | object | ❌ | N/A | Refresh token customization |
environments | array | ❌ | N/A | A list of deployment environments |
inferServiceNames | bool | ❌ | false | Should the SDKs infer service names from the spec? |
responseHeaders | bool | ❌ | false | Should response headers be included in the response object |
devContainer | bool | ❌ | false | Should the generated SDKs contain the files to be able to run it as a dev container |
authentication
The authentication
options allow you to configure the authentication that your API uses. This section is an object with the following options:
Option | Type | Required | Default | Description |
---|---|---|---|---|
access | object | ❌ | N/A | Access authentication options |
apiKey | object | ❌ | N/A | API key authentication options |
You can read more on authentication in our authentication guide.
access
This section is an object with the following options:
Option | Type | Required | Default | Description |
---|---|---|---|---|
prefix | string | ❌ | N/A | The prefix to use for the access authentication |
prefix
The prefix
is used to define a custom prefix when using a bearer token. By default when using a bearer token, the Authorization
header is passed as Bearer <token>
. For example, if your bearer token is Passw0rd123!
, the header would be:
Header | Value |
---|---|
Authorization | Bearer Passw0rd123! |
By setting the prefix
, you can replace Bearer
with the value of your choice.
For example, if you have the following:
{
...
"customizations": {
"authentication": {
"access": {
"prefix": "Token"
}
},
...
}
Then the header would be:
Header | Value |
---|---|
Authorization | Token Passw0rd123! |
apiKey
The apiKey
option is used to configure the API key authentication for the SDKs. This is used if the auth
setting is set to apikey
. This is an object with the following options:
Option | Type | Required | Default | Description |
---|---|---|---|---|
header | string | ❌ | X-API-KEY | The header to use for the API key authentication |
header
The header
option sets the header to use for the API key authentication. This is used by the SDKs to set the header when making requests to the API. For example, if you have the following:
{
...
"customizations": {
"authentication": {
"apiKey": {
"header": "MY_CUSTOM_APIKEY_HEADER"
}
},
...
}
Then the API key will be passed to your API using the MY_CUSTOM_APIKEY_HEADER
header.
If this is not set, then API keys by default will be passed using the X-API-KEY
header.
generateEnv
If this option is set to true
, then an example .env
file is created in the SDK with the name .env.example
to set environment variables for authentication. This can be used by your SDK users to configure their environment variables.
For example, if you have an API that uses a bearer token then this will need to be passed to the SDK on initialization, and it is good practice for this to be read from an environment variable or other configuration instead of being hard coded.
- TypeScript
- Python
- Java
In TypeScript you would initialize the SDK using code like this:
import { ExcitingSoda } from 'excitingsoda';
const sdk = new ExcitingSoda({ accessToken: process.env.EXCITINGSODA_ACCESS_TOKEN });
In Python you would initialize the SDK using code like this:
from os import getenv
from excitingsoda import ExcitingSoda
sdk = ExcitingSoda()
sdk.set_access_token(getenv("EXCITINGSODA_ACCESS_TOKEN"))
In Java you would initialize the SDK using code like this:
import soda.exciting.ExcitingSoda;
public class Main {
public static void main(String[] args) {
ExcitingSoda client = new ExcitingSoda(System.getenv("EXCITINGSODA_ACCESS_TOKEN"));
}
}
The EXCITINGSODA_ACCESS_TOKEN
environment variable will be defined in the .env.example
:
EXCITINGSODA_ACCESS_TOKEN=
The naming convention of .env.example
is standard practice as .env
files are normally not committed to source code control, and are usually ignored in .gitignore
files.
license
The license
option sets the open source license for the SDKs, if wanted. This is used in the LICENSE
file that is created with the SDKs, as well as in the package manifests (such as the TypeScript package.json
or Java pom.xml
files). This is an object with the following options:
Option | Type | Required | Default | Description |
---|---|---|---|---|
type | string | ❌ | NONE | The type of license to use. If this is not set, the default is to not set a license |
url | string | ❌ | "" | The URL of a custom license if the type is set to CUSTOM |
type
The type of license - such as one of a set of popular licenses, or a custom license.
Valid values are:
Value | Description |
---|---|
MIT | MIT |
GPL3 | GPL 3.0 |
APACHE | Apache 2.0 |
NONE | No license information will be set |
CUSTOM | Use a custom license |
For example, to use an MIT license for your SDK:
{
...
"customizations": {
"license": {
"type": "MIT"
}
},
...
}
If you set this to CUSTOM
, then you will need to set the url
to the URL of the license file to use.
url
The URL to a custom license file. The liblab CLI will need to be able to access this file, so the remote URL will need to be public, for example a source-available Business Source License (BSL) license available publicly on your website or in a public GitHub repo.
This is only used if the type
option is set to CUSTOM
. For example, to point the SDK to a custom license file, use the following:
{
...
"customizations": {
"license": {
"type": "CUSTOM",
"url": "https://exciting.soda/sdk/license.txt"
}
},
...
}
You can find a list of OSI approved licenses on the Open Source Initiative licenses page.
retry
The retry
setting is used to configure the retry functionality for the SDKs. This is an object with the following options:
Option | Type | Required | Default | Description |
---|---|---|---|---|
enabled | bool | ❌ | false | Should retry functionality be enabled? |
maxAttempts | integer | ❌ | 3 | The maximum number of attempts to retry |
retryDelay | integer | ❌ | 150 | The delay between retries in milliseconds |
If this setting is not set, the SDKs will not have retry functionality enabled.
enabled
The enabled
option determines whether or not retry functionality is enabled. If this is set to true
, then the SDK will retry requests that fail due to network errors or server errors. If this is set to false
, then the SDK will not retry requests.
maxAttempts
The maxAttempts
option sets the maximum number of attempts to retry a request. For example, if this is set to 3
, then the SDK will retry a request up to 3 times before giving up.
retryDelay
The retryDelay
option sets the delay between retries in milliseconds. For example, if this is set to 150
, then the SDK will wait 150 milliseconds between retries.
injectedModels
When liblab generates an SDK, it creates models for the request and response objects, as well as the different schemas defined in the components section of the API spec. Some schemas are composed of other schemas, and these are by default created as separate models.
For example, if you have:
"Message": {
"properties": {
"message": {
"type": "string",
"title": "Message"
},
"body": {
"$ref": "#/components/schemas/MessageBody"
}
},
"type": "object",
},
"MessageBody": {
...
}
Then the SDK will create 2 models, Message
and MessageBody
. If you don't want the separate MessageBody
model created, you can add "MessageBody"
to the injectedModels
array, and the MessageBody
model will be injected into the Message
model:
{
...
"customizations": {
"injectedModels": [
"MessageBody"
]
},
...
}
You will get the following:
- TypeScript
- Python
- Java
export interface Message {
message: string;
body: Body;
}
interface Body {
details: string;
}
class Body(BaseModel):
def __init__(self, details: str, **kwargs):
self.details = details
class Message(BaseModel):
def __init__(self, body: Body, message: str, **kwargs):
self.body = body
self.message = message
public class Message extends soda.exciting.models.BaseModel {
public static class Body extends soda.exciting.models.BaseModel {
private final java.lang.String details;
}
private final Message.Body body;
private final java.lang.String message;
}
customQueries
The customQueries
setting is used to configure custom queries for GraphQL APIs. GraphQL allows your APIs to be easily extensible, allowing you to query the API and get results that can change over time as more fields are added. This is great for flexibility, but makes SDK generation impossible as there is no way to know what fields are available to create type-safe SDKs.
Custom queries are a way to provide specific capabilities to your SDK, allowing you to provide a named service with a strongly typed response.
For example, if you have the following query:
query GetBooks {
book {
id
name
author
}
}
This will generate a book
object, and a service to retrieve all books:
export type Book = Attributable &
Identifiable & {
id: string;
name: string;
};
The customQueries
setting allows you to define a list of custom query files (.gql
files) to add to the SDK. This is an object with the following options:
Option | Type | Required | Default | Description |
---|---|---|---|---|
paths | array | ✅ | N/A | A list of paths to the custom query files |
paths
The paths
option is an array of paths to local custom query files.
{
...
"customizations": {
"customQueries": {
"paths": [
"./book-queries.gql"
"./movie-queries.gql"
]
}
},
...
}
environmentVariables
The environmentVariables
setting allows you to define environment variables that need to be set by the SDK user for the SDK to work - for example if you need values set that are used in code you create in a hook.
This setting is an array of objects, with each object having the following options:
Option | Type | Required | Default | Description |
---|---|---|---|---|
name | string | ✅ | N/A | The name of the environment variable |
description | string | ✅ | N/A | The value of the environment variable |
These environment variables will be declared in the .env.example
files that are created with each SDK (assuming this is enabled with the generateEnv
setting). They will also be documented in the README for the SDK.
For example, if you have the following:
{
...
"environmentVariables" : [
{
"name": "TEAM_NAME",
"description": "The team name for the SDK user"
}
],
...
}
This will add the following to the .env.example
file:
TEAM_NAME=
And the following to the README:
## Environment Variables
You will need the following environment variables in order to access all the features of this SDK:
| Name | Description |
| :-------- | :----------|
| TEAM_NAME | The team name for the SDK user |
name
The name for the environment variable.
description
The description for the environment variable. This is used for the README file only.
refreshToken
The refreshToken
setting is used to configure the refresh token functionality for the SDKs, to allow the SDK user to remain authenticated after their authentication expires. This is an object with the following options:
Option | Type | Required | Default | Description |
---|---|---|---|---|
endpoint | string | ✅ | N/A | The endpoint to use for refreshing the token |
bearerKey | string | ✅ | N/A | The response property to use to get the bearer token from the response |
refreshKey | string | ✅ | N/A | The response property to use to get the refresh token from the response |
If your API supports refresh tokens for authentication, then this needs to be set. If this section is not set, then the SDKs will not have refresh token functionality enabled. You can read more about refresh tokens in our authentication guide.
These values need to match your spec - the endpoint
needs to be defined as a supported path that can receive POST requests, and the bearerKey
and refreshKey
must be valid fields in the response from the endpoint.
endpoint
This is the endpoint to use to refresh the token.
bearerKey
The bearerKey
is the name of the key in the response from the refresh token endpoint that contains the access token that the SDK can use to continue authenticating with the API.
For example, if this is the response from the endpoint that is defined in your API spec:
"RefreshTokenPair": {
"properties": {
"accessToken": {
"type": "string"
},
"refreshToken": {
"type": "string"
}
},
"required": [
"accessToken",
"refreshToken"
],
"type": "object"
},
Then the bearerKey
should be set to access_token
:
{
"customizations": {
"refreshToken": {
...
"bearerKey": "access_token"
}
}
}
refreshKey
The refreshKey
is the name of the key in the response from the refresh token endpoint that contains the refresh token that the SDK can use to continue refreshing the access token with the API.
For example, if this is the response from the endpoint that is defined in your API spec:
"RefreshTokenPair": {
"properties": {
"accessToken": {
"type": "string"
},
"refreshToken": {
"type": "string"
}
},
"required": [
"accessToken",
"refreshToken"
],
"type": "object"
},
Then the refreshKey
should be set to refresh_token
:
{
"customizations": {
"refreshToken": {
...
"refreshKey": "refresh_token"
}
}
}
environments
Your API may have multiple environments or locations, such as preview
and production
, or NorthAmerica
, EMEA
, and APAC
. This setting allows you to configure environments to be added to the SDKs as constant values that are passed in when creating the SDK client.
This is an array of objects, with each object having the following options:
Option | Type | Required | Default | Description |
---|---|---|---|---|
name | string | ✅ | N/A | The name of the environment |
url | string | ✅ | N/A | The URL for the environment |
You can read more about configuring URLs and setting these at run time in our URLs and environments guide.
name
The name
option sets the name of the environment. This is used as the name of the constant in the SDK, and is used when creating the SDK client. This name must not have any invalid characters that would not be allowed in constant names. For example, DevNA
is valid, Dev NA
is not.
url
The url
option sets the URL for the environment. This is used as the value of the constant in the SDK, and is used when creating the SDK client. This is validated to ensure it is a valid URL.
inferServiceNames
The inferServiceNames
flag determines if service names for the SDK should be inferred from the endpoint names rather than tags
if they all share a common prefix.
The OpenAPI specification has tags
that are used to group service endpoints. For example:
tags:
- name: Boba
description: Operations for boba
- name: Soda
description: Operations for soda
paths:
"/boba":
get:
tags:
- Boba
...
"/boba/{id}":
post:
tags:
- Boba
...
"/soda":
get:
tags:
- Soda
...
In this case, the Boba
tag is used to group the GET and POST operations for the /boba
endpoints, and the Soda
tag is used for the /soda
endpoint. Using tags means the services in your SDK will be created based on these tags - for example in this case you would have a BobaService
with methods to get and post boba, and a SodaService
with a method to get soda.
tags
are optional, so you many not have these defined in your spec. You may also have a common prefix to your endpoint, such as a version number:
paths:
"/v2/boba":
get:
...
"/v2/boba/{id}":
post:
...
"/v2/soda":
get:
...
In this case the liblab CLI would default to creating a V2Service
service with methods to get and post boba, and get soda. This may not be what you want, you may want to have a BobaService
and SodaService
service instead.
To avoid this set the inferServiceNames
option to true
to infer the service names from the endpoint paths.
{
...
"customizations": {
"inferServiceNames": true
},
...
}
In this case, this would look at all the endpoints, and if they have a common prefix, then this would be ignored for the service names. Instead of a V2Service
service, you would get a BobaService
and SodaService
.
This only works if your endpoints all have the same common prefix. For example, if you have some endpoints that are v2
and some that are v3
then service names cannot be inferred, as this may lead to name clashes (for example both services having a GET endpoint for boba
). In this case, it is recommended to use tags in your OpenAPI spec.
responseHeaders
When responses come back from API calls, they can contain data, as well as important information in the headers. By default, the SDKs will only return the data from the response, and ignore the headers. If you want to include the headers in the response, then set this option to true
.
For example, if you have a response object like this:
paths:
/book/{id}:
get:
operationId: getBook
parameters:
- name: id
in: path
required: true
description: The id of the book
schema:
type: string
responses:
'200':
description: Expected response to a valid request
content:
application/json:
schema:
$ref: '#/components/schemas/Book'
components:
schemas:
Book:
type: object
properties:
name:
type: string
id:
type: string
If the response headers option is not set, or set to false
, then the generated getBook
method will look like this:
- TypeScript
- Python
- Java
async getBook(id: string): Promise<Book> {
def get_book(self, id: str) -> BookModel:
public soda.exciting.models.Book getBook(String id) throws ApiException {
If you set the response headers option to true
:
{
...
"customizations": {
"responseHeaders": true
},
...
}
- TypeScript
- Python
- Java
The the return values from calls to the SDK services will be a wrapper object containing the response data, as well as a dictionary of the headers. The getBook
method will look like this:
async getBook(id: string): Promise<Response<Book>> {
With the Response<T>
type looking like this:
export default interface Response<T> {
data: T;
headers: Record<string, string>;
}
The the return values from calls to the SDK services will be a wrapper object containing the response data, as well as a dictionary of the headers. The get_book
method will look like this:
def get_book(self, id: str) -> ResponseWithHeaders:
With the ResponseWithHeaders
type looking like this:
class ResponseWithHeaders:
def __init__(self, data, headers):
self.data = data
self.headers = headers
The the return values from calls to the SDK services will be a wrapper object containing the response data, as well as a dictionary of the headers. The getBook
method will look like this:
public ResponseWithHeaders<soda.exciting.models.Book> getBook(String id) throws ApiException
With the ResponseWithHeaders<T>
class looking like this:
public class ResponseWithHeaders<T> {
private T data;
private Headers headers;
public ResponseWithHeaders(T data, Headers headers) {
this.data = data;
this.headers = headers;
}
public T getData() {
return data;
}
public Headers getHeaders() {
return headers;
}
}
If this call returned a header such as Content-Range
, this would be in the headers
dictionary.
devContainer
The devContainer
option determines if the generated SDKs should include the files to be able to open and run the SDK using a dev container.
A dev container is a development environment setup that is containerized using technologies like Docker, making it consistent and reproducible across different development machines. It contains everything needed for the development environment, including the programming language, dependencies, libraries, tools and extensions.
If you set this to true
, every SDK generated will include a devcontainer.json
file, allowing you to start using the SDK as soon as it’s produced from inside any IDE that supports development containers, such as VS Code. Additionally, each SDK will have a VS Code launch.json
file so you can run and debug the SDK example.
{
...
"customizations": {
"devContainer": true
},
...
}
You can read more on development containers in the development containers documentation.