# Coding Patterns

## API

We're using [Redux Toolkit Query](https://redux-toolkit.js.org/rtk-query/overview) (RTK Query) to define our API endpoints and cache the response data.

### Defining Models

For each model defined on the back end, create a file named after the model in the directory `api`. At the top of the file, define the model on a global level and export it. Use the `Model` type constructor.

{% code title="api/person.ts" %}

```typescript
import { type Model } from "codeforlife/utils/api"

export type Person = Model<
    number, // the type of the "id" field
    {
        first_name: string
        last_name: string
        age: number
    }
>
```

{% endcode %}

In addition, the name of each model will need to be registered as a tag in `tagTypes`, at `api/index.ts`.

{% code title="api/index.ts" %}

```typescript
const api = createApi({
    tagTypes: ["Person"],
})
```

{% endcode %}

### Picking Model Fields

If you'd like to define an object containing a subset of a model's fields, you can use `Pick`.

```typescript
const person: Pick<Person, "id" | "age"> = { id: 1, age: 29 }
```

### Defining Results & Args

When defining the result (i.e. response body) and arg (i.e. request body) for an endpoint, you must define their types as a pair on a global level below the model's type and export them. The result's type should come first, immediately followed by the arg's type. The result & arg types should follow the naming convention `{endpoint_name}Result` and `{endpoint_name}Arg`. Each result & arg pair should be separated with one newline.

{% code title="api/person.ts" %}

```typescript
export type Person = Model<...>

// For endpoint "updatePersonName"
export type UpdatePersonNameResult = { id: number }
export type UpdatePersonNameArg = {
    id: number
    first_name: string
    last_name: string
}

// For endpoint "updatePersonAge"
export type UpdatePersonAgeResult = { id: number }
export type UpdatePersonAgeArg = {
    id: number
    age: number
}
```

{% endcode %}

