# Custom API Example

> Learn how to create and implement custom APIs in API Maker with this comprehensive example, including code snippets and best practices.

Source: https://docs.apimaker.dev/v4/examples/custom-apis/custom-api.html

> **The code in every language**
>
> Each example is the inside of `main`, written in every [language](https://docs.apimaker.dev/v4/docs/languages/index.html) API Maker runs : pick yours in the tabs, and every page shows it from then on. The types a call takes (`GetAll`, `DeepItem`…) are those of `am` : `from am import GetAll` in Python, `use am::GetAll;` in Rust, `import am.GetAll;` in Java and Kotlin ; PHP and C# have them without an import, Go names them `am.GetAll`.

The code of a [custom API](https://docs.apimaker.dev/v4/docs/apis-all/custom-apis/user-created-custom-api.html) : what to return, how to call the APIs of API Maker, how to send files.

## Basic code

- A custom API is the function `main` of its language : what it returns is the `data` of the answer. The pieces below are the inside of `main`.

**TypeScript**

```typescript
return {
    hello: 'world'
};
```

**Python**

```python
return {'hello': 'world'}
```

**PHP**

```php
return ['hello' => 'world'];
```

**C#**

```csharp
return new { hello = "world" };
```

**Go**

```go
return am.M{"hello": "world"}, nil
```

**Rust**

```rust
Ok(json!({ "hello": "world" }))
```

**Java**

```java
return Map.of("hello", "world");
```

**Kotlin**

```kotlin
return mapOf("hello" to "world")
```

## Calls of `g.sys`

- In TypeScript every `g.sys` call returns a promise : `await` it. A call without `await` runs, but its result and its errors are lost. The same in Python (in `async def main`) and C#. PHP, Go, Rust, Java and Kotlin wait for the answer of each call.

**TypeScript**

```typescript
return await g.sys.db.getAll({
    instance: "INSTANCE_NAME", database: 'DB_NAME', collection: "COLLECTION_NAME",
});
```

**Python**

```python
return await g.sys.db.get_all('INSTANCE_NAME', 'DB_NAME', 'COLLECTION_NAME')
```

**PHP**

```php
return $g->sys->db->getAll('INSTANCE_NAME', 'DB_NAME', 'COLLECTION_NAME');
```

**C#**

```csharp
return await g.Sys.Db.GetAll(new GetAll("INSTANCE_NAME", "DB_NAME", "COLLECTION_NAME"));
```

**Go**

```go
return g.Sys.Db.GetAll(am.GetAll{Instance: "INSTANCE_NAME", Database: "DB_NAME", Collection: "COLLECTION_NAME"})
```

**Rust**

```rust
Ok(json!(g.sys.db.get_all(GetAll::new("INSTANCE_NAME", "DB_NAME", "COLLECTION_NAME"))?))
```

**Java**

```java
return g.sys.db.getAll(new GetAll("INSTANCE_NAME", "DB_NAME", "COLLECTION_NAME"));
```

**Kotlin**

```kotlin
return g.sys.db.getAll(GetAll("INSTANCE_NAME", "DB_NAME", "COLLECTION_NAME"))
```

## The whole envelope : `true`, or `full`

- A `g.sys` call returns the data and **throws** the errors of the API.
- With `true` as the second argument in TypeScript, or through `full` in the other languages (`g.sys.db.full`, `g.Sys.Db.Full`), it returns the whole envelope `{ success, statusCode, data, errors, warnings }` and never throws : read `success` yourself.

**TypeScript**

```typescript
let saveData = await g.sys.db.saveSingleOrMultiple({
    instance: "INSTANCE_NAME", database: 'DB_NAME', collection: "COLLECTION_NAME",
    saveData: {},
    headers: {}
}, true);
let countOfAllData = await g.sys.db.count({
    instance: "INSTANCE_NAME", database: 'DB_NAME', collection: "COLLECTION_NAME",
}, true);
return { saveData, countOfAllData };
```

**Python**

```python
save_data = await g.sys.db.full.save_single_or_multiple('INSTANCE_NAME', 'DB_NAME', 'COLLECTION_NAME', {}, headers={})
count_of_all_data = await g.sys.db.full.count('INSTANCE_NAME', 'DB_NAME', 'COLLECTION_NAME')
return {'saveData': save_data, 'countOfAllData': count_of_all_data}
```

**PHP**

```php
$saveData = $g->sys->db->full->saveSingleOrMultiple('INSTANCE_NAME', 'DB_NAME', 'COLLECTION_NAME', new AmObject(), headers: new AmObject());
$countOfAllData = $g->sys->db->full->count('INSTANCE_NAME', 'DB_NAME', 'COLLECTION_NAME');
return ['saveData' => $saveData, 'countOfAllData' => $countOfAllData];
```

**C#**

```csharp
var saveData = await g.Sys.Db.Full.SaveSingleOrMultiple(new Save("INSTANCE_NAME", "DB_NAME", "COLLECTION_NAME", new JsonObject()) { Headers = new JsonObject() });
var countOfAllData = await g.Sys.Db.Full.Count(new Count("INSTANCE_NAME", "DB_NAME", "COLLECTION_NAME"));
return new { saveData, countOfAllData };
```

**Go**

```go
saveData, err := g.Sys.Db.Full.SaveSingleOrMultiple(am.Save{Instance: "INSTANCE_NAME", Database: "DB_NAME", Collection: "COLLECTION_NAME", SaveData: am.M{}, Headers: am.M{}})
if err != nil {
	return nil, err
}
countOfAllData, err := g.Sys.Db.Full.Count(am.Count{Instance: "INSTANCE_NAME", Database: "DB_NAME", Collection: "COLLECTION_NAME"})
if err != nil {
	return nil, err
}
return am.M{"saveData": saveData, "countOfAllData": countOfAllData}, nil
```

**Rust**

```rust
let save_data = g.sys.db.full.save_single_or_multiple(Save::new("INSTANCE_NAME", "DB_NAME", "COLLECTION_NAME", json!({})).headers(json!({})))?;
let count_of_all_data = g.sys.db.full.count(Count::new("INSTANCE_NAME", "DB_NAME", "COLLECTION_NAME"))?;
Ok(json!({ "saveData": { "success": save_data.success, "data": save_data.data.row() }, "countOfAllData": { "success": count_of_all_data.success, "data": count_of_all_data.data } }))
```

**Java**

```java
ApiResponse<Saved> saveData = g.sys.db.full.saveSingleOrMultiple(new Save("INSTANCE_NAME", "DB_NAME", "COLLECTION_NAME", Map.of()).headers(Map.of()));
ApiResponse<Long> countOfAllData = g.sys.db.full.count(new Count("INSTANCE_NAME", "DB_NAME", "COLLECTION_NAME"));
return Map.of("saveData", saveData, "countOfAllData", countOfAllData);
```

**Kotlin**

```kotlin
val saveData = g.sys.db.full.saveSingleOrMultiple(Save("INSTANCE_NAME", "DB_NAME", "COLLECTION_NAME", mapOf<String, Any?>()).headers(mapOf<String, Any?>()))
val countOfAllData = g.sys.db.full.count(Count("INSTANCE_NAME", "DB_NAME", "COLLECTION_NAME"))
return mapOf("saveData" to saveData, "countOfAllData" to countOfAllData)
```

## Throw an error

- An error raised ends the call with `success: false`, the message in `errors` and its code as the status. A message listed in the `errorList` of the API can be [translated](https://docs.apimaker.dev/v4/docs/i18/i18.html).

**TypeScript**

```typescript
let saveData = await g.sys.db.saveSingleOrMultiple({
    instance: "INSTANCE_NAME", database: 'DB_NAME', collection: "COLLECTION_NAME",
    saveData: {},
    headers: {}
}, true);
if (!saveData.success) throw [{ code: T.EStatusCode.BAD_REQUEST, message: 'Your data is not saved!' }];
return saveData;
```

**Python**

```python
save_data = await g.sys.db.full.save_single_or_multiple('INSTANCE_NAME', 'DB_NAME', 'COLLECTION_NAME', {}, headers={})
if not save_data.success:
    raise Errors('Your data is not saved!', code=400)
return save_data
```

**PHP**

```php
$saveData = $g->sys->db->full->saveSingleOrMultiple('INSTANCE_NAME', 'DB_NAME', 'COLLECTION_NAME', new AmObject(), headers: new AmObject());
if (!$saveData->success) {
    throw new AmErrors('Your data is not saved!', 400);
}
return $saveData;
```

**C#**

```csharp
var saveData = await g.Sys.Db.Full.SaveSingleOrMultiple(new Save("INSTANCE_NAME", "DB_NAME", "COLLECTION_NAME", new JsonObject()) { Headers = new JsonObject() });
if (!saveData.Success) throw new AmErrors("Your data is not saved!", 400);
return saveData;
```

**Go**

```go
saveData, err := g.Sys.Db.Full.SaveSingleOrMultiple(am.Save{Instance: "INSTANCE_NAME", Database: "DB_NAME", Collection: "COLLECTION_NAME", SaveData: am.M{}, Headers: am.M{}})
if err != nil {
	return nil, err
}
if !saveData.Success {
	return nil, am.Fail(400, "Your data is not saved!")
}
return saveData, nil
```

**Rust**

```rust
let save_data = g.sys.db.full.save_single_or_multiple(Save::new("INSTANCE_NAME", "DB_NAME", "COLLECTION_NAME", json!({})).headers(json!({})))?;
if !save_data.success {
    return Err(am::fail(400, "Your data is not saved!"));
}
Ok(json!({ "success": save_data.success, "data": save_data.data.row() }))
```

**Java**

```java
ApiResponse<Saved> saveData = g.sys.db.full.saveSingleOrMultiple(new Save("INSTANCE_NAME", "DB_NAME", "COLLECTION_NAME", Map.of()).headers(Map.of()));
if (!saveData.isSuccess()) throw new AmErrors("Your data is not saved!", 400);
return saveData;
```

**Kotlin**

```kotlin
val saveData = g.sys.db.full.saveSingleOrMultiple(Save("INSTANCE_NAME", "DB_NAME", "COLLECTION_NAME", mapOf<String, Any?>()).headers(mapOf<String, Any?>()))
if (!saveData.isSuccess) throw AmErrors("Your data is not saved!", 400)
return saveData
```

## Return an image or any binary

- Set `g.res.contentType` and return the base64 of the content.

**TypeScript**

```typescript
g.res.contentType = 'image/gif' as T.EContentType;
return `data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOwA=`
```

**Python**

```python
g.res.content_type = 'image/gif'
return 'data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOwA='
```

**PHP**

```php
$g->res->contentType = 'image/gif';
return 'data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOwA=';
```

**C#**

```csharp
g.Res.ContentType = "image/gif";
return "data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOwA=";
```

**Go**

```go
g.Res.SetContentType("image/gif")
return "data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOwA=", nil
```

**Rust**

```rust
g.res.set_content_type("image/gif");
Ok(json!("data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOwA="))
```

**Java**

```java
g.res.setContentType("image/gif");
return "data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOwA=";
```

**Kotlin**

```kotlin
g.res.contentType = "image/gif"
return "data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOwA="
```

## Download file

**TypeScript**

```typescript
import * as T from 'types';
import * as fs from 'fs';
import * as path from 'path';
async function main(g: T.IAMGlobal) {
    // always write file in the uploads folder which you want to send to user.
    // you can write other files in any directory which you don't want to send to user.
    await fs.promises.mkdir(path.join(__dirname, 'uploads'), { recursive: true });
    let filePath = path.join(__dirname, 'uploads', 'myfile.txt');
    await fs.promises.writeFile(filePath, 'file 1 content', { encoding: 'utf8' });
    return {
        // just provide file name which is written to "uploads" directory
        __am__downloadFilePath: 'myfile.txt',
        __am__downloadFolderFileName: 'newName.txt', // download file will have this name.
    }
};
module.exports = main;
```

**Python**

```python
import os

from am import G


async def main(g: G):
    # Write the files to send in the folder uploads ; the other files anywhere else.
    os.makedirs('uploads', exist_ok=True)
    with open(os.path.join('uploads', 'myfile.txt'), 'w', encoding='utf-8') as file:
        file.write('file 1 content')
    return {
        '__am__downloadFilePath': 'myfile.txt',          # its name in the folder uploads
        '__am__downloadFolderFileName': 'newName.txt',   # the name the file is saved under
    }
```

**PHP**

```php
<?php
function main(AmG $g)
{
    // Write the files to send in the folder uploads ; the other files anywhere else.
    if (!is_dir('uploads')) {
        mkdir('uploads', 0777, true);
    }
    file_put_contents('uploads/myfile.txt', 'file 1 content');
    return [
        '__am__downloadFilePath' => 'myfile.txt',          // its name in the folder uploads
        '__am__downloadFolderFileName' => 'newName.txt',   // the name the file is saved under
    ];
}
```

**C#**

```csharp
using System.IO;

file static class Api
{
    public static async Task<object?> Main(G g)
    {
        // Write the files to send in the folder uploads ; the other files anywhere else.
        Directory.CreateDirectory("uploads");
        await File.WriteAllTextAsync(Path.Combine("uploads", "myfile.txt"), "file 1 content");
        return new JsonObject
        {
            ["__am__downloadFilePath"] = "myfile.txt",          // its name in the folder uploads
            ["__am__downloadFolderFileName"] = "newName.txt",   // the name the file is saved under
        };
    }
}
```

**Go**

```go
package code

import (
	"os"
	"path/filepath"

	"apimaker/am"
)

func Main(g *am.G) (any, error) {
	// Write the files to send in the folder uploads ; the other files anywhere else.
	if err := os.MkdirAll("uploads", 0o755); err != nil {
		return nil, err
	}
	if err := os.WriteFile(filepath.Join("uploads", "myfile.txt"), []byte("file 1 content"), 0o644); err != nil {
		return nil, err
	}
	return am.M{
		"__am__downloadFilePath":       "myfile.txt",  // its name in the folder uploads
		"__am__downloadFolderFileName": "newName.txt", // the name the file is saved under
	}, nil
}
```

**Rust**

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

pub fn main(g: &mut G) -> Result<Value, Errors> {
    // Write the files to send in the folder uploads ; the other files anywhere else.
    std::fs::create_dir_all("uploads")?;
    std::fs::write("uploads/myfile.txt", "file 1 content")?;
    Ok(json!({
        "__am__downloadFilePath": "myfile.txt",          // its name in the folder uploads
        "__am__downloadFolderFileName": "newName.txt",   // the name the file is saved under
    }))
}
```

**Java**

```java
import am.G;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;

class Api {
    static Object main(G g) throws Exception {
        // Write the files to send in the folder uploads ; the other files anywhere else.
        Files.createDirectories(Path.of("uploads"));
        Files.writeString(Path.of("uploads", "myfile.txt"), "file 1 content");
        return Map.of(
            "__am__downloadFilePath", "myfile.txt",          // its name in the folder uploads
            "__am__downloadFolderFileName", "newName.txt");  // the name the file is saved under
    }
}
```

**Kotlin**

```kotlin
import am.G
import java.io.File

fun main(g: G): Any? {
    // Write the files to send in the folder uploads ; the other files anywhere else.
    File("uploads").mkdirs()
    File("uploads", "myfile.txt").writeText("file 1 content")
    return mapOf(
        "__am__downloadFilePath" to "myfile.txt",          // its name in the folder uploads
        "__am__downloadFolderFileName" to "newName.txt",   // the name the file is saved under
    )
}
```

**TypeScript**

```typescript
import * as T from 'types';
import * as fs from 'fs';
import * as path from 'path';
async function main(g: T.IAMGlobal) {
    return {
        __am__downloadFileOrFolderPaths: [
            {
                fsSource: path.join('folder1', 'folder2', 'folder3', 'data_folder'), // path into uploads folder
                archiveDestination: 'data_folder', // path in zip file, so data_folder folder will be directly available in zip file.
            },
        ],
        __am__downloadFolderFileName: `output.zip`, // download file will have this name.
    }
};
module.exports = main;
```

**Python**

```python
from am import G


async def main(g: G):
    return {
        '__am__downloadFileOrFolderPaths': [
            {
                'fsSource': 'folder1/folder2/folder3/data_folder',   # a path in the folder uploads
                'archiveDestination': 'data_folder',                  # its path in the zip file
            },
        ],
        '__am__downloadFolderFileName': 'output.zip',                 # the name the file is saved under
    }
```

**PHP**

```php
<?php
function main(AmG $g)
{
    return [
        '__am__downloadFileOrFolderPaths' => [
            [
                'fsSource' => 'folder1/folder2/folder3/data_folder',   // a path in the folder uploads
                'archiveDestination' => 'data_folder',                  // its path in the zip file
            ],
        ],
        '__am__downloadFolderFileName' => 'output.zip',                 // the name the file is saved under
    ];
}
```

**C#**

```csharp
file static class Api
{
    public static async Task<object?> Main(G g)
    {
        return new JsonObject
        {
            ["__am__downloadFileOrFolderPaths"] = new JsonArray(new JsonObject
            {
                ["fsSource"] = "folder1/folder2/folder3/data_folder",   // a path in the folder uploads
                ["archiveDestination"] = "data_folder",                  // its path in the zip file
            }),
            ["__am__downloadFolderFileName"] = "output.zip",             // the name the file is saved under
        };
    }
}
```

**Go**

```go
package code

import "apimaker/am"

func Main(g *am.G) (any, error) {
	return am.M{
		"__am__downloadFileOrFolderPaths": []any{
			am.M{
				"fsSource":           "folder1/folder2/folder3/data_folder", // a path in the folder uploads
				"archiveDestination": "data_folder",                         // its path in the zip file
			},
		},
		"__am__downloadFolderFileName": "output.zip", // the name the file is saved under
	}, nil
}
```

**Rust**

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

pub fn main(g: &mut G) -> Result<Value, Errors> {
    Ok(json!({
        "__am__downloadFileOrFolderPaths": [
            {
                "fsSource": "folder1/folder2/folder3/data_folder",   // a path in the folder uploads
                "archiveDestination": "data_folder",                  // its path in the zip file
            },
        ],
        "__am__downloadFolderFileName": "output.zip",                 // the name the file is saved under
    }))
}
```

**Java**

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

class Api {
    static Object main(G g) {
        return Map.of(
            "__am__downloadFileOrFolderPaths", List.of(Map.of(
                "fsSource", "folder1/folder2/folder3/data_folder",   // a path in the folder uploads
                "archiveDestination", "data_folder")),               // its path in the zip file
            "__am__downloadFolderFileName", "output.zip");           // the name the file is saved under
    }
}
```

**Kotlin**

```kotlin
import am.G

fun main(g: G): Any? {
    return mapOf(
        "__am__downloadFileOrFolderPaths" to listOf(
            mapOf(
                "fsSource" to "folder1/folder2/folder3/data_folder",   // a path in the folder uploads
                "archiveDestination" to "data_folder",                  // its path in the zip file
            ),
        ),
        "__am__downloadFolderFileName" to "output.zip",                 // the name the file is saved under
    )
}
```

## Download files & folders

**TypeScript**

```typescript
import * as T from 'types';
import * as fs from 'fs';
import * as path from 'path';
async function main(g: T.IAMGlobal) {
    // always write file in the uploads folder which you want to send to user.
    // you can write other files in any directory which you don't want to send to user.
    await fs.promises.mkdir(path.join(__dirname, 'uploads'), { recursive: true });
    let filePath = path.join(__dirname, 'uploads', 'myfile.txt');
    await fs.promises.writeFile(filePath, 'file 1 content', { encoding: 'utf8' });
    return {
        // You can provide list of files or folders, it will will make zip and send to user.
        __am__downloadFileOrFolderPaths: [
            'myfile.txt', // file name
            'someFolder', // folder name
        ],
        __am__downloadFolderFileName: 'zipFile.zip', // this will be zip file name
    }
};
module.exports = main;
```

**Python**

```python
import os

from am import G


async def main(g: G):
    # Write the files to send in the folder uploads ; the other files anywhere else.
    os.makedirs('uploads', exist_ok=True)
    with open(os.path.join('uploads', 'myfile.txt'), 'w', encoding='utf-8') as file:
        file.write('file 1 content')
    return {
        # Files and folders of the folder uploads : they go out as one zip file.
        '__am__downloadFileOrFolderPaths': ['myfile.txt', 'someFolder'],
        '__am__downloadFolderFileName': 'zipFile.zip',   # the name of the zip file
    }
```

**PHP**

```php
<?php
function main(AmG $g)
{
    // Write the files to send in the folder uploads ; the other files anywhere else.
    if (!is_dir('uploads')) {
        mkdir('uploads', 0777, true);
    }
    file_put_contents('uploads/myfile.txt', 'file 1 content');
    return [
        // Files and folders of the folder uploads : they go out as one zip file.
        '__am__downloadFileOrFolderPaths' => ['myfile.txt', 'someFolder'],
        '__am__downloadFolderFileName' => 'zipFile.zip',   // the name of the zip file
    ];
}
```

**C#**

```csharp
using System.IO;

file static class Api
{
    public static async Task<object?> Main(G g)
    {
        // Write the files to send in the folder uploads ; the other files anywhere else.
        Directory.CreateDirectory("uploads");
        await File.WriteAllTextAsync(Path.Combine("uploads", "myfile.txt"), "file 1 content");
        return new JsonObject
        {
            // Files and folders of the folder uploads : they go out as one zip file.
            ["__am__downloadFileOrFolderPaths"] = new JsonArray("myfile.txt", "someFolder"),
            ["__am__downloadFolderFileName"] = "zipFile.zip",   // the name of the zip file
        };
    }
}
```

**Go**

```go
package code

import (
	"os"
	"path/filepath"

	"apimaker/am"
)

func Main(g *am.G) (any, error) {
	// Write the files to send in the folder uploads ; the other files anywhere else.
	if err := os.MkdirAll("uploads", 0o755); err != nil {
		return nil, err
	}
	if err := os.WriteFile(filepath.Join("uploads", "myfile.txt"), []byte("file 1 content"), 0o644); err != nil {
		return nil, err
	}
	return am.M{
		// Files and folders of the folder uploads : they go out as one zip file.
		"__am__downloadFileOrFolderPaths": []string{"myfile.txt", "someFolder"},
		"__am__downloadFolderFileName":    "zipFile.zip", // the name of the zip file
	}, nil
}
```

**Rust**

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

pub fn main(g: &mut G) -> Result<Value, Errors> {
    // Write the files to send in the folder uploads ; the other files anywhere else.
    std::fs::create_dir_all("uploads")?;
    std::fs::write("uploads/myfile.txt", "file 1 content")?;
    Ok(json!({
        // Files and folders of the folder uploads : they go out as one zip file.
        "__am__downloadFileOrFolderPaths": ["myfile.txt", "someFolder"],
        "__am__downloadFolderFileName": "zipFile.zip",   // the name of the zip file
    }))
}
```

**Java**

```java
import am.G;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;

class Api {
    static Object main(G g) throws Exception {
        // Write the files to send in the folder uploads ; the other files anywhere else.
        Files.createDirectories(Path.of("uploads"));
        Files.writeString(Path.of("uploads", "myfile.txt"), "file 1 content");
        return Map.of(
            // Files and folders of the folder uploads : they go out as one zip file.
            "__am__downloadFileOrFolderPaths", List.of("myfile.txt", "someFolder"),
            "__am__downloadFolderFileName", "zipFile.zip");   // the name of the zip file
    }
}
```

**Kotlin**

```kotlin
import am.G
import java.io.File

fun main(g: G): Any? {
    // Write the files to send in the folder uploads ; the other files anywhere else.
    File("uploads").mkdirs()
    File("uploads", "myfile.txt").writeText("file 1 content")
    return mapOf(
        // Files and folders of the folder uploads : they go out as one zip file.
        "__am__downloadFileOrFolderPaths" to listOf("myfile.txt", "someFolder"),
        "__am__downloadFolderFileName" to "zipFile.zip",   // the name of the zip file
    )
}
```

## Download file from Angular sent by custom API

```ts
async function downloadFileFromBrowser() {
    const requestPayload = {};
    const requestHeaders = {};
    const resp = <any>await this.http.post(`API Maker custom API endpoint`, requestPayload, {
        headers: requestHeaders,
        responseType: <any>'text',
        observe: 'response',
    }).toPromise();
    const fileName = resp.headers.get('content-filename');
    await this.downloadFileFromBlob('application/zip', resp.body, fileName);
    return resp;
}

async function downloadFileFromBlob(contentType, base64Data: any, fileName) {
    const a = document.createElement('a');
    document.body.appendChild(a);
    a.style.display = 'none';

    let blob;
    if (base64Data instanceof Blob) {
        blob = base64Data;
    } else {
        const base64Response = await fetch(`data:${contentType};base64,` + base64Data);
        blob = await base64Response.blob();
    }

    const url = window.URL.createObjectURL(blob);
    a.href = url;
    a.download = fileName;
    a.click();
    window.URL.revokeObjectURL(url);
}
```

## Multi-tenant

- Call a custom API with the [x-am-tenant-username](https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html#x-am-tenant-username) header to run it for one tenant of a [multi-tenant](https://docs.apimaker.dev/v4/docs/features/multi-tenant.html) instance.
- It reads the tenant in `g.req.params`, and the APIs it calls with `g.sys.db` and `g.sys.system` work on the database of that tenant: the header is passed on, and each instance finds the tenant in its own tenants table.
- `instance: 'crm::globex'` names another tenant for one call, and an empty tenant header names the structure database. See [Custom APIs of a multi-tenant instance](https://docs.apimaker.dev/v4/docs/features/multi-tenant.html#custom-apis).

```text
GET /api/custom-api/admin/customers-report
x-am-tenant-username: acme
```

**TypeScript**

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

async function main(g: T.IAMGlobal) {
    const tenant = g.req.params.tenantUsername; // 'acme'
    const customers = await g.sys.db.getAll({ instance: 'crm', database: 'crm', collection: 'customers' }); // from the database of acme
    const customersOfGlobex = await g.sys.db.count({ instance: 'crm::globex', database: 'crm', collection: 'customers' });
    return { tenant, customers, customersOfGlobex };
}
module.exports = main;
```

**Python**

```python
from am import G


async def main(g: G):
    tenant = g.req.params.tenantUsername  # 'acme'
    customers = await g.sys.db.get_all('crm', 'crm', 'customers')  # from the database of acme
    customers_of_globex = await g.sys.db.count('crm::globex', 'crm', 'customers')
    return {'tenant': tenant, 'customers': customers, 'customersOfGlobex': customers_of_globex}
```

**PHP**

```php
<?php
function main(AmG $g)
{
    $tenant = $g->req->params->tenantUsername; // 'acme'
    $customers = $g->sys->db->getAll('crm', 'crm', 'customers'); // from the database of acme
    $customersOfGlobex = $g->sys->db->count('crm::globex', 'crm', 'customers');
    return ['tenant' => $tenant, 'customers' => $customers, 'customersOfGlobex' => $customersOfGlobex];
}
```

**C#**

```csharp
file static class Api
{
    public static async Task<object?> Main(G g)
    {
        var tenant = g.Req.Params.GetString("tenantUsername"); // "acme"
        var customers = await g.Sys.Db.GetAll(new GetAll("crm", "crm", "customers")); // from the database of acme
        var customersOfGlobex = await g.Sys.Db.Count(new Count("crm::globex", "crm", "customers"));
        return new { tenant, customers, customersOfGlobex };
    }
}
```

**Go**

```go
package code

import "apimaker/am"

func Main(g *am.G) (any, error) {
	tenant := g.Req.Params().String("tenantUsername") // "acme"
	customers, err := g.Sys.Db.GetAll(am.GetAll{Instance: "crm", Database: "crm", Collection: "customers"}) // from the database of acme
	if err != nil {
		return nil, err
	}
	customersOfGlobex, err := g.Sys.Db.Count(am.Count{Instance: "crm::globex", Database: "crm", Collection: "customers"})
	if err != nil {
		return nil, err
	}
	return am.M{"tenant": tenant, "customers": customers, "customersOfGlobex": customersOfGlobex}, nil
}
```

**Rust**

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

pub fn main(g: &mut G) -> Result<Value, Errors> {
    let tenant = g.req.params()["tenantUsername"].clone(); // "acme"
    let customers = g.sys.db.get_all(GetAll::new("crm", "crm", "customers"))?; // from the database of acme
    let customers_of_globex = g.sys.db.count(Count::new("crm::globex", "crm", "customers"))?;
    Ok(json!({ "tenant": tenant, "customers": customers, "customersOfGlobex": customers_of_globex }))
}
```

**Java**

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

class Api {
    static Object main(G g) {
        String tenant = g.req.getParams().getString("tenantUsername"); // "acme"
        List<JsonObject> customers = g.sys.db.getAll(new GetAll("crm", "crm", "customers")); // from the database of acme
        long customersOfGlobex = g.sys.db.count(new Count("crm::globex", "crm", "customers"));
        return JsonObject.of("tenant", tenant, "customers", customers, "customersOfGlobex", customersOfGlobex);
    }
}
```

**Kotlin**

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

fun main(g: G): Any? {
    val tenant = g.req.params.getString("tenantUsername") // "acme"
    val customers = g.sys.db.getAll(GetAll("crm", "crm", "customers")) // from the database of acme
    val customersOfGlobex = g.sys.db.count(Count("crm::globex", "crm", "customers"))
    return mapOf("tenant" to tenant, "customers" to customers, "customersOfGlobex" to customersOfGlobex)
}
```
