Path: blob/master/src/packages/frontend/client/api.ts
1503 views
/*1Use the api v2 endpoint from the app frontend. This is everything defined2in @cocalc/next/pages/api/v234We always use POST requests here.56The v1 api is also exported here.78This doesn't know anything about types, etc.9*/1011import { join } from "path";12import { appBasePath } from "@cocalc/frontend/customize/app-base-path";13import { delay } from "awaiting";14import { trunc } from "@cocalc/util/misc";1516export default async function api(endpoint: string, args?: object) {17return await callApi(join("v2", endpoint), args);18}1920// also the old v1 api21export async function v1(endpoint: string, args?: object) {22return await callApi(join("v1", endpoint), args);23}2425// if api call fails (typically 5xx due to a temporary restart of26// backend servers e.g., in kubernetes) we wait RETRY_DELAY_MS, then give27// it NUM_RETRIES many ties before showing the user an error.28// Setting the third numRetriesOnFail argument to 0 below29// can be used to disable this behavior.30// This "api call fails" isn't where you get an error json31// back, but when actually making the request really is32// failing, e.g., due to network or server issues.33const RETRY_DELAY_MS = 3000;34const NUM_RETRIES = 2;3536// NOTE: I made this complicated with respClone, so I can see37// what the response is if it is not JSON.38async function callApi(39endpoint: string,40args?: object,41numRetriesOnFail?: number,42) {43// console.log("callApi", { endpoint, args });44const url = join(appBasePath, "api", endpoint);45const resp = await fetch(url, {46method: "POST",47headers: {48"Content-Type": "application/json",49},50...(args != null ? { body: JSON.stringify(args) } : undefined),51});52const respClone = resp.clone();53let json: any = null;54try {55json = await resp.json();56} catch (e) {57console.log(e);58const r = await respClone.text();59console.log(trunc(r, 2000));60if (numRetriesOnFail != null && numRetriesOnFail == 0) {61throw Error("API server is down -- try again later");62}63numRetriesOnFail = numRetriesOnFail ?? NUM_RETRIES;64console.log(65`waiting ${RETRY_DELAY_MS}ms then trying again up to ${numRetriesOnFail} more times`,66);67await delay(RETRY_DELAY_MS);68return await callApi(endpoint, args, numRetriesOnFail - 1);69}70if (json == null) {71throw Error("timeout -- try again later");72}73if (typeof json == "object" && json.error) {74throw Error(json.error);75}76if (typeof json == "object" && json.errors) {77// This is what happens when the api request fails due to schema validation issues.78// I.e., this is soemthing we only see in dev mode since the schema stuff is disabled in production.79throw Error(80`API Schema Error: ${json.message} ${JSON.stringify(json.errors)}`,81);82}83// console.log("got ", json);84return json;85}868788