@aptos-labs/ts-sdk
Version:
Aptos TypeScript SDK
125 lines (111 loc) • 4.01 kB
text/typescript
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0
import { AptosConfig } from "../api/aptosConfig";
import { AptosApiError, AptosResponse } from "./types";
import { VERSION } from "../version";
import { AnyNumber, AptosRequest, Client, ClientRequest, ClientResponse, MimeType } from "../types";
import { AptosApiType } from "../utils";
/**
* Meaningful errors map
*/
const errors: Record<number, string> = {
400: "Bad Request",
401: "Unauthorized",
403: "Forbidden",
404: "Not Found",
429: "Too Many Requests",
500: "Internal Server Error",
502: "Bad Gateway",
503: "Service Unavailable",
};
/**
* Given a url and method, sends the request with axios and
* returns the response.
*/
export async function request<Req, Res>(options: ClientRequest<Req>, client: Client): Promise<ClientResponse<Res>> {
const { url, method, body, contentType, params, overrides, originMethod } = options;
const headers: Record<string, string | AnyNumber | boolean | undefined> = {
...overrides?.HEADERS,
"x-aptos-client": `aptos-typescript-sdk/${VERSION}`,
"content-type": contentType ?? MimeType.JSON,
"x-aptos-typescript-sdk-origin-method": originMethod,
};
if (overrides?.AUTH_TOKEN) {
headers.Authorization = `Bearer ${overrides?.AUTH_TOKEN}`;
}
if (overrides?.API_KEY) {
headers.Authorization = `Bearer ${overrides?.API_KEY}`;
}
/*
* make a call using the @aptos-labs/aptos-client package
* {@link https://www.npmjs.com/package/@aptos-labs/aptos-client}
*/
return client.provider<Req, Res>({
url,
method,
body,
params,
headers,
overrides,
});
}
/**
* The main function to use when doing an API request.
*
* @param options AptosRequest
* @param aptosConfig The config information for the SDK client instance
* @returns the response or AptosApiError
*/
export async function aptosRequest<Req extends {}, Res extends {}>(
options: AptosRequest,
aptosConfig: AptosConfig,
apiType: AptosApiType,
): Promise<AptosResponse<Req, Res>> {
const { url, path } = options;
const fullUrl = path ? `${url}/${path}` : url;
const response = await request<Req, Res>({ ...options, url: fullUrl }, aptosConfig.client);
const result: AptosResponse<Req, Res> = {
status: response.status,
statusText: response.statusText!,
data: response.data,
headers: response.headers,
config: response.config,
request: response.request,
url: fullUrl,
};
// Handle case for `Unauthorized` error (i.e API_KEY error)
if (result.status === 401) {
throw new AptosApiError(options, result, `Error: ${result.data}`);
}
// to support both fullnode and indexer responses,
// check if it is an indexer query, and adjust response.data
if (apiType === AptosApiType.INDEXER) {
const indexerResponse = result.data as any;
// Handle Indexer general errors
if (indexerResponse.errors) {
throw new AptosApiError(
options,
result,
`Indexer error: ${indexerResponse.errors[0].message}` ??
`Indexer unhandled Error ${response.status} : ${response.statusText}`,
);
}
result.data = indexerResponse.data as Res;
}
if (result.status >= 200 && result.status < 300) {
return result;
}
let errorMessage: string;
if (result && result.data && "message" in result.data && "error_code" in result.data) {
errorMessage = JSON.stringify(result.data);
} else if (result.status in errors) {
// If it's not an API type, it must come form infra, these are prehandled
errorMessage = errors[result.status];
} else {
// Everything else is unhandled
errorMessage = `Unhandled Error ${result.status} : ${result.statusText}`;
}
// We have to explicitly check for all request types, because if the error is a non-indexer error, but
// comes from an indexer request (e.g. 404), we'll need to mention it appropriately
throw new AptosApiError(options, result, `${apiType} error: ${errorMessage}`);
}