Skip to content

Latest commit

 

History

History
131 lines (103 loc) · 5.32 KB

endpoints.md

File metadata and controls

131 lines (103 loc) · 5.32 KB

Endpoints

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

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',
  },
})

Endpoint Types

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.

Cache Key Generator

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

Custom input validation

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

Price Endpoint

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,
  ...
})