Newer
Older
CrypticOreWallet / src / krist / api / index.ts
@Drew Lemmy Drew Lemmy on 28 Feb 2021 1 KB feat: search box boilerplate
import { notification } from "antd";
import i18n from "../../utils/i18n";

import { store } from "../../App";

import { APIResponse } from "./types";
import { throttle } from "lodash-es";

export class APIError extends Error {
  constructor(message: string, public parameter?: string) {
    super(message);
  }
}

export class RateLimitError extends APIError {
  constructor() { super("rate_limit_hit"); }
}

// Realistically, the only situation in which a rate limit will actually be hit
// by KristWeb is if an infinite loop is introduced (e.g. via useEffect), so we
// would want to avoid spamming notifications and making the performance bug
// worse, therefore this notification is throttled to 5 seconds.
const _notifyRateLimit = () =>
  notification.error({ message: i18n.t("rateLimitTitle"), description: i18n.t("rateLimitDescription") });
const notifyRateLimit = throttle(_notifyRateLimit, 5000);

interface RequestOptions extends RequestInit {
  /** Suppresses the notification for a rate limited request. An error will
   * still be thrown. */
  ignoreRateLimit?: boolean;
}

export async function request<T>(method: string, endpoint: string, options?: RequestOptions): Promise<APIResponse<T>> {
  const syncNode = store.getState().node.syncNode;

  // Let the fetch bubble its error upwards
  const res = await fetch(syncNode + "/" + endpoint, {
    method,
    ...options
  });

  if (res.status === 429) {
    if (!options?.ignoreRateLimit) notifyRateLimit();
    throw new RateLimitError();
  }

  const data: APIResponse<T> = await res.json();
  if (!data.ok || data.error)
    throw new APIError(data.error || "unknown_error", data.parameter);

  return data;
}

export const get = <T>(endpoint: string, options?: RequestOptions): Promise<APIResponse<T>> =>
  request("GET", endpoint, options);
export const post = <T>(endpoint: string, options?: RequestOptions): Promise<APIResponse<T>> =>
  request("POST", endpoint, options);