If the fields of the result or arg are fields of the model, use the `Result` and `Arg` type constructors. These function similarly to [`Pick`](#picking-model-fields).

```typescript
import { type Arg, type Result } from "codeforlife/utils/api"

export type UpdatePersonNameResult = Result<Person>
export type UpdatePersonNameArg = Arg<
  Person, // model
  never, // required fields.
  "first_name" | "last_name" // optional fields
> & Pick<Person, "id"> // "id" cannot be a required or optional field
```

If the result and arg are for a [default action on the model-view-set](https://www.django-rest-framework.org/api-guide/viewsets/#viewset-actions), use the default type helper for the specific action. These function similarly to [`Pick`](#picking-model-fields).

```typescript
import { type UpdateArg, type UpdateResult } from "codeforlife/utils/api"

export type UpdatePersonResult = UpdateResult<Person>
export type UpdatePersonArg = UpdateArg<
  Person, // model
  never, // required fields
  "first_name" | "last_name" | "age" // optional fields
> // "id" is picked by default
```

If no data is included in the result or arg, set either to `null`.&#x20;

```typescript
export type DeletePersonResult = null
export type DeletePersonArg = Person["id"]
```

### Defining URLs

<mark style="color:red;">Documentation coming soon</mark>

### Defining Endpoints

Endpoints should be defined below the result & arg pairs. Endpoints should be injected into the base API, imported from `api/index.ts`.  The subset of API endpoints should follow the naming convention `{model_name}Api` and be exported as default. In addition, each hook auto-generated from the endpoints should be unpacked and exported as a constant below the default export.

{% code title="api/person.ts" %}

```typescript
import api from "."

export type Person = Model<...>

export type UpdatePersonResult = UpdateResult<Person>
export type UpdatePersonArg = UpdateArg<Person, ...>

const personApi = api.injectEndpoints({
    endpoints: build => ({
        updatePerson: build.mutation<UpdatePersonResult, UpdatePersonArg>({
            query: ({ id, ...body }) => ({
                url: `persons/${id}/`,
                method: "PATCH",
                body,
            })
        })
    })
})

export default personApi
export const { useUpdatePersonMutation } = personApi
```

{% endcode %}

### Defining a Retrieve Endpoint

When defining a retrieve endpoint, it's recommended to use the `RetrieveResult` and `RetrieveArg` type constructors. The endpoint should follow the naming convention `retrieve{model_name}`, be defined as a `query`, use the method `"GET"`, and name the query's arg as `id`. To build the URL, the `buildUrl` utility should be called, providing the model's detail URL and id as a URL parameter. The tag should be provided by calling the `tagData` utility and providing the model's name as an argument.

```typescript
import {
    type RetrieveArg,
    type RetrieveResult,
    buildUrl,
    tagData,
} from "codeforlife/utils/api"

export type RetrievePersonResult = RetrieveResult<Person, ...>
export type RetrievePersonArg = RetrieveArg<Person>

const personApi = api.injectEndpoints({
    endpoints: build => ({
        retrievePerson: build.query<RetrievePersonResult, RetrievePersonArg>({
            query: id => ({
                url: buildUrl(personUrls.detail, { url: { id } }),
                method: "GET",
            }),
            providesTags: tagData("Person")
        })
    })
})
```

### Defining a List Endpoint

When defining a list endpoint, it's recommended to use the `ListResult` and `ListArg` type constructors. The endpoint should follow the naming convention `list{model_name_plural}`, be defined as a `query`, use the method `"GET"`, and name the query's arg as `search`. To build the URL, the `buildUrl` utility should be called, providing the model's list URL and the search parameters. The tags should be provided by calling the `tagData` utility and providing the model's name as an argument and setting `includeListTag` to true.

```typescript
import {
    type ListArg,
    type ListResult,
    buildUrl,
    tagData,
} from "codeforlife/utils/api"

export type ListPersonsResult = ListResult<Person, ...>
export type ListPersonsArg = ListArg<...>

const personApi = api.injectEndpoints({
    endpoints: build => ({
        listPersons: build.query<ListPersonsResult, ListPersonsArg>({
            query: search => ({
                url: buildUrl(personUrls.list, { search }),
                method: "GET",
            }),
            providesTags: tagData("Person", { includeListTag: true })
        })
    })
})
```

### Defining a Create Endpoint

When defining a create endpoint, it's recommended to use the `CreateResult` and `CreateArg` type constructors. The endpoint should follow the naming convention `create{model_name}`, be defined as a `mutation`, use the method `"POST"`, and name the query's arg as `body`. The URL should be set as the model's list URL. The tags should be invalidated by calling the `tagData` utility and providing the model's name as an argument and setting `includeListTag` to true.

```typescript
import {
    type CreateArg,
    type CreateResult,
    tagData,
} from "codeforlife/utils/api"

export type CreatePersonResult = CreateResult<Person, ...>
export type CreatePersonArg = CreateArg<Person, ...>

const personApi = api.injectEndpoints({
    endpoints: build => ({
        createPerson: build.mutation<CreatePersonResult, CreatePersonArg>({
            query: body => ({
                url: personUrls.list,
                method: "POST",
                body,
            }),
            invalidatesTags: tagData("Person", { includeListTag: true })
        })
    })
})
```

### Defining a Update Endpoint

When defining a update endpoint, it's recommended to use the `UpdateResult` and `UpdateArg` type constructors. The endpoint should follow the naming convention `update{model_name}`, be defined as a `mutation`, use the method `"PATCH"`, and unpack the query's arg as `({ id, ...body })`. To build the URL, the `buildUrl` utility should be called, providing the model's detail URL and id as a URL parameter. The tags should be invalidated by calling the `tagData` utility and providing the model's name as an argument and setting `includeListTag` to true.

```typescript
import {
    type UpdateArg,
    type UpdateResult,
    buildUrl,
    tagData,
} from "codeforlife/utils/api"

export type UpdatePersonResult = UpdateResult<Person, ...>
export type UpdatePersonArg = UpdateArg<Person, ...>

const personApi = api.injectEndpoints({
    endpoints: build => ({
        updatePerson: build.mutation<UpdatePersonResult, UpdatePersonArg>({
            query: ({ id, ...body }) => ({
                url: buildUrl(personUrls.detail, { url: { id } }),
                method: "PATCH",
                body,
            }),
            invalidatesTags: tagData("Person", { includeListTag: true })
        })
    })
})
```

### Defining a Destroy Endpoint

When defining a destroy endpoint, it's recommended to use the `DestoryResult` and `DestroyArg` type constructors. The endpoint should follow the naming convention `destroy{model_name}`, be defined as a `mutation`, use the method `"DELETE"`, and name the query's arg as `id`. To build the URL, the `buildUrl` utility should be called, providing the model's detail URL and id as a URL parameter. The tags should be invalidated by calling the `tagData` utility and providing the model's name as an argument and setting `includeListTag` to true.

```typescript
import {
    type DestroyArg,
    type DestroyResult,
    buildUrl,
    tagData,
} from "codeforlife/utils/api"

export type DestroyPersonResult = DestroyResult
export type DestroyPersonArg = DestroyArg<Person>

const personApi = api.injectEndpoints({
    endpoints: build => ({
        destroyPerson: build.mutation<DestroyPersonResult, DestroyPersonArg>({
            query: id => ({
                url: buildUrl(personUrls.detail, { url: { id } }),
                method: "DELETE",
            }),
            invalidatesTags: tagData("Person", { includeListTag: true })
        })
    })
})
```

## Forms

<mark style="color:red;">Documentation coming soon</mark>

### Defining Fields

<mark style="color:red;">Documentation coming soon</mark>

### Submitting a Form

<mark style="color:red;">Documentation coming soon</mark>

## Schemas

<mark style="color:red;">Documentation coming soon</mark>

### Defining a Schema

<mark style="color:red;">Documentation coming soon</mark>

## Components

<mark style="color:red;">Documentation coming soon</mark>

### Defining a Component

<mark style="color:red;">Documentation coming soon</mark>

### Reusing Components

<mark style="color:red;">Documentation coming soon</mark>

## Pages

<mark style="color:red;">Documentation coming soon</mark>

### Defining Routes

<mark style="color:red;">Documentation coming soon</mark>

### Navigating to Routes

<mark style="color:red;">Documentation coming soon</mark>

### Link Components

<mark style="color:red;">Documentation coming soon</mark>

## Theme

<mark style="color:red;">Documentation coming soon</mark>

### Extending the Theme

<mark style="color:red;">Documentation coming soon</mark>

## Environment Variables

<mark style="color:red;">Documentation coming soon</mark>

### Setting Env. Variables

<mark style="color:red;">Documentation coming soon</mark>

### Importing Env. Variables

<mark style="color:red;">Documentation coming soon</mark>
