# Building API Routes in Remix Supabase

> The best practices to write API routes in your Remix Supabase application

*Canonical: https://makerkit.dev/docs/remix-supabase/tutorials/api-routes*

---

Makerkit provides some utilities to reduce the boilerplate needed to write Remix API functions.
This section will teach you everything you need to know to write your API functions.

There are many ways to call an API route in Remix:
1. Using the Remix `useSubmit` or `useFetcher` hooks
2. Using the `fetch` API wrapper in Makerkit `useFetcher`
3. Using the `fetch` API directly

As a rule of thumb - it's best to use the `useSubmit` or `useFetcher` hooks whenever possible. It's the Remix way ad closer to the Remix philosophy.

With that said, there are times where the `useFetch` hook can be useful - where `useFetcher` and `useSubmit` may not be as convenient - for example - when you need to await the request using a Promise.

### Calling API functions from the client

API functions in Remix are defined using the special functions `loader` and
`action`.

These functions are defined in the routes.

These functions can be called in
various ways, for example using Remix's `useFetcher`, `useSubmit`, `<Form>`,
or can be called using simple `fetch` calls.

#### Defining global API functions

When the API functions are "standalone" and not tied to a specific page, it
makes sense to prefix them with the `resources` path.

For example, the following code calls the `action` function `owner` from the
file `resources.organizations.members.transfer-ownership.ts`.

##### Using Fetch + React Query
First, we define a function using the utility `useFetch` and React Query's `useMutation` hook.

```tsx
import useFetch from '~/core/hooks/use-fetch';
import { useMutation } from '@tanstack/react-query';

function useTransferOrganizationOwnership() {
  const transferOrganizationFetch = useFetch<{ membershipId: number }>(
    `/resources/organizations/members/transfer-ownership`,
    'PUT'
  );

  return useMutation((membershipId: number) => {
    return transferOrganizationFetch({ membershipId });
  });
}

export default useTransferOrganizationOwnership;
```

Then, we can define an `action` function in the file `resources.organizations.ts`.

```tsx
export async function action(args: ActionArgs) {
  const req = args.request;

  // logic to transfer ownership
}
```

#### Using useFetcher and useSubmit

Alternatively, we can use the Remix functions `useSubmit` or `useFetcher` to call the same endpoint.

This is more conventional to Remix, so you may prefer this latter approach.

```tsx {2,5}
function useTransferOrganizationOwnership() {
  const submit = useSubmit();

  return useCallback((membershipId: number) => {
    return submit({
      data: { membershipId: membershipId.toString() },
      action: `/resources/organizations/members/transfer-ownership`,
      method: `put`
    });
  });
}

export default useTransferOrganizationOwnership;
```

### Submitting Actions using JSON

It's important to notice that in this case the payload is sent as `FormData`, not as `JSON` - but you can specify the `encType` parameter to change this behavior.

```tsx
const fetcher = useFetcher();

<button onClick={() => {
  fetcher.submit(data, {
    method: 'POST',
    encType: 'application/json'
  })
}}>Submit</button>
```

```tsx
export async function action(args: ActionArgs) {
  const req = args.request;
  const body = await req.json();

  // ...
}
```

#### Sending the CSRF Token

When you use the hook `useFetch`, you don't need to worry about sending the CSRF token. The hook will automatically send the token.

Instead, if you use `fetch` directly, you need to send the CSRF token.
To retrieve the page's CSRF token, you can use the `useGetCsrfToken` hook:

```tsx
const getCsrfToken = useGetCsrfToken();
const csrfToken = getCsrfToken();

console.log(csrfToken) // token
```

You will need to send a header `x-csrf-token` with the value returned by `getCsrfToken()`.

### CSRF Token check

We must pass a `CSRF token` when using POST requests to prevent malicious attacks. To do so, we use the pipe `withCsrfToken`.

1. The CSRF token is generated when the page is server-rendered
2. The CSRF token is stored in a `meta` tag
3. The CSRF token is sent to an HTTP POST request automatically when using
the `useFetch` hook
4. This function will throw an error when the token is invalid

By using this function, **we ensure all the following functions will not be executed unless the token is valid**.

```tsx
export const action: ActionFunction = async ({ request }) => {
  await withCsrf(request);
}
```

### Sending CSRF Token using useFetcher and useSubmit

When using `useSubmit` and `useFetcher` - you must pass the CSRF token manually:

```tsx
const submit = useSubmit();
const csrfToken = useCsrfToken();

submit({ csrfToken }, {
  method: 'POST',
  encType: 'application/json'
})
```

We can then validate it using the `withCsrf` function:

```tsx
export const action: ActionFunction = async ({ request }) => {
  const body = await request.json();
  await withCsrf(request, () => body.csrfToken);
}
```

### API Logging

The boilerplate uses `Pino` for API logging, a lightweight logging utility for Node.js.

Logging is necessary to debug your applications and understand what happens when things don't behave as expected. You will find various instances of logging throughout the API, but we encourage you to log more if necessary.

We import the logging utility from `~/core/logger`. Typically, you can log every time you perform an action, both before and after. For example:

```tsx
async function myFunction(params: {
  organizationId: string;
  userId: string;
}) {
  logger.info(
    {
      organizationId: params.organizationId,
      userId: params.userId,
    },
    `Performing action...`
  );

  await performAction();

  logger.info(
    {
      organizationId: params.organizationId,
      userId: params.userId,
    },
    `Action successful`
  );
}
```

It's always important to add context to your logs: as you can see, we use the first parameter to add important information.
