Endpoints are defined for the different types of data points an adapter needs to provide. These bear no relation to HTTP endpoints for a Data Provider, although in many cases there will be a 1:1 match. To reach a particular adapter endpoint, the endpoint
parameter can be specified in the request using either its name or any of its aliases.
If the endpoint field is omitted, the adapter will use the default endpoint in the Adapter
parameters, if set. If the field is explicitly set and does not match any endpoints or their aliases, the request will fail.
Define each endpoint in its own file in the /endpoints
folder. Name the file the endpoint's name such as crypto.ts
. Use the parameters described in the sections below to create endpoints that fit the need of your particular adapter.
Input parameters define the structure of the request expected by the endpoint. The framework provides helpful fields to customize input parameters along with validations to ensure each request conforms to the structure.
const inputParameters = new InputParameters({
param1: {
aliases: ['param-one'],
required: true,
type: 'string',
description: 'The symbol of the currency to query',
options: ['option1', 'option2'], // Enumerated options
default: 'option1',
dependsOn: ['param2'], // Other inputs this one depends on
exclusive: ['param3'], // Other inputs that cannot be present with this one
},
param2: {
required: false,
type: 'string',
description: 'Required by param1',
},
param3: {
required: false,
type: 'string',
description: 'Exclusive from param1',
},
})
Endpoints contain a type parameter that allows specifying relevant types of an endpoint. The developer can specify types for the adapter request, adapter response, and adapter config. An example is shown below.
import { config } from './config'
interface ResponseSchema {
base: string
quote: string
price: number
}
export type BaseEndpointTypes = {
// Expected adapter request structure received by this endpoint
Parameters: typeof inputParameters.definition
// Expected adapter response structure returned by the endpoint
Response: {
Data: ResponseSchema
Result: number
}
Settings: typeof config.settings
}
Transport files extend BaseEndpointTypes and additionally provide DP specific information like DP request/response type or websocket message types are defined in corresponding transport files.
Only use if absolutely necessary
The Cache Key Generator method allows the developer to provide custom logic used to generate a unique cache key for each request. The framework's default behavior is to concat the endpoint name with the stringified input parameters such as test-{"base":"ETH","quote": "USD"}
.
A potential scenario of where a custom cache key generator would be needed is if an input parameter is an array or an object. To ensure that a request generates the same unique key every time, the array or object would need to be sorted. An example of this code is shown below.
function cacheKeyGenerator(data) {
data.base = (data.base as unknown as string[]).sort()
return `test-${JSON.stringify(data)}`
}
To understand where the cache key generator is applied, please refer to the Request Validation & Transformation Diagram
Only use if absolutely necessary
The custom input validation method allows the developer to specify custom logic to validate input on top of the built-in validations the framework provides. It returns an AdapterError
on failure, otherwise undefined
on success. An example is shown below.
function customInputValidation(
req: TypeFromDefinition<typeof inputParameters.definition>,
config: AdapterConfig<typeof customSettings>,
): AdapterError | undefined {
if (req.value && (req.value < 0 || req.value > 100)) {
return new AdapterInputError({
statusCode: 400,
message: 'Value is less than 0 or greater than 100',
})
}
return
}
To understand where these validations are applied, please refer to the Request Validation & Transformation Diagram
As mentioned in the Adapter Guide, PriceAdapter
requires that one of its endpoints is a PriceEndpoint
. To help standardize the format of a price feed endpoint, the framework provides helpful built-in types. This would alter some of the examples shown in the previous sections like below.
import {
PriceEndpoint,
priceEndpointInputParameters,
PriceEndpointParams,
} from '@chainlink/external-adapter-framework/adapter'
import { SingleNumberResultResponse } from '@chainlink/external-adapter-framework/util'
type EndpointTypes = {
// The PriceEndpointInputParameters type is the most common request params for a price endpoint. {base: string, quote: string}
Parameters: PriceEndpointInputParameters
...
// The SingleNumberResultResponse type is the most common response format for price endpoints. {result: number, data: { result: number }}
Response: SingleNumberResultResponse,
...
}
export const endpoint = new PriceEndpoint({
...
inputParameters: priceEndpointInputParameters,
...
})