# Items as Files - What to Write for Each Kind of Item

> The files API Maker reads for every kind of item, in the Git repository and in the folder of the local client - the folder, the YAML, the settings and the code of custom APIs, hooks, events, WebSocket events, schedulers, migrations, process initializers, utility classes, test cases, schemas, settings, auth providers, groups, API users and instances - so an AI agent writes a new item which saves at once, in its language.

Source: https://docs.apimaker.dev/v4/docs/ai/items-as-files.html
Code: in 8 languages. The same page with the code of one language only: [TypeScript](https://docs.apimaker.dev/v4/docs/ai/items-as-files.typescript.md) · [Python](https://docs.apimaker.dev/v4/docs/ai/items-as-files.python.md) · [PHP](https://docs.apimaker.dev/v4/docs/ai/items-as-files.php.md) · [C#](https://docs.apimaker.dev/v4/docs/ai/items-as-files.csharp.md) · [Go](https://docs.apimaker.dev/v4/docs/ai/items-as-files.go.md) · [Rust](https://docs.apimaker.dev/v4/docs/ai/items-as-files.rust.md) · [Java](https://docs.apimaker.dev/v4/docs/ai/items-as-files.java.md) · [Kotlin](https://docs.apimaker.dev/v4/docs/ai/items-as-files.kotlin.md)

Every item of an account is a folder of files in its [Git repository](https://docs.apimaker.dev/v4/docs/Git/git.html) and in the folder of the [local client](https://docs.apimaker.dev/v4/docs/features/developer-tools.html#your-own-editor). An AI agent which adds a custom API, a hook or a scheduler writes these files : this page says which files each kind has, what is in each one, and what makes an item new. Pick your language in the tabs : the files of the code follow it.

> **In short**
>
> - `src/<kind>/<item>/` holds `<item>.yaml` (the item, without its code), `<item>.<ts|py|php|cs|go|rs|java|kt>` (its code : the extension is its language) and, for a few kinds, `<item>.config.ts` (its settings, TypeScript in every language).
> - A new item has no `_id` and no `guid` : API Maker gives them. Code next to the YAML is read when the YAML lists `versions` with one version `"-1"`.
> - Run the local client (`npm run start`) : a saved folder is checked and saved the way the admin panel saves it, and `✗ Not applied on server : …` says what is wrong.

## The whole tree

A repository with one item of each kind, its code in the language of the tab :

**TypeScript**

**A repository, the code in TypeScript**

```text
AGENTS.md                                                            the guide for AI agents, written by API Maker
package.json  tsconfig.json  am-types/types.ts  localClient.js       written by API Maker
src/
  custom_apis/shippingCost/
    shippingCost.yaml                                                name, path, versions and their hooks (no code)
    shippingCost.config.ts                                           path, method, access, schemas : TypeScript in every language
    shippingCost.ts                                                  the code
    pre-hooks/check_weight.ts                                        a hook of the API, named in versions[].preHooks
  collection_hooks/Row_scope-orders/                                 one folder per hook
    Row_scope-orders.yaml                                            its table, pre or post, its order
    Row_scope-orders.ts
  events/Order_placed/
    Order_placed.yaml                                                its listeners, its triggers
    listeners/Notify_the_shop/Notify_the_shop.ts
  web_socket_events/defaultorders-updated/                           the event default/orders-updated
    defaultorders-updated.yaml
    defaultorders-updated.ts                                         who may subscribe
  schedulers/Clean_old_carts/
    Clean_old_carts.yaml                                             intervals, isActive
    Clean_old_carts.ts
  database_migrations/Add_description/
    Add_description.yaml                                             label, order, active
    Add_description.ts
  process_initializers/Load_rates/
    Load_rates.yaml                                                  order
    Load_rates.ts
  utility_classes/utils---Money/                                     the class Money of the folder utils/
    Money.yaml
    Money.ts
  test_cases/Shipping_cost/
    Shipping_cost.yaml                                               what it tests, its mocks
    Shipping_cost.ts                                                 the tests
  schemas/<guid>/                                                    a table : its YAML and <guid>.config.ts
  instance_api_settings/orders/                                      settings of a table : orders.yaml, orders.config.ts
  system_api_settings/HASH_DATA/                                     settings of a system API : HASH_DATA.yaml, HASH_DATA.config.ts
  auth_providers/default_db_login/
    default_db_login.yaml
    default_db_login.ts                                              what the provider is : TypeScript in every language
    default_db_login.fg.ts                                           its fields generator
  groups/Shop_app/Shop_app.yaml
  api_users/shop_app/shop_app.yaml
  instances/mongodb/mongodb.yaml
  i18ns/hindi/hindi.yaml  hindi.ts
  admin_user/data.yaml                                               sandboxes : runtimes, versions, packages
  ssr/<website>/                                                     an SSR website, in TypeScript
  types/db-interfaces.d.ts  api_security_report/  auto_increments/   written by API Maker
```

**Python**

**A repository, the code in Python**

```text
AGENTS.md                                                            the guide for AI agents, written by API Maker
package.json  tsconfig.json  am-types/types.ts  localClient.js       written by API Maker
pyproject.toml                                                       the project of the language, written by API Maker
src/
  custom_apis/shippingCost/
    shippingCost.yaml                                                name, path, versions and their hooks (no code)
    shippingCost.config.ts                                           path, method, access, schemas : TypeScript in every language
    shippingCost.py                                                  the code
    pre-hooks/check_weight.py                                        a hook of the API, named in versions[].preHooks
  collection_hooks/Row_scope-orders/                                 one folder per hook
    Row_scope-orders.yaml                                            its table, pre or post, its order
    Row_scope-orders.py
  events/Order_placed/
    Order_placed.yaml                                                its listeners, its triggers
    listeners/Notify_the_shop/Notify_the_shop.py
  web_socket_events/defaultorders-updated/                           the event default/orders-updated
    defaultorders-updated.yaml
    defaultorders-updated.py                                         who may subscribe
  schedulers/Clean_old_carts/
    Clean_old_carts.yaml                                             intervals, isActive
    Clean_old_carts.py
  database_migrations/Add_description/
    Add_description.yaml                                             label, order, active
    Add_description.py
  process_initializers/Load_rates/
    Load_rates.yaml                                                  order
    Load_rates.py
  utility_classes/utils---Money/                                     the class Money of the folder utils/
    Money.yaml
    Money.py
  test_cases/Shipping_cost/
    Shipping_cost.yaml                                               what it tests, its mocks
    Shipping_cost.py                                                 the tests
  schemas/<guid>/                                                    a table : its YAML and <guid>.config.ts
  instance_api_settings/orders/                                      settings of a table : orders.yaml, orders.config.ts
  system_api_settings/HASH_DATA/                                     settings of a system API : HASH_DATA.yaml, HASH_DATA.config.ts
  auth_providers/default_db_login/
    default_db_login.yaml
    default_db_login.ts                                              what the provider is : TypeScript in every language
    default_db_login.fg.py                                           its fields generator
  groups/Shop_app/Shop_app.yaml
  api_users/shop_app/shop_app.yaml
  instances/mongodb/mongodb.yaml
  i18ns/hindi/hindi.yaml  hindi.ts
  admin_user/data.yaml                                               sandboxes : runtimes, versions, packages
  ssr/<website>/                                                     an SSR website, in TypeScript
  types/db-interfaces.d.ts  api_security_report/  auto_increments/   written by API Maker
```

**PHP**

**A repository, the code in PHP**

```text
AGENTS.md                                                            the guide for AI agents, written by API Maker
package.json  tsconfig.json  am-types/types.ts  localClient.js       written by API Maker
composer.json                                                        the project of the language, written by API Maker
src/
  custom_apis/shippingCost/
    shippingCost.yaml                                                name, path, versions and their hooks (no code)
    shippingCost.config.ts                                           path, method, access, schemas : TypeScript in every language
    shippingCost.php                                                 the code
    pre-hooks/check_weight.php                                       a hook of the API, named in versions[].preHooks
  collection_hooks/Row_scope-orders/                                 one folder per hook
    Row_scope-orders.yaml                                            its table, pre or post, its order
    Row_scope-orders.php
  events/Order_placed/
    Order_placed.yaml                                                its listeners, its triggers
    listeners/Notify_the_shop/Notify_the_shop.php
  web_socket_events/defaultorders-updated/                           the event default/orders-updated
    defaultorders-updated.yaml
    defaultorders-updated.php                                        who may subscribe
  schedulers/Clean_old_carts/
    Clean_old_carts.yaml                                             intervals, isActive
    Clean_old_carts.php
  database_migrations/Add_description/
    Add_description.yaml                                             label, order, active
    Add_description.php
  process_initializers/Load_rates/
    Load_rates.yaml                                                  order
    Load_rates.php
  utility_classes/utils---Money/                                     the class Money of the folder utils/
    Money.yaml
    Money.php
  test_cases/Shipping_cost/
    Shipping_cost.yaml                                               what it tests, its mocks
    Shipping_cost.php                                                the tests
  schemas/<guid>/                                                    a table : its YAML and <guid>.config.ts
  instance_api_settings/orders/                                      settings of a table : orders.yaml, orders.config.ts
  system_api_settings/HASH_DATA/                                     settings of a system API : HASH_DATA.yaml, HASH_DATA.config.ts
  auth_providers/default_db_login/
    default_db_login.yaml
    default_db_login.ts                                              what the provider is : TypeScript in every language
    default_db_login.fg.php                                          its fields generator
  groups/Shop_app/Shop_app.yaml
  api_users/shop_app/shop_app.yaml
  instances/mongodb/mongodb.yaml
  i18ns/hindi/hindi.yaml  hindi.ts
  admin_user/data.yaml                                               sandboxes : runtimes, versions, packages
  ssr/<website>/                                                     an SSR website, in TypeScript
  types/db-interfaces.d.ts  api_security_report/  auto_increments/   written by API Maker
```

**C#**

**A repository, the code in C#**

```text
AGENTS.md                                                            the guide for AI agents, written by API Maker
package.json  tsconfig.json  am-types/types.ts  localClient.js       written by API Maker
ApiMaker.csproj  am/dotnet/                                          the project of the language, written by API Maker
src/
  custom_apis/shippingCost/
    shippingCost.yaml                                                name, path, versions and their hooks (no code)
    shippingCost.config.ts                                           path, method, access, schemas : TypeScript in every language
    shippingCost.cs                                                  the code
    pre-hooks/check_weight.cs                                        a hook of the API, named in versions[].preHooks
  collection_hooks/Row_scope-orders/                                 one folder per hook
    Row_scope-orders.yaml                                            its table, pre or post, its order
    Row_scope-orders.cs
  events/Order_placed/
    Order_placed.yaml                                                its listeners, its triggers
    listeners/Notify_the_shop/Notify_the_shop.cs
  web_socket_events/defaultorders-updated/                           the event default/orders-updated
    defaultorders-updated.yaml
    defaultorders-updated.cs                                         who may subscribe
  schedulers/Clean_old_carts/
    Clean_old_carts.yaml                                             intervals, isActive
    Clean_old_carts.cs
  database_migrations/Add_description/
    Add_description.yaml                                             label, order, active
    Add_description.cs
  process_initializers/Load_rates/
    Load_rates.yaml                                                  order
    Load_rates.cs
  utility_classes/utils---Money/                                     the class Money of the folder utils/
    Money.yaml
    Money.cs
  test_cases/Shipping_cost/
    Shipping_cost.yaml                                               what it tests, its mocks
    Shipping_cost.cs                                                 the tests
  schemas/<guid>/                                                    a table : its YAML and <guid>.config.ts
  instance_api_settings/orders/                                      settings of a table : orders.yaml, orders.config.ts
  system_api_settings/HASH_DATA/                                     settings of a system API : HASH_DATA.yaml, HASH_DATA.config.ts
  auth_providers/default_db_login/
    default_db_login.yaml
    default_db_login.ts                                              what the provider is : TypeScript in every language
    default_db_login.fg.cs                                           its fields generator
  groups/Shop_app/Shop_app.yaml
  api_users/shop_app/shop_app.yaml
  instances/mongodb/mongodb.yaml
  i18ns/hindi/hindi.yaml  hindi.ts
  admin_user/data.yaml                                               sandboxes : runtimes, versions, packages
  ssr/<website>/                                                     an SSR website, in TypeScript
  types/db-interfaces.d.ts  api_security_report/  auto_increments/   written by API Maker
```

**Go**

**A repository, the code in Go**

```text
AGENTS.md                                                            the guide for AI agents, written by API Maker
package.json  tsconfig.json  am-types/types.ts  localClient.js       written by API Maker
go.mod  am/am.go  am/methods.go                                      the project of the language, written by API Maker
src/
  custom_apis/shippingCost/
    shippingCost.yaml                                                name, path, versions and their hooks (no code)
    shippingCost.config.ts                                           path, method, access, schemas : TypeScript in every language
    shippingCost.go                                                  the code
    pre-hooks/check_weight/check_weight.go                           a hook of the API, named in versions[].preHooks
  collection_hooks/Row_scope-orders/                                 one folder per hook
    Row_scope-orders.yaml                                            its table, pre or post, its order
    Row_scope-orders.go
  events/Order_placed/
    Order_placed.yaml                                                its listeners, its triggers
    listeners/Notify_the_shop/Notify_the_shop.go
  web_socket_events/defaultorders-updated/                           the event default/orders-updated
    defaultorders-updated.yaml
    defaultorders-updated.go                                         who may subscribe
  schedulers/Clean_old_carts/
    Clean_old_carts.yaml                                             intervals, isActive
    Clean_old_carts.go
  database_migrations/Add_description/
    Add_description.yaml                                             label, order, active
    Add_description.go
  process_initializers/Load_rates/
    Load_rates.yaml                                                  order
    Load_rates.go
  utility_classes/utils---Money/                                     the class Money of the folder utils/
    Money.yaml
    Money.go
  test_cases/Shipping_cost/
    Shipping_cost.yaml                                               what it tests, its mocks
    Shipping_cost.go                                                 the tests
  schemas/<guid>/                                                    a table : its YAML and <guid>.config.ts
  instance_api_settings/orders/                                      settings of a table : orders.yaml, orders.config.ts
  system_api_settings/HASH_DATA/                                     settings of a system API : HASH_DATA.yaml, HASH_DATA.config.ts
  auth_providers/default_db_login/
    default_db_login.yaml
    default_db_login.ts                                              what the provider is : TypeScript in every language
    default_db_login.fg/default_db_login.fg.go                       its fields generator
  groups/Shop_app/Shop_app.yaml
  api_users/shop_app/shop_app.yaml
  instances/mongodb/mongodb.yaml
  i18ns/hindi/hindi.yaml  hindi.ts
  admin_user/data.yaml                                               sandboxes : runtimes, versions, packages
  ssr/<website>/                                                     an SSR website, in TypeScript
  types/db-interfaces.d.ts  api_security_report/  auto_increments/   written by API Maker
```

**Rust**

**A repository, the code in Rust**

```text
AGENTS.md                                                            the guide for AI agents, written by API Maker
package.json  tsconfig.json  am-types/types.ts  localClient.js       written by API Maker
Cargo.toml  am/rust/ (project.rs names every .rs file as a module)   the project of the language, written by API Maker
src/
  custom_apis/shippingCost/
    shippingCost.yaml                                                name, path, versions and their hooks (no code)
    shippingCost.config.ts                                           path, method, access, schemas : TypeScript in every language
    shippingCost.rs                                                  the code
    pre-hooks/check_weight.rs                                        a hook of the API, named in versions[].preHooks
  collection_hooks/Row_scope-orders/                                 one folder per hook
    Row_scope-orders.yaml                                            its table, pre or post, its order
    Row_scope-orders.rs
  events/Order_placed/
    Order_placed.yaml                                                its listeners, its triggers
    listeners/Notify_the_shop/Notify_the_shop.rs
  web_socket_events/defaultorders-updated/                           the event default/orders-updated
    defaultorders-updated.yaml
    defaultorders-updated.rs                                         who may subscribe
  schedulers/Clean_old_carts/
    Clean_old_carts.yaml                                             intervals, isActive
    Clean_old_carts.rs
  database_migrations/Add_description/
    Add_description.yaml                                             label, order, active
    Add_description.rs
  process_initializers/Load_rates/
    Load_rates.yaml                                                  order
    Load_rates.rs
  utility_classes/utils---Money/                                     the class Money of the folder utils/
    Money.yaml
    Money.rs
  test_cases/Shipping_cost/
    Shipping_cost.yaml                                               what it tests, its mocks
    Shipping_cost.rs                                                 the tests
  schemas/<guid>/                                                    a table : its YAML and <guid>.config.ts
  instance_api_settings/orders/                                      settings of a table : orders.yaml, orders.config.ts
  system_api_settings/HASH_DATA/                                     settings of a system API : HASH_DATA.yaml, HASH_DATA.config.ts
  auth_providers/default_db_login/
    default_db_login.yaml
    default_db_login.ts                                              what the provider is : TypeScript in every language
    default_db_login.fg.rs                                           its fields generator
  groups/Shop_app/Shop_app.yaml
  api_users/shop_app/shop_app.yaml
  instances/mongodb/mongodb.yaml
  i18ns/hindi/hindi.yaml  hindi.ts
  admin_user/data.yaml                                               sandboxes : runtimes, versions, packages
  ssr/<website>/                                                     an SSR website, in TypeScript
  types/db-interfaces.d.ts  api_security_report/  auto_increments/   written by API Maker
```

**Java**

**A repository, the code in Java**

```text
AGENTS.md                                                            the guide for AI agents, written by API Maker
package.json  tsconfig.json  am-types/types.ts  localClient.js       written by API Maker
settings.gradle.kts  am/java/build.gradle.kts  am/jvm/am/            the project of the language, written by API Maker
src/
  custom_apis/shippingCost/
    shippingCost.yaml                                                name, path, versions and their hooks (no code)
    shippingCost.config.ts                                           path, method, access, schemas : TypeScript in every language
    shippingCost.java                                                the code
    pre-hooks/check_weight/check_weight.java                         a hook of the API, named in versions[].preHooks
  collection_hooks/Row_scope-orders/                                 one folder per hook
    Row_scope-orders.yaml                                            its table, pre or post, its order
    Row_scope-orders.java
  events/Order_placed/
    Order_placed.yaml                                                its listeners, its triggers
    listeners/Notify_the_shop/Notify_the_shop.java
  web_socket_events/defaultorders-updated/                           the event default/orders-updated
    defaultorders-updated.yaml
    defaultorders-updated.java                                       who may subscribe
  schedulers/Clean_old_carts/
    Clean_old_carts.yaml                                             intervals, isActive
    Clean_old_carts.java
  database_migrations/Add_description/
    Add_description.yaml                                             label, order, active
    Add_description.java
  process_initializers/Load_rates/
    Load_rates.yaml                                                  order
    Load_rates.java
  utility_classes/utils---Money/                                     the class Money of the folder utils/
    Money.yaml
    Money.java
  test_cases/Shipping_cost/
    Shipping_cost.yaml                                               what it tests, its mocks
    Shipping_cost.java                                               the tests
  schemas/<guid>/                                                    a table : its YAML and <guid>.config.ts
  instance_api_settings/orders/                                      settings of a table : orders.yaml, orders.config.ts
  system_api_settings/HASH_DATA/                                     settings of a system API : HASH_DATA.yaml, HASH_DATA.config.ts
  auth_providers/default_db_login/
    default_db_login.yaml
    default_db_login.ts                                              what the provider is : TypeScript in every language
    default_db_login.fg/default_db_login.fg.java                     its fields generator
  groups/Shop_app/Shop_app.yaml
  api_users/shop_app/shop_app.yaml
  instances/mongodb/mongodb.yaml
  i18ns/hindi/hindi.yaml  hindi.ts
  admin_user/data.yaml                                               sandboxes : runtimes, versions, packages
  ssr/<website>/                                                     an SSR website, in TypeScript
  types/db-interfaces.d.ts  api_security_report/  auto_increments/   written by API Maker
```

**Kotlin**

**A repository, the code in Kotlin**

```text
AGENTS.md                                                            the guide for AI agents, written by API Maker
package.json  tsconfig.json  am-types/types.ts  localClient.js       written by API Maker
settings.gradle.kts  am/kotlin/build.gradle.kts  am/jvm/am/          the project of the language, written by API Maker
src/
  custom_apis/shippingCost/
    shippingCost.yaml                                                name, path, versions and their hooks (no code)
    shippingCost.config.ts                                           path, method, access, schemas : TypeScript in every language
    shippingCost.kt                                                  the code
    pre-hooks/check_weight/check_weight.kt                           a hook of the API, named in versions[].preHooks
  collection_hooks/Row_scope-orders/                                 one folder per hook
    Row_scope-orders.yaml                                            its table, pre or post, its order
    Row_scope-orders.kt
  events/Order_placed/
    Order_placed.yaml                                                its listeners, its triggers
    listeners/Notify_the_shop/Notify_the_shop.kt
  web_socket_events/defaultorders-updated/                           the event default/orders-updated
    defaultorders-updated.yaml
    defaultorders-updated.kt                                         who may subscribe
  schedulers/Clean_old_carts/
    Clean_old_carts.yaml                                             intervals, isActive
    Clean_old_carts.kt
  database_migrations/Add_description/
    Add_description.yaml                                             label, order, active
    Add_description.kt
  process_initializers/Load_rates/
    Load_rates.yaml                                                  order
    Load_rates.kt
  utility_classes/utils---Money/                                     the class Money of the folder utils/
    Money.yaml
    Money.kt
  test_cases/Shipping_cost/
    Shipping_cost.yaml                                               what it tests, its mocks
    Shipping_cost.kt                                                 the tests
  schemas/<guid>/                                                    a table : its YAML and <guid>.config.ts
  instance_api_settings/orders/                                      settings of a table : orders.yaml, orders.config.ts
  system_api_settings/HASH_DATA/                                     settings of a system API : HASH_DATA.yaml, HASH_DATA.config.ts
  auth_providers/default_db_login/
    default_db_login.yaml
    default_db_login.ts                                              what the provider is : TypeScript in every language
    default_db_login.fg/default_db_login.fg.kt                       its fields generator
  groups/Shop_app/Shop_app.yaml
  api_users/shop_app/shop_app.yaml
  instances/mongodb/mongodb.yaml
  i18ns/hindi/hindi.yaml  hindi.ts
  admin_user/data.yaml                                               sandboxes : runtimes, versions, packages
  ssr/<website>/                                                     an SSR website, in TypeScript
  types/db-interfaces.d.ts  api_security_report/  auto_increments/   written by API Maker
```

## The rules of every item

### Folders and file names

| Rule | Example |
|---|---|
| One folder per kind in `src`, the kind in small letters with `_` | `src/custom_apis`, `src/web_socket_events` |
| One folder per item : its name, where what is no letter, digit, `.`, `-` or `_` is written `_` | the scheduler `Daily report` is `src/schedulers/Daily_report/` |
| ` - ` and ` -- ` between the parts of a name lose their spaces | the hook `Row scope - orders` is `Row_scope-orders/` |
| The files of an item are named as its folder | `Daily_report/Daily_report.yaml`, `Daily_report/Daily_report.py` |
| The name itself is `name:` in the YAML : nothing is read back from the folder | `name: Daily report` |

- A utility class is its folder with `-` for `/`, then `--`, then its name : the class `Money` of the folder `utils/` is `src/utility_classes/utils---Money/` (of `cloud/db/` : `cloud-db---Money/`), its YAML `Money.yaml`, its code `Money.<extension>`.
- A schema is the folder of its guid when API Maker writes it. A new one can have any folder (the name of its table) : the next commit writes it under its guid.
- The settings of a system API are the folder of the API : `src/system_api_settings/GET_TOKEN/`. A WebSocket event is its name without `/` and `@` : `default/orders-updated` is `defaultorders-updated/`.
- Two hooks of one name : the second one gets its table, then a number (`Check_access`, `Check_access--orders`). See [Hooks in Git](https://docs.apimaker.dev/v4/docs/Git/git.html#hooks).

### The YAML

- The item as the admin panel saves it, without its code and without the keys of its settings file. Write the keys this page shows for its kind ; API Maker writes the others back with their defaults.
- **A new item** : no `_id` and no `guid`. A guid copied from another item replaces that item : never copy a folder with its guid.
- The language of the code is the extension of its file : `orders.py` next to `orders.yaml` is Python. `codeLanguage: python` in the YAML says the same, and wins over the extension.

### The code

- One function `main` in each file, which gets `g` : what it returns is the answer. The code blocks of this page show it in each language ; the [global object g](https://docs.apimaker.dev/v4/docs/pre-defined-terms/global-object-g.html) has every call.
- An API and all its hooks are one language. In Go, Java and Kotlin, a piece of code which shares a folder with other code (the hooks of a custom API, the functions of an auth provider) is in a folder named as its file : `pre-hooks/check_weight/check_weight.go`.
- A file of Go never ends as Go reads names : `orders_test` is written `orders_test_.go`.

### Versions

- `versions` in the YAML is the list of the versions of the code. One version `"-1"` (named `Original`) : the code is `<item>.<extension>`, next to the YAML.
- Several versions : `versions/<version>/<Name_of_the_version>.<extension>`, its hooks in `versions/<version>/pre-hooks/` and `post-hooks/`, and `isActive: true` on the version which runs.

### The settings

- `<item>.config.ts` for a custom API, a schema and the settings of a database, a table, an API or a system API ; `<item>.ts` for an auth provider and for texts (i18n). They are **TypeScript in every language** : API Maker runs them itself when it saves. Each exports one object : `module.exports = customApi;`.
- The settings file wins over the YAML for the keys it has : the `name`, `path`, `requestMethod`, `apiAccessType`… of a custom API, the columns of an auth provider. Keep `name` the same in both : it names the folder.

### Files API Maker writes : never edit them

`AGENTS.md` (the block of API Maker), `am-types/types.ts`, `src/types/db-interfaces.d.ts`, `localClient.js`, `package.json`, `tsconfig.json`, `pyproject.toml`, `composer.json`, `go.mod`, `Cargo.toml`, `ApiMaker.csproj`, `settings.gradle.kts`, `am/`, `src/api_security_report/`, `src/auto_increments/`. A package goes into `src/admin_user/data.yaml` : see [Packages](https://docs.apimaker.dev/v4/docs/ai/items-as-files.html#packages).

### Check a change

- With the local client : `npm run start` sends each saved folder, and the server answers `✓ Saved on server` or `✗ Not applied on server : <errors>`. Deleting the folder of an item deletes the item.
- `npm run test` runs the [test cases](https://docs.apimaker.dev/v4/docs/test-cases/test-cases.html) of the account with the code of the folder.
- The compiler of the language, offline :

**TypeScript**

```bash
npx tsc --noEmit
```

**Python**

```bash
python -m compileall src
```

**PHP**

```bash
php -l src/custom_apis/shippingCost/shippingCost.php
```

**C#**

```bash
dotnet build
```

**Go**

```bash
go build ./... && go vet ./...
```

**Rust**

```bash
cargo check
```

**Java**

```bash
gradle check
```

**Kotlin**

```bash
gradle check
```

## Every kind at a glance

| Kind | Folder in `src` | Files | Code |
|---|---|---|---|
| [Custom API](https://docs.apimaker.dev/v4/docs/ai/items-as-files.html#custom-api) | `custom_apis/` | YAML, `.config.ts`, code, `.docs.json`, `pre-hooks/`, `post-hooks/`, `versions/` | `main`, the answer |
| [Hook of an instance, a database, a table, an API, a system API](https://docs.apimaker.dev/v4/docs/ai/items-as-files.html#hooks) | `instance_hooks/`, `database_hooks/`, `collection_hooks/`, `instance_api_hooks/`, `system_api_hooks/` | YAML, code : one folder per hook | `main`, before or after the APIs |
| [Event](https://docs.apimaker.dev/v4/docs/ai/items-as-files.html#event) | `events/` | YAML, `listeners/<listener>/<listener>.<ext>` | one `main` per listener |
| [WebSocket event](https://docs.apimaker.dev/v4/docs/ai/items-as-files.html#web-socket-event) | `web_socket_events/` | YAML, code | who may subscribe |
| [Scheduler](https://docs.apimaker.dev/v4/docs/ai/items-as-files.html#scheduler) | `schedulers/` | YAML, code | the job |
| [Database migration](https://docs.apimaker.dev/v4/docs/ai/items-as-files.html#database-migration) | `database_migrations/` | YAML, code | runs once per environment, after a pull |
| [Process initializer](https://docs.apimaker.dev/v4/docs/ai/items-as-files.html#process-initializer) | `process_initializers/` | YAML, code | runs once per runner, when it starts |
| [Utility class](https://docs.apimaker.dev/v4/docs/ai/items-as-files.html#utility-class) | `utility_classes/` | YAML, code | shared by the code of its language |
| [Test case](https://docs.apimaker.dev/v4/docs/ai/items-as-files.html#test-case) | `test_cases/` | YAML, code | the tests |
| [Schema](https://docs.apimaker.dev/v4/docs/ai/items-as-files.html#schema) | `schemas/` | YAML, `.config.ts` | none : TypeScript settings |
| [Settings of a database, a table, an API](https://docs.apimaker.dev/v4/docs/ai/items-as-files.html#settings) | `instance_api_settings/` | YAML, `.config.ts` | none |
| [Settings of a system API](https://docs.apimaker.dev/v4/docs/ai/items-as-files.html#system-api-settings) | `system_api_settings/` | YAML, `.config.ts` | none |
| [Auth provider](https://docs.apimaker.dev/v4/docs/ai/items-as-files.html#auth-provider) | `auth_providers/` | YAML, `.ts`, `.fg.<ext>`, `.tg.<ext>`, `.tv.<ext>` | the functions of the provider |
| [Group, API user, instance](https://docs.apimaker.dev/v4/docs/ai/items-as-files.html#access) | `groups/`, `api_users/`, `instances/` | YAML | none |
| [Texts, log profiles, saved requests](https://docs.apimaker.dev/v4/docs/ai/items-as-files.html#other-kinds) | `i18ns/`, `log_profiles/`, `api_testing_states/` | YAML, and `.ts` for texts | none |
| [Packages and sandboxes](https://docs.apimaker.dev/v4/docs/ai/items-as-files.html#packages) | `admin_user/` | `data.yaml`, `sandbox/<family>/Dockerfile` | none |
| [SSR website](https://docs.apimaker.dev/v4/docs/ai/items-as-files.html#ssr-website) | `ssr/<website>/` | a project of TypeScript | pages |

## Custom API

A function behind a path and a method : see [Custom APIs](https://docs.apimaker.dev/v4/docs/apis-all/custom-apis/user-created-custom-api.html). The custom API `shippingCost` gives the cost of a parcel from the rates table ; the [test case](https://docs.apimaker.dev/v4/docs/ai/items-as-files.html#test-case) below tests it.

**src/custom_apis/shippingCost/shippingCost.yaml**

```yaml
name: shippingCost          # the same name as in shippingCost.config.ts : it names the folder and the files
path: /shipping-cost        # as in the settings file, which wins
versions:
  - name: Original
    version: "-1"           # "-1" : the code is the file next to this one, shippingCost.<extension>
    isActive: true
    preHooks: []            # the hooks of the API, see below
    postHooks: []
```

**src/custom_apis/shippingCost/shippingCost.config.ts**

```typescript
import * as T from 'types';

// The settings of the API : TypeScript, whatever the language of its code.
const customApi: T.ICustomApiSettingsTypes = {
    name: 'shippingCost',
    path: '/shipping-cost',                         // POST /api/custom-api/<user path>/shipping-cost
    requestMethod: T.ERequestMethod.POST,
    apiAccessType: T.EAPIAccessType.TOKEN_ACCESS,   // IS_PUBLIC : no token ; NO_ACCESS : from code only
    errorList: ['Too heavy to ship.'],              // the messages it throws, for i18n and its docs
    reqBodySchema: {                                // checked before the code runs
        weight: { __type: T.EType.number, validations: { required: true, min: 0 } },
    },
};
module.exports = customApi;
```

**TypeScript**

**src/custom_apis/shippingCost/shippingCost.ts**

```typescript
import * as T from 'types';

async function main(g: T.IAMGlobal) {
    const weight = Number(g.req.body.weight || 0);
    if (weight > 30) throw [{ code: 422, message: 'Too heavy to ship.' }];
    const rates = await g.sys.db.getAll({ instance: 'mysql_8', database: 'shop', collection: 'rates' });
    return { cost: weight * rates[0].per_kg };
}
module.exports = main;
```

**Python**

**src/custom_apis/shippingCost/shippingCost.py**

```python
from am import Errors, G


async def main(g: G):
    weight = float(g.req.body.weight or 0)
    if weight > 30:
        raise Errors('Too heavy to ship.', code=422)
    rates = await g.sys.db.get_all('mysql_8', 'shop', 'rates')
    return {'cost': weight * rates[0]['per_kg']}
```

**PHP**

**src/custom_apis/shippingCost/shippingCost.php**

```php
<?php
function main(AmG $g)
{
    $weight = (float) ($g->req->body->weight ?? 0);
    if ($weight > 30) {
        throw new AmErrors('Too heavy to ship.', 422);
    }
    $rates = $g->sys->db->getAll('mysql_8', 'shop', 'rates');
    return ['cost' => $weight * $rates[0]->per_kg];
}
```

**C#**

**src/custom_apis/shippingCost/shippingCost.cs**

```csharp
file static class Api
{
    public static async Task<object?> Main(G g)
    {
        var weight = g.Req.Body.GetDouble("weight");
        if (weight > 30) throw new AmErrors("Too heavy to ship.", 422);
        var rates = await g.Sys.Db.GetAll(new GetAll("mysql_8", "shop", "rates"));
        return new { cost = weight * rates[0].GetDouble("per_kg") };
    }
}
```

**Go**

**src/custom_apis/shippingCost/shippingCost.go**

```go
package code

import "apimaker/am"

func Main(g *am.G) (any, error) {
	weight := g.Req.Body().Float("weight")
	if weight > 30 {
		return nil, am.Fail(422, "Too heavy to ship.")
	}
	rates, err := g.Sys.Db.GetAll(am.GetAll{Instance: "mysql_8", Database: "shop", Collection: "rates"})
	if err != nil {
		return nil, err
	}
	return am.M{"cost": weight * rates[0].Float("per_kg")}, nil
}
```

**Rust**

**src/custom_apis/shippingCost/shippingCost.rs**

```rust
use am::{Errors, G, GetAll, Value, json};

pub fn main(g: &mut G) -> Result<Value, Errors> {
    let weight = g.req.body()["weight"].as_f64().unwrap_or(0.0);
    if weight > 30.0 {
        return Err(am::fail(422, "Too heavy to ship."));
    }
    let rates = g.sys.db.get_all(GetAll::new("mysql_8", "shop", "rates"))?;
    let per_kg = rates.first().and_then(|rate| rate["per_kg"].as_f64()).unwrap_or(0.0);
    Ok(json!({ "cost": weight * per_kg }))
}
```

**Java**

**src/custom_apis/shippingCost/shippingCost.java**

```java
import am.AmErrors;
import am.G;
import am.GetAll;
import am.JsonObject;
import java.util.List;
import java.util.Map;

class Api {
    static Object main(G g) {
        double weight = g.req.getBody().getDouble("weight");
        if (weight > 30) throw new AmErrors("Too heavy to ship.", 422);
        List<JsonObject> rates = g.sys.db.getAll(new GetAll("mysql_8", "shop", "rates"));
        return Map.of("cost", weight * rates.get(0).getDouble("per_kg"));
    }
}
```

**Kotlin**

**src/custom_apis/shippingCost/shippingCost.kt**

```kotlin
import am.AmErrors
import am.G
import am.GetAll

fun main(g: G): Any? {
    val weight = g.req.body.getDouble("weight")
    if (weight > 30) throw AmErrors("Too heavy to ship.", 422)
    val rates = g.sys.db.getAll(GetAll("mysql_8", "shop", "rates"))
    return mapOf("cost" to weight * rates[0].getDouble("per_kg"))
}
```

- **Its hooks** are named in the version they run with, and their code is in `pre-hooks/` and `post-hooks/` of the folder of the API : `preHooks: [{ name: check_weight, isActive: true }]` is `pre-hooks/check_weight.<extension>` (in Go, Java and Kotlin `pre-hooks/check_weight/check_weight.<extension>`). A hook of an API is in the language of the API ; `groupNames` on a hook runs it for the API users of those groups only.
- **Its docs** (the Docs tab) are `shippingCost.docs.json`, which the panel writes. An API without docs has no such file.
- **Its path** is unique with its method, and its `name` is unique in the account.

## Hooks

The hooks of an instance, a database, a table, an API of a table and a system API are a folder each, named by the hook. See [Pre hooks](https://docs.apimaker.dev/v4/docs/apis-all/hooks/preHook-api.html) and [Post hooks](https://docs.apimaker.dev/v4/docs/apis-all/hooks/postHook-api.html).

| Hook of | Folder in `src` | Where it runs : keys of its YAML |
|---|---|---|
| An instance | `instance_hooks/` | `instanceName` |
| A database | `database_hooks/` | `instanceName`, `databaseName` |
| A table | `collection_hooks/` | `instanceName`, `databaseName`, `collectionName` |
| One API of a table | `instance_api_hooks/` | `instanceName`, `databaseName`, `collectionName`, `apiId` (`SCHEMA_GET_ALL`, `GEN_POST_QUERY`…) |
| A system API | `system_api_hooks/` | `apiId` (`GET_TOKEN`…) |

**src/collection_hooks/Row_scope-orders/Row_scope-orders.yaml**

```yaml
name: Row scope - orders    # the folder : Row_scope-orders
instanceName: mongodb       # where it runs : the table orders
databaseName: shop
collectionName: orders
hookType: pre               # pre : before the APIs of the table ; post : after them
order: 1                    # its place among the pre hooks of the table, from 1
isActive: true
groupNames: [Shop app]      # only for the API users of these groups ; leave it out for all of them
```

- A new hook needs no `_id` and no `guid` : it joins the other hooks of its place. Delete its folder and the hook goes.
- The code is `Row_scope-orders.<extension>`, next to its YAML, in every language : a hook of a table is alone in its folder.

**TypeScript**

**src/collection_hooks/Row_scope-orders/Row_scope-orders.ts**

```typescript
import * as T from 'types';
import * as db from 'db-interfaces';

async function main(g: T.IAMGlobal) {
    if (!g.req.isApiRequestFromUser) return; // calls made by your own code : leave them alone

    // g.req.query, g.req.body, g.req.params, g.req.headers : the request, changeable
    // g.req.auth.authAMUser, g.req.auth.authAMDB… : who calls
    // g.res.shared : values for the API and the post hooks of the same request
}
module.exports = main;
```

**Python**

**src/collection_hooks/Row_scope-orders/Row_scope-orders.py**

```python
from am import G


async def main(g: G):
    if not g.req.is_api_request_from_user:
        return  # calls made by your own code : leave them alone

    # g.req.query, g.req.body, g.req.params, g.req.headers : the request, changeable
    # g.req.auth.authAMUser, g.req.auth.authAMDB… : who calls
    # g.res.shared : values for the API and the post hooks of the same request
```

**PHP**

**src/collection_hooks/Row_scope-orders/Row_scope-orders.php**

```php
<?php
function main(AmG $g)
{
    if (!$g->req->isApiRequestFromUser) {
        return null; // calls made by your own code : leave them alone
    }

    // $g->req->query, $g->req->body, $g->req->params, $g->req->headers : the request, changeable
    // $g->req->auth->authAMUser, $g->req->auth->authAMDB… : who calls
    // $g->res->shared : values for the API and the post hooks of the same request
    return null;
}
```

**C#**

**src/collection_hooks/Row_scope-orders/Row_scope-orders.cs**

```csharp
file static class Api
{
    public static async Task<object?> Main(G g)
    {
        if (!g.Req.IsApiRequestFromUser) return null; // calls made by your own code : leave them alone

        // g.Req.Query, g.Req.Body, g.Req.Params, g.Req.Headers : the request, changeable
        // g.Req.Auth["authAMUser"], g.Req.Auth["authAMDB"]… : who calls
        // g.Res.Shared : values for the API and the post hooks of the same request
        return null;
    }
}
```

**Go**

**src/collection_hooks/Row_scope-orders/Row_scope-orders.go**

```go
package code

import "apimaker/am"

func Main(g *am.G) (any, error) {
	if !g.Req.IsApiRequestFromUser() {
		return nil, nil // calls made by your own code : leave them alone
	}

	// g.Req.Query(), g.Req.Body(), g.Req.Params(), g.Req.Headers() : the request, changeable
	// g.Req.Auth().Map("authAMUser"), g.Req.Auth().Map("authAMDB")… : who calls
	// g.Res.Shared() : values for the API and the post hooks of the same request
	return nil, nil
}
```

**Rust**

**src/collection_hooks/Row_scope-orders/Row_scope-orders.rs**

```rust
use am::{Errors, G, Value};

pub fn main(g: &mut G) -> Result<Value, Errors> {
    if !g.req.is_api_request_from_user() {
        return Ok(Value::Null); // calls made by your own code : leave them alone
    }

    // g.req.query_mut(), g.req.body_mut(), g.req.params_mut(), g.req.headers_mut() : the request, changeable
    // g.req.auth()["authAMUser"], g.req.auth()["authAMDB"]… : who calls
    // g.res.shared_mut() : values for the API and the post hooks of the same request
    Ok(Value::Null)
}
```

**Java**

**src/collection_hooks/Row_scope-orders/Row_scope-orders.java**

```java
import am.G;

class Api {
    static Object main(G g) {
        if (!g.req.isApiRequestFromUser()) return null; // calls made by your own code : leave them alone

        // g.req.getQuery(), g.req.getBody(), g.req.getParams(), g.req.getHeaders() : the request, changeable
        // g.req.getAuth().getObject("authAMUser"), g.req.getAuth().getObject("authAMDB")… : who calls
        // g.res.getShared() : values for the API and the post hooks of the same request
        return null;
    }
}
```

**Kotlin**

**src/collection_hooks/Row_scope-orders/Row_scope-orders.kt**

```kotlin
import am.G

fun main(g: G): Any? {
    if (!g.req.isApiRequestFromUser) return null // calls made by your own code : leave them alone

    // g.req.query, g.req.body, g.req.params, g.req.headers : the request, changeable
    // g.req.auth.getObject("authAMUser"), g.req.auth.getObject("authAMDB")… : who calls
    // g.res.shared : values for the API and the post hooks of the same request
    return null
}
```

- The code which keeps each person to its own rows is on [Narrow a request to the rows of the caller](https://docs.apimaker.dev/v4/docs/apis-all/hooks/preHook-api.html#narrow-a-request-to-the-rows-of-the-caller), in every language. A table which people read through an application needs it before its first API user : the [security report](https://docs.apimaker.dev/v4/docs/apis-security/api-security-report.html) says where it is missing.

## Event

An event has listeners, each one a folder of code under `listeners/`. See [Events](https://docs.apimaker.dev/v4/docs/apis-all/events/user-created-events-api.html).

**src/events/Order_placed/Order_placed.yaml**

```yaml
name: Order placed                       # g.sys.system.emitEvent('Order placed', data) emits it
isAutomaticallyTriggerOnApiHit: false    # true : emitted after each successful call of the APIs of triggerOnApis
triggerOnApis: []
eventListeners:
  - name: Notify the shop                # its code : listeners/Notify_the_shop/Notify_the_shop.<extension>
    versions:
      - name: Original
        version: "-1"
        isActive: true
```

**TypeScript**

**src/events/Order_placed/listeners/Notify_the_shop/Notify_the_shop.ts**

```typescript
import * as T from 'types';
import * as db from 'db-interfaces';

async function main(g: T.IAMGlobal) {
    const data = g.req.eventData;   // what was emitted
    await g.sys.db.saveSingleOrMultiple({
        instance: 'mongodb', database: 'shop', collection: 'notifications',
        saveData: { order_id: data.id, text: `Order ${data.id} placed` },
    });
    return { notified: true };      // returned to the emitter
}
module.exports = main;
```

**Python**

**src/events/Order_placed/listeners/Notify_the_shop/Notify_the_shop.py**

```python
from am import G


async def main(g: G):
    data = g.req.event_data   # what was emitted
    await g.sys.db.save_single_or_multiple(
        'mongodb', 'shop', 'notifications',
        save_data={'order_id': data.id, 'text': f'Order {data.id} placed'},
    )
    return {'notified': True}  # returned to the emitter
```

**PHP**

**src/events/Order_placed/listeners/Notify_the_shop/Notify_the_shop.php**

```php
<?php
function main(AmG $g)
{
    $data = $g->req->eventData;   // what was emitted
    $g->sys->db->saveSingleOrMultiple(
        'mongodb', 'shop', 'notifications',
        saveData: ['order_id' => $data->id, 'text' => "Order {$data->id} placed"],
    );
    return ['notified' => true];  // returned to the emitter
}
```

**C#**

**src/events/Order_placed/listeners/Notify_the_shop/Notify_the_shop.cs**

```csharp
file static class Api
{
    public static async Task<object?> Main(G g)
    {
        var orderId = g.Req.EventData?["id"]?.GetValue<long>();   // what was emitted
        await g.Sys.Db.SaveSingleOrMultiple(new Save("mongodb", "shop", "notifications", new { order_id = orderId, text = $"Order {orderId} placed" }));
        return new { notified = true };  // returned to the emitter
    }
}
```

**Go**

**src/events/Order_placed/listeners/Notify_the_shop/Notify_the_shop.go**

```go
package code

import (
	"fmt"

	"apimaker/am"
)

func Main(g *am.G) (any, error) {
	data := am.AsM(g.Req.EventData()) // what was emitted
	_, err := g.Sys.Db.SaveSingleOrMultiple(am.Save{
		Instance: "mongodb", Database: "shop", Collection: "notifications",
		SaveData: am.M{"order_id": data.Get("id"), "text": fmt.Sprintf("Order %v placed", data.Get("id"))},
	})
	if err != nil {
		return nil, err
	}
	return am.M{"notified": true}, nil // returned to the emitter
}
```

**Rust**

**src/events/Order_placed/listeners/Notify_the_shop/Notify_the_shop.rs**

```rust
use am::{Errors, G, Save, Value, json};

pub fn main(g: &mut G) -> Result<Value, Errors> {
    let order_id = g.req.event_data()["id"].clone(); // what was emitted
    g.sys.db.save_single_or_multiple(Save::new(
        "mongodb",
        "shop",
        "notifications",
        json!({ "order_id": order_id, "text": format!("Order {order_id} placed") }),
    ))?;
    Ok(json!({ "notified": true })) // returned to the emitter
}
```

**Java**

**src/events/Order_placed/listeners/Notify_the_shop/Notify_the_shop.java**

```java
import am.G;
import am.JsonObject;
import am.Save;
import java.util.Map;

class Api {
    static Object main(G g) {
        JsonObject data = JsonObject.from(g.req.getEventData());   // what was emitted
        g.sys.db.saveSingleOrMultiple(new Save("mongodb", "shop", "notifications",
            Map.of("order_id", data.getLong("id"), "text", "Order " + data.getLong("id") + " placed")));
        return Map.of("notified", true);  // returned to the emitter
    }
}
```

**Kotlin**

**src/events/Order_placed/listeners/Notify_the_shop/Notify_the_shop.kt**

```kotlin
import am.G
import am.JsonObject
import am.Save

fun main(g: G): Any? {
    val data = JsonObject.from(g.req.eventData)   // what was emitted
    g.sys.db.saveSingleOrMultiple(
        Save("mongodb", "shop", "notifications", mapOf("order_id" to data.getLong("id"), "text" to "Order ${data.getLong("id")} placed")),
    )
    return mapOf("notified" to true)  // returned to the emitter
}
```

- A listener is a folder of its own in every language : `listeners/<listener>/<listener>.<extension>`.
- To emit it after a call of an API instead of from code : `isAutomaticallyTriggerOnApiHit: true`, and the API in `triggerOnApis` (`{ apiCategory: CUSTOM_APIS, customApiName: shippingCost }`, or an API of a table with `instanceName`, `databaseName`, `collectionName`, `instanceApiName`).

## WebSocket event

A custom WebSocket event, emitted by `g.sys.system.emitEventWS`. Its code says who may subscribe. See [WebSocket events](https://docs.apimaker.dev/v4/docs/pages/web-socket-event-page.html#custom-websocket-events).

**src/web_socket_events/defaultorders-updated/defaultorders-updated.yaml**

```yaml
name: default/orders-updated   # the folder is the name without its /
apiCategory: WEB_SOCKET_EVENT
authProviders: [default_db_login]   # a client subscribes with a person token of these providers
versions:
  - name: Original
    version: "-1"
    isActive: true
```

**TypeScript**

**src/web_socket_events/defaultorders-updated/defaultorders-updated.ts**

```typescript
import * as T from 'types';

// Who may subscribe : a person of the users table.
async function main(g: T.IAMGlobal) {
    if (!g.req.auth.authAMDB) return { canConnect: false, errorText: 'Please sign in.' };
    return { canConnect: true, errorText: '' };
}
module.exports = main;
```

**Python**

**src/web_socket_events/defaultorders-updated/defaultorders-updated.py**

```python
from am import G


# Who may subscribe : a person of the users table.
async def main(g: G):
    if not g.req.auth.authAMDB:
        return {'canConnect': False, 'errorText': 'Please sign in.'}
    return {'canConnect': True, 'errorText': ''}
```

**PHP**

**src/web_socket_events/defaultorders-updated/defaultorders-updated.php**

```php
<?php
// Who may subscribe : a person of the users table.
function main(AmG $g)
{
    if (empty($g->req->auth->authAMDB)) {
        return ['canConnect' => false, 'errorText' => 'Please sign in.'];
    }
    return ['canConnect' => true, 'errorText' => ''];
}
```

**C#**

**src/web_socket_events/defaultorders-updated/defaultorders-updated.cs**

```csharp
// Who may subscribe : a person of the users table.
file static class Api
{
    public static async Task<object?> Main(G g)
    {
        if (g.Req.Auth.GetObject("authAMDB").Count == 0) return new { canConnect = false, errorText = "Please sign in." };
        return new { canConnect = true, errorText = "" };
    }
}
```

**Go**

**src/web_socket_events/defaultorders-updated/defaultorders-updated.go**

```go
package code

import "apimaker/am"

// Main says who may subscribe : a person of the users table.
func Main(g *am.G) (any, error) {
	if len(g.Req.Auth().Map("authAMDB")) == 0 {
		return am.M{"canConnect": false, "errorText": "Please sign in."}, nil
	}
	return am.M{"canConnect": true, "errorText": ""}, nil
}
```

**Rust**

**src/web_socket_events/defaultorders-updated/defaultorders-updated.rs**

```rust
use am::{Errors, G, Value, json};

// Who may subscribe : a person of the users table.
pub fn main(g: &mut G) -> Result<Value, Errors> {
    if g.req.auth()["authAMDB"].is_null() {
        return Ok(json!({ "canConnect": false, "errorText": "Please sign in." }));
    }
    Ok(json!({ "canConnect": true, "errorText": "" }))
}
```

**Java**

**src/web_socket_events/defaultorders-updated/defaultorders-updated.java**

```java
import am.G;
import java.util.Map;

// Who may subscribe : a person of the users table.
class Api {
    static Object main(G g) {
        if (g.req.getAuth().getObject("authAMDB").isEmpty()) return Map.of("canConnect", false, "errorText", "Please sign in.");
        return Map.of("canConnect", true, "errorText", "");
    }
}
```

**Kotlin**

**src/web_socket_events/defaultorders-updated/defaultorders-updated.kt**

```kotlin
import am.G

// Who may subscribe : a person of the users table.
fun main(g: G): Any? {
    if (g.req.auth.getObject("authAMDB").isEmpty()) return mapOf("canConnect" to false, "errorText" to "Please sign in.")
    return mapOf("canConnect" to true, "errorText" to "")
}
```

## Scheduler

Code which runs on a timetable. See [Schedulers](https://docs.apimaker.dev/v4/docs/apis-all/schedulers/user-created-schedulers-api.html).

**src/schedulers/Clean_old_carts/Clean_old_carts.yaml**

```yaml
name: Clean old carts
isActive: true                    # the switch of the whole scheduler
intervalArr:
  - timeInterval: 0 30 2 * * *    # cron, seconds first : every day at 02:30
    timeZone: Asia/Kolkata
    active: true                  # each interval has its own switch
schedulerTimeoutInMinutes: 10
versions:
  - name: Original
    version: "-1"
    isActive: true
```

**TypeScript**

**src/schedulers/Clean_old_carts/Clean_old_carts.ts**

```typescript
import * as T from 'types';

async function main(g: T.IAMGlobal) {
    const before = Date.now() - 7 * 86400000;   // a week ago, in milliseconds since 1970 : what updated_at holds
    const removed = await g.sys.db.removeByQuery({
        instance: 'mongodb', database: 'shop', collection: 'carts',
        find: { updated_at: { $lt: before } },
    });
    g.logger.log(`${removed.deletedRowsCount} carts removed`);
    return { removed: removed.deletedRowsCount };
}
module.exports = main;
```

**Python**

**src/schedulers/Clean_old_carts/Clean_old_carts.py**

```python
import time

from am import G


async def main(g: G):
    before = int(time.time() * 1000) - 7 * 86400000   # a week ago, in milliseconds since 1970 : what updated_at holds
    removed = await g.sys.db.remove_by_query('mongodb', 'shop', 'carts', find={'updated_at': {'$lt': before}})
    g.logger.log(f'{removed.deleted_rows_count} carts removed')
    return {'removed': removed.deleted_rows_count}
```

**PHP**

**src/schedulers/Clean_old_carts/Clean_old_carts.php**

```php
<?php
function main(AmG $g)
{
    $before = (int) (microtime(true) * 1000) - 7 * 86400000;   // a week ago, in milliseconds since 1970 : what updated_at holds
    $removed = $g->sys->db->removeByQuery('mongodb', 'shop', 'carts', find: ['updated_at' => ['$lt' => $before]]);
    $g->logger->log("{$removed->deletedRowsCount} carts removed");
    return ['removed' => $removed->deletedRowsCount];
}
```

**C#**

**src/schedulers/Clean_old_carts/Clean_old_carts.cs**

```csharp
file static class Api
{
    public static async Task<object?> Main(G g)
    {
        var before = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - 7 * 86400000L;   // a week ago, in milliseconds since 1970 : what updated_at holds
        var removed = await g.Sys.Db.RemoveByQuery(new RemoveByQuery("mongodb", "shop", "carts")
        {
            Find = new JsonObject { ["updated_at"] = new JsonObject { ["$lt"] = before } },
        });
        g.Logger.Log($"{removed.DeletedRowsCount} carts removed");
        return new { removed = removed.DeletedRowsCount };
    }
}
```

**Go**

**src/schedulers/Clean_old_carts/Clean_old_carts.go**

```go
package code

import (
	"fmt"
	"time"

	"apimaker/am"
)

func Main(g *am.G) (any, error) {
	before := time.Now().UnixMilli() - 7*86400000 // a week ago, in milliseconds since 1970 : what updated_at holds
	removed, err := g.Sys.Db.RemoveByQuery(am.RemoveByQuery{
		Instance: "mongodb", Database: "shop", Collection: "carts",
		Find: am.M{"updated_at": am.M{"$lt": before}},
	})
	if err != nil {
		return nil, err
	}
	g.Logger.Log(fmt.Sprintf("%d carts removed", removed.DeletedRowsCount))
	return am.M{"removed": removed.DeletedRowsCount}, nil
}
```

**Rust**

**src/schedulers/Clean_old_carts/Clean_old_carts.rs**

```rust
use std::time::{SystemTime, UNIX_EPOCH};

use am::{Errors, G, RemoveByQuery, Value, json};

pub fn main(g: &mut G) -> Result<Value, Errors> {
    let now = SystemTime::now().duration_since(UNIX_EPOCH).map_or(0, |since| since.as_millis() as i64);
    let before = now - 7 * 86_400_000; // a week ago, in milliseconds since 1970 : what updated_at holds
    let removed = g.sys.db.remove_by_query(RemoveByQuery::new("mongodb", "shop", "carts").find(json!({ "updated_at": { "$lt": before } })))?;
    g.logger.log(format!("{} carts removed", removed.deleted_rows_count));
    Ok(json!({ "removed": removed.deleted_rows_count }))
}
```

**Java**

**src/schedulers/Clean_old_carts/Clean_old_carts.java**

```java
import am.G;
import am.RemoveByQuery;
import am.RemoveByQueryResult;
import java.util.Map;

class Api {
    static Object main(G g) {
        long before = System.currentTimeMillis() - 7 * 86400000L;   // a week ago, in milliseconds since 1970 : what updated_at holds
        RemoveByQueryResult removed = g.sys.db.removeByQuery(new RemoveByQuery("mongodb", "shop", "carts")
            .find(Map.of("updated_at", Map.of("$lt", before))));
        g.logger.log(removed.getDeletedRowsCount() + " carts removed");
        return Map.of("removed", removed.getDeletedRowsCount());
    }
}
```

**Kotlin**

**src/schedulers/Clean_old_carts/Clean_old_carts.kt**

```kotlin
import am.G
import am.RemoveByQuery

fun main(g: G): Any? {
    val before = System.currentTimeMillis() - 7 * 86400000L   // a week ago, in milliseconds since 1970 : what updated_at holds
    val removed = g.sys.db.removeByQuery(RemoveByQuery("mongodb", "shop", "carts").find(mapOf("updated_at" to mapOf("\$lt" to before))))
    g.logger.log("${removed.deletedRowsCount} carts removed")
    return mapOf("removed" to removed.deletedRowsCount)
}
```

## Database migration

A script which runs once on each environment, after the pull which brings it. See [Database migration](https://docs.apimaker.dev/v4/docs/features/database-migration.html).

**src/database_migrations/Add_description/Add_description.yaml**

```yaml
name: Add description
label: Add the column description to order_transactions   # what the list of scripts shows
order: 1                         # the pull runs the scripts which never ran here, from the lowest order
active: true
dbMigrationTimeoutInMinutes: 10
```

**TypeScript**

**src/database_migrations/Add_description/Add_description.ts**

```typescript
import * as T from 'types';

async function main(g: T.IAMGlobal) {
    const meta = await g.sys.system.getTableMeta({ instance: 'mysql8', database: 'inventory', table: 'order_transactions' });
    const hasDescription = meta.some(column => column.name === 'description');

    if (!hasDescription) {
        await g.sys.system.executeQuery({
            instance: 'mysql8',
            query: 'ALTER TABLE `inventory`.`order_transactions` ADD COLUMN `description` varchar(255) NULL AFTER `qty`',
        });
    }
    return { hasDescription };
}
module.exports = main;
```

**Python**

**src/database_migrations/Add_description/Add_description.py**

```python
from am import G


async def main(g: G):
    meta = await g.sys.system.get_table_meta(instance='mysql8', database='inventory', table='order_transactions')
    has_description = any(column.name == 'description' for column in meta)

    if not has_description:
        await g.sys.system.execute_query(
            'mysql8',
            'ALTER TABLE `inventory`.`order_transactions` ADD COLUMN `description` varchar(255) NULL AFTER `qty`',
        )
    return {'hasDescription': has_description}
```

**PHP**

**src/database_migrations/Add_description/Add_description.php**

```php
<?php
function main(AmG $g)
{
    $meta = $g->sys->system->getTableMeta(instance: 'mysql8', database: 'inventory', table: 'order_transactions');
    $hasDescription = (bool) array_filter($meta, fn ($column) => $column->name === 'description');

    if (!$hasDescription) {
        $g->sys->system->executeQuery(
            'mysql8',
            'ALTER TABLE `inventory`.`order_transactions` ADD COLUMN `description` varchar(255) NULL AFTER `qty`',
        );
    }
    return ['hasDescription' => $hasDescription];
}
```

**C#**

**src/database_migrations/Add_description/Add_description.cs**

```csharp
file static class Api
{
    public static async Task<object?> Main(G g)
    {
        var meta = await g.Sys.System.GetTableMeta(new GetTableMeta { Instance = "mysql8", Database = "inventory", Table = "order_transactions" });
        var hasDescription = meta.Any(column => column.Name == "description");

        if (!hasDescription)
        {
            await g.Sys.System.ExecuteQuery(new ExecuteQuery("mysql8",
                "ALTER TABLE `inventory`.`order_transactions` ADD COLUMN `description` varchar(255) NULL AFTER `qty`"));
        }
        return new { hasDescription };
    }
}
```

**Go**

**src/database_migrations/Add_description/Add_description.go**

```go
package code

import "apimaker/am"

func Main(g *am.G) (any, error) {
	meta, err := g.Sys.System.GetTableMeta(am.GetTableMeta{Instance: "mysql8", Database: "inventory", Table: "order_transactions"})
	if err != nil {
		return nil, err
	}
	hasDescription := false
	for _, column := range meta {
		hasDescription = hasDescription || column.Name == "description"
	}

	if !hasDescription {
		_, err := g.Sys.System.ExecuteQuery(am.ExecuteQuery{
			Instance: "mysql8",
			Query:    "ALTER TABLE `inventory`.`order_transactions` ADD COLUMN `description` varchar(255) NULL AFTER `qty`",
		})
		if err != nil {
			return nil, err
		}
	}
	return am.M{"hasDescription": hasDescription}, nil
}
```

**Rust**

**src/database_migrations/Add_description/Add_description.rs**

```rust
use am::{Errors, ExecuteQuery, G, GetTableMeta, Value, json};

pub fn main(g: &mut G) -> Result<Value, Errors> {
    let meta = g.sys.system.get_table_meta(GetTableMeta::default().instance("mysql8").database("inventory").table("order_transactions"))?;
    let has_description = meta.iter().any(|column| column.name == "description");

    if !has_description {
        g.sys.system.execute_query(ExecuteQuery::new(
            "mysql8",
            "ALTER TABLE `inventory`.`order_transactions` ADD COLUMN `description` varchar(255) NULL AFTER `qty`",
        ))?;
    }
    Ok(json!({ "hasDescription": has_description }))
}
```

**Java**

**src/database_migrations/Add_description/Add_description.java**

```java
import am.ExecuteQuery;
import am.G;
import am.GetTableMeta;
import am.TableMetaField;
import java.util.List;
import java.util.Map;

class Api {
    static Object main(G g) {
        List<TableMetaField> meta = g.sys.system.getTableMeta(new GetTableMeta().instance("mysql8").database("inventory").table("order_transactions"));
        boolean hasDescription = meta.stream().anyMatch(column -> "description".equals(column.getName()));

        if (!hasDescription) {
            g.sys.system.executeQuery(new ExecuteQuery("mysql8",
                "ALTER TABLE `inventory`.`order_transactions` ADD COLUMN `description` varchar(255) NULL AFTER `qty`"));
        }
        return Map.of("hasDescription", hasDescription);
    }
}
```

**Kotlin**

**src/database_migrations/Add_description/Add_description.kt**

```kotlin
import am.ExecuteQuery
import am.G
import am.GetTableMeta

fun main(g: G): Any? {
    val meta = g.sys.system.getTableMeta(GetTableMeta().instance("mysql8").database("inventory").table("order_transactions"))
    val hasDescription = meta.any { it.name == "description" }

    if (!hasDescription) {
        g.sys.system.executeQuery(
            ExecuteQuery("mysql8", "ALTER TABLE `inventory`.`order_transactions` ADD COLUMN `description` varchar(255) NULL AFTER `qty`"),
        )
    }
    return mapOf("hasDescription" to hasDescription)
}
```

- Write a script which can run twice without harm : a script which failed half way runs again after the fix.

## Process initializer

Code which runs once in each runner of the sandboxes of the account, when it starts. See [Process initializers](https://docs.apimaker.dev/v4/docs/features/process-initializers.html).

**src/process_initializers/Load_rates/Load_rates.yaml**

```yaml
name: Load rates
order: 1                                 # the initializers run one after the other, from the lowest order
active: true
processInitializerTimeoutInMinutes: 1
```

**TypeScript**

**src/process_initializers/Load_rates/Load_rates.ts**

```typescript
import * as T from 'types';

async function main(g: T.IAMGlobal) {
    // Read once per process : every request this process serves finds them in g.shared.
    const rates = await g.sys.db.getAll({ instance: 'mysql_8', database: 'shop', collection: 'rates' });
    g.shared.rates = rates;
    g.logger.log(`${rates.length} rates loaded`);
}
module.exports = main;
```

**Python**

**src/process_initializers/Load_rates/Load_rates.py**

```python
from am import G


async def main(g: G):
    # Read once per process : every request this process serves finds them in g.shared.
    rates = await g.sys.db.get_all('mysql_8', 'shop', 'rates')
    g.shared['rates'] = rates
    g.logger.log(len(rates), 'rates loaded')
```

**PHP**

**src/process_initializers/Load_rates/Load_rates.php**

```php
<?php
function main(AmG $g)
{
    // Read once per process : every request this process serves finds them in $g->shared.
    $rates = $g->sys->db->getAll('mysql_8', 'shop', 'rates');
    $g->shared->rates = $rates;
    $g->logger->log(count($rates), 'rates loaded');
    return null;
}
```

**C#**

**src/process_initializers/Load_rates/Load_rates.cs**

```csharp
file static class Api
{
    public static async Task<object?> Main(G g)
    {
        // Read once per process : every request this process serves finds them in g.Shared.
        var rates = await g.Sys.Db.GetAll(new GetAll("mysql_8", "shop", "rates"));
        g.Shared["rates"] = rates;
        g.Logger.Log(rates.Count, "rates loaded");
        return null;
    }
}
```

**Go**

**src/process_initializers/Load_rates/Load_rates.go**

```go
package code

import "apimaker/am"

func Main(g *am.G) (any, error) {
	// Read once per process : every request this process serves finds them in g.Shared.
	rates, err := g.Sys.Db.GetAll(am.GetAll{Instance: "mysql_8", Database: "shop", Collection: "rates"})
	if err != nil {
		return nil, err
	}
	g.Shared.Set("rates", rates)
	g.Logger.Log(len(rates), "rates loaded")
	return nil, nil
}
```

**Rust**

**src/process_initializers/Load_rates/Load_rates.rs**

```rust
use am::{Errors, G, GetAll, Value, json};

pub fn main(g: &mut G) -> Result<Value, Errors> {
    // Read once per process : every request this process serves finds them in g.shared.
    let rates = g.sys.db.get_all(GetAll::new("mysql_8", "shop", "rates"))?;
    g.logger.log(format!("{} rates loaded", rates.len()));
    g.shared.set("rates", json!(rates));
    Ok(Value::Null)
}
```

**Java**

**src/process_initializers/Load_rates/Load_rates.java**

```java
import am.G;
import am.GetAll;
import am.JsonObject;
import java.util.List;

class Api {
    static Object main(G g) {
        // Read once per process : every request this process serves finds them in g.shared.
        List<JsonObject> rates = g.sys.db.getAll(new GetAll("mysql_8", "shop", "rates"));
        g.shared.put("rates", rates);
        g.logger.log(rates.size(), "rates loaded");
        return null;
    }
}
```

**Kotlin**

**src/process_initializers/Load_rates/Load_rates.kt**

```kotlin
import am.G
import am.GetAll

fun main(g: G): Any? {
    // Read once per process : every request this process serves finds them in g.shared.
    val rates = g.sys.db.getAll(GetAll("mysql_8", "shop", "rates"))
    g.shared["rates"] = rates
    g.logger.log(rates.size, "rates loaded")
    return null
}
```

## Utility class

Code which the other code of its language imports. See [Utility classes](https://docs.apimaker.dev/v4/docs/utility-class/utility-class.html#other-languages).

**src/utility_classes/utils---Money/Money.yaml**

```yaml
name: Money                 # what the code imports ; the file is named Money.yaml, not as the folder
folderPath: utils/          # its folder in the list : the folder in git is utils---Money
versions:
  - name: Original
    version: "-1"           # the code is Money.<extension>, next to this file
    isActive: true
```

**TypeScript**

The class is `src/utility_classes/utils---Money/Money.ts`, imported as `utils/Money`.

**The utility class Money, in the folder utils**

```typescript
export function round(amount: number): number {
    return Math.round(amount * 100) / 100;
}
```

**A custom API which uses it**

```typescript
import * as T from 'types';
import * as Money from 'utils/Money';

async function main(g: T.IAMGlobal) {
    return { total: Money.round(g.req.body.amount * 1.18) };
}
module.exports = main;
```

**Python**

The class is `src/utility_classes/utils---Money/Money.py`, the module `Money`.

**The utility class Money, in the folder utils**

```python
def round_amount(amount):
    return round(amount, 2)
```

**A custom API which uses it**

```python
from Money import round_amount


async def main(g):
    return {'total': round_amount(g.req.body.amount * 1.18)}
```

**PHP**

The class is `src/utility_classes/utils---Money/Money.php` : what it declares is there for the code of PHP.

**The utility class Money, in the folder utils**

```php
<?php
class Money
{
    public static function round($amount)
    {
        return round($amount, 2);
    }
}
```

**A custom API which uses it**

```php
<?php
function main($g)
{
    return ['total' => Money::round($g->req->body->amount * 1.18)];
}
```

**C#**

The class is `src/utility_classes/utils---Money/Money.cs`, compiled with the code.

**The utility class Money, in the folder utils**

```csharp
public static class Money
{
    public static double Round(double amount) => Math.Round(amount, 2);
}
```

**A custom API which uses it**

```csharp
file static class Api
{
    public static async Task<object?> Main(G g)
    {
        return new { total = Money.Round(g.Req.Body.GetDouble("amount") * 1.18) };
    }
}
```

**Go**

The class is `src/utility_classes/utils---Money/Money.go`, a package imported by its folder.

**The utility class Money, in the folder utils**

```go
package money

import "math"

func Round(amount float64) float64 {
	return math.Round(amount*100) / 100
}
```

**A custom API which uses it**

```go
package code

import (
	"apimaker/am"
	money "apimaker/src/utility_classes/utils---Money"
)

func Main(g *am.G) (any, error) {
	return am.M{"total": money.Round(g.Req.Body().Float("amount") * 1.18)}, nil
}
```

**Rust**

The class is `src/utility_classes/utils---Money/Money.rs`, the module `Money` of the crate of the code.

**The utility class Money, in the folder utils**

```rust
pub fn round(amount: f64) -> f64 {
    (amount * 100.0).round() / 100.0
}
```

**A custom API which uses it**

```rust
use am::{Errors, G, Value, json};

use crate::Money;

pub fn main(g: &mut G) -> Result<Value, Errors> {
    Ok(json!({ "total": Money::round(g.req.body()["amount"].as_f64().unwrap_or(0.0) * 1.18) }))
}
```

**Java**

The class is `src/utility_classes/utils---Money/Money.java`, compiled with the other classes of Java.

**The utility class Money, in the folder utils**

```java
public class Money {
    public static double round(double amount) {
        return Math.round(amount * 100) / 100.0;
    }
}
```

**A custom API which uses it**

```java
import am.G;
import java.util.Map;

class Api {
    static Object main(G g) {
        return Map.of("total", Money.round(g.req.getBody().getDouble("amount") * 1.18));
    }
}
```

**Kotlin**

The class is `src/utility_classes/utils---Money/Money.kt`, compiled with the other classes of Kotlin.

**The utility class Money, in the folder utils**

```kotlin
object Money {
    fun round(amount: Double): Double = Math.round(amount * 100) / 100.0
}
```

**A custom API which uses it**

```kotlin
import am.G

fun main(g: G): Any? = mapOf("total" to Money.round(g.req.body.getDouble("amount") * 1.18))
```

- A class is used by the code of its own language only.

## Test case

The tests of a custom API or of a utility class, run by `npm run test` and the Test Cases page. See [Test cases](https://docs.apimaker.dev/v4/docs/test-cases/test-cases.html).

**src/test_cases/Shipping_cost/Shipping_cost.yaml**

```yaml
name: Shipping cost
testCaseType: CUSTOM_APIS       # UTILITY_CLASS with utilityClassName for a utility class
customApiName: shippingCost     # the custom API it tests
mocks: []                       # the mocks of the Mocks tab ; the code adds its own
```

**TypeScript**

The test case is `src/test_cases/Shipping_cost/Shipping_cost.ts`.

**A test case**

```typescript
import * as T from 'types';
import * as assert from 'node:assert';

module.exports = [{
    name: 'the cost of a parcel',
    mocks: [{ method: 'db.getAll', return: [{ per_kg: 2.5 }] }],
    code: async (g: T.IAMGlobal) => {
        assert.deepStrictEqual(await g.sys.test.runCustomApi({ name: 'shippingCost', body: { weight: 4 } }), { cost: 10 });
        assert.strictEqual(g.sys.test.calls('db.getAll').length, 1);
        await assert.rejects(g.sys.test.runCustomApi({ name: 'shippingCost', body: { weight: 40 } }), (errors: any) => errors[0].code === 422);
    },
}];
```

**Python**

The test case is `src/test_cases/Shipping_cost/Shipping_cost.py`.

**A test case**

```python
async def test_the_cost_of_a_parcel(g):
    await g.sys.test.mock("db.getAll", when={"collection": "rates"}, returns=[{"per_kg": 2.5}])
    out = await g.sys.test.run_custom_api("shippingCost", body={"weight": 4})
    assert out == {"cost": 10}, out
    assert len(await g.sys.test.calls("db.getAll")) == 1


async def test_too_heavy_is_refused(g):
    try:
        await g.sys.test.run_custom_api("shippingCost", body={"weight": 40})
    except g.Errors as refused:
        assert refused.code == 422
    else:
        raise AssertionError("a parcel of 40 kg was accepted")
```

**PHP**

The test case is `src/test_cases/Shipping_cost/Shipping_cost.php`.

**A test case**

```php
<?php
function test_the_cost_of_a_parcel(AmG $g)
{
    $g->sys->test->mock('db.getAll', when: ['collection' => 'rates'], returns: [['per_kg' => 2.5]]);
    $out = $g->sys->test->runCustomApi('shippingCost', body: ['weight' => 4]);
    if ($out->cost != 10) {
        throw new Exception("the cost is {$out->cost}");
    }
}

function test_too_heavy_is_refused(AmG $g)
{
    try {
        $g->sys->test->runCustomApi('shippingCost', body: ['weight' => 40]);
    } catch (AmErrors $refused) {
        if ($refused->getCode() !== 422) {
            throw new Exception("refused with {$refused->getCode()}");
        }
        return;
    }
    throw new Exception('a parcel of 40 kg was accepted');
}
```

**C#**

The test case is `src/test_cases/Shipping_cost/Shipping_cost.cs`.

**A test case**

```csharp
public static class ShippingTests
{
    public static async Task TestTheCostOfAParcel(G g)
    {
        await g.Sys.Test.Mock(new Mock("db.getAll") { When = new { collection = "rates" }, Return = new[] { new { per_kg = 2.5 } } });
        var output = await g.Sys.Test.RunCustomApi(new RunCustomApi("shippingCost") { Body = new { weight = 4 } });
        if (output.GetDouble("cost") != 10) throw new Exception("the cost is " + output?["cost"]);
    }

    public static async Task TestTooHeavyIsRefused(G g)
    {
        try
        {
            await g.Sys.Test.RunCustomApi(new RunCustomApi("shippingCost") { Body = new { weight = 40 } });
        }
        catch (AmErrors refused) when (refused.Code == 422)
        {
            return;
        }
        throw new Exception("a parcel of 40 kg was accepted");
    }
}
```

**Go**

The test case is `src/test_cases/Shipping_cost/Shipping_cost.go`.

**A test case**

```go
package code

import (
	"errors"
	"fmt"

	"apimaker/am"
)

func theCostOfAParcel(g *am.G) error {
	if err := g.Sys.Test.Mock(am.Mock{Method: "db.getAll", When: am.M{"collection": "rates"}, Return: []any{am.M{"per_kg": 2.5}}}); err != nil {
		return err
	}
	out, err := g.Sys.Test.RunCustomApi(am.RunCustomApi{Name: "shippingCost", Body: am.M{"weight": 4}})
	if err != nil {
		return err
	}
	if cost := am.AsM(out).Float("cost"); cost != 10 {
		return fmt.Errorf("the cost is %v", cost)
	}
	return nil
}

func tooHeavyIsRefused(g *am.G) error {
	_, err := g.Sys.Test.RunCustomApi(am.RunCustomApi{Name: "shippingCost", Body: am.M{"weight": 40}})
	var refused *am.Errors
	if errors.As(err, &refused) && refused.Code() == 422 {
		return nil
	}
	return fmt.Errorf("a parcel of 40 kg was not refused : %v", err)
}

var Tests = []am.Test{
	{Name: "the cost of a parcel", Run: theCostOfAParcel},
	{Name: "too heavy is refused", Run: tooHeavyIsRefused},
}
```

**Rust**

The test case is `src/test_cases/Shipping_cost/Shipping_cost.rs`.

**A test case**

```rust
use am::{Errors, G, Mock, RunCustomApi, json};

fn the_cost_of_a_parcel(g: &mut G) -> Result<(), Errors> {
    g.sys.test.mock(Mock::new("db.getAll").when(json!({ "collection": "rates" })).returns(json!([{ "per_kg": 2.5 }])))?;
    let out = g.sys.test.run_custom_api(RunCustomApi::new("shippingCost").body(json!({ "weight": 4 })))?;
    assert_eq!(out["cost"].as_f64(), Some(10.0));
    Ok(())
}

fn too_heavy_is_refused(g: &mut G) -> Result<(), Errors> {
    match g.sys.test.run_custom_api(RunCustomApi::new("shippingCost").body(json!({ "weight": 40 }))) {
        Err(refused) if refused.code() == 422 => Ok(()),
        other => Err(am::fail(500, &format!("a parcel of 40 kg was not refused : {other:?}"))),
    }
}

am::tests!(the_cost_of_a_parcel, too_heavy_is_refused);
```

**Java**

The test case is `src/test_cases/Shipping_cost/Shipping_cost.java`.

**A test case**

```java
import am.AmErrors;
import am.G;
import am.JsonObject;
import am.Mock;
import am.RunCustomApi;
import java.util.List;
import java.util.Map;

class ShippingTests {
    static void testTheCostOfAParcel(G g) {
        g.sys.test.mock(new Mock("db.getAll").when(Map.of("collection", "rates")).returns(List.of(Map.of("per_kg", 2.5))));
        JsonObject output = JsonObject.from(g.sys.test.runCustomApi(new RunCustomApi("shippingCost").body(Map.of("weight", 4))));
        if (output.getDouble("cost") != 10) throw new IllegalStateException("the cost is " + output.get("cost"));
    }

    static void testTooHeavyIsRefused(G g) {
        try {
            g.sys.test.runCustomApi(new RunCustomApi("shippingCost").body(Map.of("weight", 40)));
        } catch (AmErrors refused) {
            if (refused.code == 422) return;
            throw refused;
        }
        throw new IllegalStateException("a parcel of 40 kg was accepted");
    }
}
```

**Kotlin**

The test case is `src/test_cases/Shipping_cost/Shipping_cost.kt`.

**A test case**

```kotlin
import am.AmErrors
import am.G
import am.JsonObject
import am.Mock
import am.RunCustomApi

fun testTheCostOfAParcel(g: G) {
    g.sys.test.mock(Mock("db.getAll").whenever(mapOf("collection" to "rates")).returns(listOf(mapOf("per_kg" to 2.5))))
    val output = JsonObject.from(g.sys.test.runCustomApi(RunCustomApi("shippingCost").body(mapOf("weight" to 4))))
    check(output.getDouble("cost") == 10.0) { "the cost is " + output["cost"] }
}

fun testTooHeavyIsRefused(g: G) {
    try {
        g.sys.test.runCustomApi(RunCustomApi("shippingCost").body(mapOf("weight" to 40)))
    } catch (refused: AmErrors) {
        check(refused.code == 422) { "refused with ${refused.code}" }
        return
    }
    error("a parcel of 40 kg was accepted")
}
```

- How each language names its tests : [Test cases in every language](https://docs.apimaker.dev/v4/docs/test-cases/test-cases.html#other-languages).

## Schema

The columns of a table, their checks and conversions, and the relations to other tables. TypeScript in every language. See [Table schema](https://docs.apimaker.dev/v4/docs/schema/schema.html).

**src/schemas/orders/orders.yaml**

```yaml
instanceName: mongodb       # the table the schema is for
databaseName: shop
collectionName: orders
active: true
```

**src/schemas/orders/orders.config.ts**

```typescript
import { EType, ISchemaType, ISchemaProperty } from 'types';

const schema: ISchemaType = {
    _id: <ISchemaProperty>{
        __type: EType.objectId,
        isPrimaryKey: true,
        isAutoGenerateByAM: { valueGeneratorType: 'ObjectID' },
    },
    customer_id: <ISchemaProperty>{          // a relation : orders.customer_id is customers.customer_id
        __type: EType.number,
        validations: { required: true },
        database: 'shop', collection: 'customers', column: 'customer_id',
    },
    status: <ISchemaProperty>{
        __type: EType.string,
        validations: { enum: ['PENDING', 'PAID', 'SHIPPED', 'CANCELLED'] },
        conversions: { toUpperCase: true, defaults: { defaultValue: 'PENDING' } },
    },
    total_cents: <ISchemaProperty>{ __type: EType.number, validations: { min: 0 } },
};
module.exports = { schema };
```

- The YAML is named as the folder. API Maker writes a schema in the folder of its guid (`src/schemas/01M6NW232XSTGXXD4222CXZDM7/`) : a new one goes in any folder, and the next commit writes it under its guid.
- `src/types/db-interfaces.d.ts` gets the interface of the table once the schema is saved (`db.mongodb.shop.IOrders`).

## Settings of a database, a table, an API

Who may call the APIs of a table, their caching and their auth providers. See [Collection settings](https://docs.apimaker.dev/v4/docs/settings/collectionSettings.html).

**src/instance_api_settings/orders/orders.yaml**

```yaml
name: orders                       # names the folder ; the guid of the settings when it has none
instanceName: mongodb
databaseName: shop
collectionName: orders
apiId: __COLLECTION_SETTINGS__     # the table ; SCHEMA_GET_ALL… : one API of it ;
                                   # __DATABASE_SETTINGS__ here and in collectionName : the whole database
```

**src/instance_api_settings/orders/orders.config.ts**

```typescript
import * as T from 'types';

let instanceColSetting: T.IInstanceApiSettingsTypes = {
    enableCaching: true,                       // the reads are cached until a write of the table
    acceptOnlyEncryptedData: false,
    apiAccessType: T.EAPIAccessType.TOKEN_ACCESS,
    authProviders: ['default_db_login'],       // every API of the table needs a person token of this provider
};
module.exports = instanceColSetting;
```

- A key left out is taken from the level above : API settings, then the table, then the database, then the secret.

## Settings of a system API

**src/system_api_settings/HASH_DATA/HASH_DATA.yaml**

```yaml
apiId: HASH_DATA                   # the folder and the files are named by the API
```

**src/system_api_settings/HASH_DATA/HASH_DATA.config.ts**

```typescript
import * as T from 'types';

let systemApi: T.ISystemApiSettingsTypes = {
    apiAccessType: T.EAPIAccessType.TOKEN_ACCESS,   // NO_ACCESS without settings : only g.sys reaches it
};
module.exports = systemApi;
```

- The ids : [System API settings](https://docs.apimaker.dev/v4/docs/settings/systemApiSettings.html).

## Auth provider

Who the person behind a request is : a row of your users table, Google, Azure AD, AWS Cognito or your own code. See [Auth of a database user](https://docs.apimaker.dev/v4/docs/authorization/AMDB.html).

**src/auth_providers/default_db_login/default_db_login.yaml**

```yaml
name: default_db_login
generatorType: DB_TOKEN_GENERATOR   # CUSTOM_TOKEN_GENERATOR, GOOGLE, AZURE, AWS : the other kinds
```

**src/auth_providers/default_db_login/default_db_login.ts**

```typescript
import * as T from 'types';

// What the provider is : TypeScript in every language. It wins over the YAML.
const provider: T.IAuthTokenAMDB = {
    name: 'default_db_login',
    instance: 'mongodb',
    database: 'shop',
    collection: 'customers',        // table: '…' for SQL
    usernameColumn: 'email',
    passwordColumn: 'password',     // a column the schema hashes
    expiresInSeconds: 259200,
};
module.exports = provider;
```

- Its functions are in the language of the provider, next to it : `default_db_login.fg.<extension>` (the fields it adds to the token, may be empty), and for a provider of your own code `<name>.tg.<extension>` (it makes the token) and `<name>.tv.<extension>` (it checks one). In Go, Java and Kotlin each one is in a folder named as its file : `my_tokens.tg/my_tokens.tg.go`. Their code : [A provider of your own](https://docs.apimaker.dev/v4/examples/req/auth/authCustom.html).

## Group, API user, instance

**src/groups/Shop_app/Shop_app.yaml**

```yaml
name: Shop app
apisRegex:                          # true : every item of a kind ; false : only the ones named below
  allInstances: false
  allDatabases: false
  allCollections: false
  allInstanceApis: false
  allFields: true
  allCustomApis: false
  allSystemApis: false
  allWebSocketEvents: false
queryParams:                        # the grants, by name
  instanceNames: [mongodb]
  databaseNames: [shop]
  collectionNames: [products, orders]
  supportedApiNames: [SCHEMA_GET_ALL, SCHEMA_GET_BY_ID, SCHEMA_POST_QUERY]
  customApiNames: [shippingCost]
  systemApiNames: []
  webSocketEventNames: []
```

**src/api_users/shop_app/shop_app.yaml**

```yaml
name: shop_app
username: shop_app
groupNames: [Shop app]
enablePasswordPath: true
apiUserPasswordPath: common.apiUserPasswords.shop_app   # where the password is in the secret : never write password
```

**src/instances/mongodb/mongodb.yaml**

```yaml
name: mongodb
type: MONGO_DB                      # MYSQL_DB, MARIA_DB, POSTGRE_SQL_DB, SQL_SERVER_DB, ORACLE_DB
connectionString: common.connectionString.mongodb          # a path in the secret, never the string itself
connectionStringSandbox: common.connectionString.mongodb   # the one the sandboxes use
maxPoolConnections: 10
```

- Secrets are not in the repository : ask the person to add what a path names (`common.apiUserPasswords.shop_app`) to the secret of each environment. See [Secrets](https://docs.apimaker.dev/v4/docs/secrets/secrets.html) and [API group permission](https://docs.apimaker.dev/v4/docs/apis-security/api-group-permission.html).

## Texts, log profiles, saved requests

- **Texts of a language** (`src/i18ns/<name>/`) : `<name>.yaml` (`name`) and `<name>.ts`, `module.exports = { "message": "translation" }`. See [Internationalization](https://docs.apimaker.dev/v4/docs/i18/i18.html).
- **Log profiles** (`src/log_profiles/<name>/<name>.yaml`) and the shared saved requests of API Testing (`src/api_testing_states/<name>/` : YAML, `.code.ts`, `.response.txt`) : made in the admin panel, kept as they are.

## Packages and sandboxes

`src/admin_user/data.yaml` holds the sandboxes of the account : the runtime, the version and the packages of each family of languages (`javascript`, `python`, `php`, `dotnet`, `go`, `rust`, `java`, `kotlin`), under `settings.sandbox.environments`. A package of a new library goes there :

**src/admin_user/data.yaml (a part)**

```yaml
settings:
  sandbox:
    environments:
      python:
        packages:
          - name: requests
            version: 2.32.3
```

- The Dockerfile written for a family is `src/admin_user/sandbox/<family>/Dockerfile`. See [Sandbox settings](https://docs.apimaker.dev/v4/docs/settings/sandboxSettings.html#packages).

## SSR website

`src/ssr/<website>/` is a project of TypeScript : `website.yaml`, `site.config.ts`, `pages/`, `components/`, `public/`. Read the [guide for AI assistants of the SSR websites](https://docs.apimaker.dev/v4/docs/ssr/ai-guide.html) first.

## Related

- [Git integration](https://docs.apimaker.dev/v4/docs/Git/git.html) · [Docs for AI assistants](https://docs.apimaker.dev/v4/docs/ai/llms.html) · [Languages and runtimes](https://docs.apimaker.dev/v4/docs/languages/index.html) · [Custom API examples](https://docs.apimaker.dev/v4/examples/custom-apis/custom-api.html) · [The global object g](https://docs.apimaker.dev/v4/docs/pre-defined-terms/global-object-g.html)
