API: Please accept data in the body as JSON

Currently it appears that the API does not accept data in the form of a JSON-stringified body. So, for example, in Node the following code does not work:

async function api({
  route,
  method = "get",
  data,
}: {
  route: string;
  method?: "get" | "post" | "put" | "delete";
  data?: Record<string, unknown>;
}) {
  const result = await fetch(`${API_ROOT}${route}?auth_token=${API_KEY}`, {
    method,
    body: JSON.stringify(data),
  });

  if (!result.ok) {
    throw new Error(`${result.status} ${result.statusText}`);
  }

  return result.json();
}

Whereas this code does work:

async function api({
  route,
  method = "get",
  data,
}: {
  route: string;
  method?: "get" | "post" | "put" | "delete";
  data?: Record<string, unknown>;
}) {
  const form = new URLSearchParams();
  if (data) {
    Object.entries(data).forEach(([key, value]) => {
      form.append(key, value as string);
    });
  }

  const result = await fetch(`${API_ROOT}${route}?auth_token=${API_KEY}`, {
    method,
    body: data && form,
  });

  if (!result.ok) {
    throw new Error(`${result.status} ${result.statusText}`);
  }

  return result.json();
}

I would prefer to use the former code since it’s easier both to read and to write, and requires less code. Could the API be modified to accept both URL-encoded form data (the second example) and JSON bodies (the first example)?

2 Likes

As is standard, you need to pass a Content-Type header to specify the format of the data that you’re passing (for JSON, that’s application/json), at least if it isn’t the default (Beeminder by default assumes form encoding, application/x-www-form-urlencoded.)

In your example with fetch, that would look something like

const result = await fetch(`${API_ROOT}${route}?auth_token=${API_KEY}`, {
  method,
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(data)
})
6 Likes

Oh, gotcha! I’ll try that and see if it works. Makes since that it may have been user error here. :sweat_smile:

4 Likes

Yup, that was it! Appreciate your help, @zzq :smile:

2 Likes