# fetchJson

Fetch wrapper for calling app routes, rest-api or external resources.

| Param              | Type     | Default | Description                                                                                                                         |
| ------------------ | -------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| uri                | `String` |         | URI for resource                                                                                                                    |
| \[options]         | `Object` | `{}`    | Options object, two custom options rest is standard [fetch options](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options) |
| \[options.params]  | `Object` | `{}`    | Object with parameters to be appended to the URI                                                                                    |
| \[options.retries] | `Number` | `0`     | Number of retries if the request times out.                                                                                         |

**Returns**: `Promise<Object>` - Promise containing parsed JSON-data.\
**Throws** `Error` - Extended error object with custom properties for `status`, `aborted` and other JSON-data returned by the request.

URI:s starting with `/rest-api`, `/appresource`, `/edit-app-config` or a protocol, for example `https://` will be left as is.\
Other URI:s will be converted to match a route in the current app with `getRouteUri`.

Most common usage is getting data from a route in the current app.

```js
import { fetchJson } from '@soleil-se/app-util/client';


async function getItems() {
  const params = { query: 'foo', start: 0, num: 10 };
  const result = await fetchJson('/items', { params });
  console.log(result);
}
```

Posting form data.

> **Formdata & maxUploadSizeInMB**
>
> The `maxUploadSizeInMB` property must set to a **non-zero value** in the application manifest to expose the formdata payload within `req.params`.\
> This requirement applies universally, regardless of whether the payload includes files.

```js
import { fetchJson } from '@soleil-se/app-util/client';


async function postForm() {
  // Remember to set maxUploadSizeInMB !== 0 in manifest to allow formdata payloads
  const body = new FormData();
  body.append('name', 'Foo');
  body.append('mail', 'foo@bar.com');


  const result = await fetchJson('/create', { method: 'POST', body });
  console.log(result);
}
```

Specify number of retries if a request times out.

```js
import { fetchJson } from '@soleil-se/app-util/client';


async function getItems() {
  const params = { query: 'foo', start: 0, num: 10 };
  const result = await fetchJson('/items', { params, retries: 5 });
  console.log(result);
}
```

When searching on input you probably want to abort the ongoing request to avoid weird bugs when the first request might be completed after the subsequent one.\
Pass an [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) signal in options and abort the ongoing call.

```js
import { fetchJson } from '@soleil-se/app-util/client';


let controller;


async function onInput() {
  if(controller) controller.abort();
  controller = new AbortController();


  const params = { query: 'foo' };
  try {
    const result = await fetchJson('/search', { params, signal: controller.signal });
    console.log(result);
  } catch(e) {
    // Ignore aborts due to new search.
    if(e.aborted) return;
    // Handle error as usual.
  }
}
```

## TypeScript

Generic type for the result.

```ts
import { fetchJson } from '@soleil-se/app-util/client';


type Result = {
  hits: {
    id: string;
    title: string;
    description: string;
  }[];
  count: number;
};


async function search() {
  const params = { query: 'foo', start: 0, num: 10 };
  // Pass type to get proper typing for the result.
  const result = await fetchJson<Result>('/search', { params });
  console.log(result);
}
```