# Go

> Write custom APIs, hooks, schedulers and event listeners of API Maker in Go. func Main(g *am.G) (any, error), g.Req, g.Res, the typed calls of g.Sys.Db (am.GetAll, []am.M), am.Fail, Go modules, with many examples.

Source: https://docs.apimaker.dev/v4/docs/languages/go.html

Code in Go defines `func Main(g *am.G) (any, error)` in a package which imports `apimaker/am`. It is built once as a plugin of the runner, on the official image of Go (`golang:1.27` unless the [settings](https://docs.apimaker.dev/v4/docs/settings/sandboxSettings.html) name another version), with only the modules you add.

> **In short**
>
> - Write `func Main(g *am.G) (any, error)` in a package that imports `apimaker/am`.
> - It is built once, on `golang:1.27` unless the settings name another version.
> - Refuse a request with `return nil, am.Fail(400, message)`.

**hello.go**

```go
package code

import "apimaker/am"

func Main(g *am.G) (any, error) {
	name := g.Req.Body().String("name")
	if name == "" {
		name = "world"
	}
	return am.M{"hello": name}, nil
}
```

| | |
|---|---|
| Runtime | Go 1.21 or later, a version of `golang:<version>` |
| `Main` | `func Main(g *am.G) (any, error)` : what it returns is the answer, an error refuses |
| Calls of `g.Sys` | typed : what a call takes is a struct (`am.GetAll{…}`), what it answers has its type (`[]am.M`, `int`, `am.UpdateManyResult`) |
| Values | `am.M` (`map[string]any`) for objects and rows, `[]any` for lists, `float64` for numbers |
| Package | any name, `package code` in a new item : the sandbox builds it as a plugin whatever it is named. In [git](https://docs.apimaker.dev/v4/docs/Git/git.html#project-of-every-language) every piece of code is a package of its own, in a folder of its own |
| Packages | Go modules : `github.com/google/uuid` `v1.6.0` |
| Printing | `fmt.Println` and `log` go to the console of the account |

## The request and the answer

`g.Req` and `g.Res` have a method for each key they are known to have : `g.Req.Body()`, `g.Req.Query()`, `g.Req.Params()`, `g.Req.Headers()`, `g.Res.SetStatusCode(201)`. What they give is an `am.M` : `Map`, `List`, `String`, `Float`, `Int` and `Bool` read a key without a type assertion and give the zero value when it is not there. The map is the one inside the request, so changing it changes the request.

A body which is a list (`[{…}, {…}]`, several rows to save) is read with `g.Req.List("body")` ; `g.Req.Body()` is an empty `am.M` then.

**Read the request, shape the answer**

```go
package code

import "apimaker/am"

func Main(g *am.G) (any, error) {
	status := g.Req.Query().String("status")
	if status == "" {
		status = "open"
	}
	_, signed := g.Req.Headers()["x-am-authorization"]
	g.Res.SetStatusCode(201)
	g.Res.Headers()["X-Request-Kind"] = "orders"
	return am.M{"customer": g.Req.Params().String("id"), "status": status, "signed": signed}, nil
}
```

### Bytes as the answer

A file, a picture, a PDF : return its bytes (a `[]byte`), or give them to `g.Res.SetOutput`, and name their kind of content. The answer is those bytes as they are, whatever the kind of content, with the headers of the code. (A text under a kind of bytes, `image/gif`, is read as base64.)

**A picture as the answer**

```go
package code

import (
	"encoding/base64"

	"apimaker/am"
)

func Main(g *am.G) (any, error) {
	picture, err := base64.StdEncoding.DecodeString("R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOwA=")
	if err != nil {
		return nil, err
	}
	g.Res.SetContentType("image/gif")
	g.Res.Headers()["Content-Disposition"] = `inline; filename="pixel.gif"`
	return picture, nil
}
```

## Calling API Maker : `g.Sys`

Every call has its types. What it takes is a struct of `am` whose fields are its keys (`am.GetAll`, `am.GetById`, `am.Count`…) ; what it answers is typed too : rows are a `[]am.M`, a row an `am.M` (`nil` when there is none), a count an `int`, a key of the cache a `*string` (`nil` when it is not there), what an API describes a struct (`am.UpdateManyResult`).

**Database calls**

```go
package code

import "apimaker/am"

func Main(g *am.G) (any, error) {
	rows, err := g.Sys.Db.GetAll(am.GetAll{Instance: "mysql_8", Database: "inventory", Collection: "customers", Limit: 10, Sort: "-customer_id"})
	if err != nil {
		return nil, err
	}
	two, err := g.Sys.Db.GetById(am.GetById{Instance: "mysql_8", Database: "inventory", Collection: "customers", Id: 2})
	if err != nil {
		return nil, err
	}
	count, err := g.Sys.Db.Count(am.Count{Instance: "mysql_8", Database: "inventory", Collection: "customers", Find: am.M{"country": "IN"}})
	if err != nil {
		return nil, err
	}
	var first am.M
	if len(rows) > 0 {
		first = rows[0]
	}
	return am.M{"first": first, "name": two.String("first_name"), "inIndia": count}, nil
}
```

- A key which is left out is not sent. A flag which is on by default is a pointer, so `false` can be said : `NoCycle: am.Ptr(false)`.
- A key the struct has no field for goes in `Other` : `am.GetAll{Instance: "mysql_8", Other: am.M{"cursor": "abc"}}`.
- A row as a struct of your own : `customer, err := am.To[Customer](rows[0])`.

**What a call answers has its type**

```go
package code

import "apimaker/am"

func Main(g *am.G) (any, error) {
	changed, err := g.Sys.Db.UpdateMany(am.UpdateMany{Instance: "mysql_8", Database: "inventory", Collection: "customers",
		Find: am.M{"country": "IN"}, UpdateData: am.M{"active": true}})
	if err != nil {
		return nil, err
	}
	// What a save answers : saved.Row (one row was saved), saved.Rows (a list was), saved.IsList
	saved, err := g.Sys.Db.SaveSingleOrMultiple(am.Save{Instance: "mysql_8", Database: "inventory", Collection: "customers",
		SaveData: am.M{"first_name": "Ada"}})
	if err != nil {
		return nil, err
	}
	// The whole answer of a call : whole.Success, whole.StatusCode, whole.Data ([]am.M), whole.TotalCount, whole.Errors
	whole, err := g.Sys.Db.Full.GetAll(am.GetAll{Instance: "mysql_8", Database: "inventory", Collection: "customers", GetTotalCount: true})
	if err != nil {
		return nil, err
	}
	return am.M{"updated": changed.UpdatedRowsCount, "id": saved.Row["customer_id"], "of": whole.TotalCount, "rows": len(whole.Data)}, nil
}
```

Calls at the same time are goroutines :

**Calls at the same time**

```go
package code

import (
	"sync"

	"apimaker/am"
)

func Main(g *am.G) (any, error) {
	counts := map[string]any{}
	var lock sync.Mutex
	var wait sync.WaitGroup
	for _, collection := range []string{"orders", "products"} {
		wait.Add(1)
		go func(collection string) {
			defer wait.Done()
			count, _ := g.Sys.Db.Count(am.Count{Instance: "mongodb", Database: "shop", Collection: collection})
			lock.Lock()
			counts[collection] = count
			lock.Unlock()
		}(collection)
	}
	wait.Wait()
	return counts, nil
}
```

### Rows as they arrive

**Sum a large table**

```go
package code

import "apimaker/am"

func Main(g *am.G) (any, error) {
	total := 0.0
	err := g.Sys.Db.GetAllByStream(am.GetAll{Instance: "mysql_8", Database: "inventory", Collection: "orders"}, func(row am.M) error {
		total += row.Float("amount")
		return nil
	})
	return am.M{"amount": total}, err
}
```

### System APIs and cache

**Encrypt, hash, events and cache keys**

```go
package code

import "apimaker/am"

func Main(g *am.G) (any, error) {
	sealed, err := g.Sys.System.Encrypt(am.M{"card": "4111"})
	if err != nil {
		return nil, err
	}
	digest, _ := g.Sys.System.Hash("my text")
	_, _ = g.Sys.System.EmitEvent("order_created", am.M{"id": 7}, "notify_shop")
	_, _ = g.Sys.Cache.SetKey("last_order", "7", 600)
	last, _ := g.Sys.Cache.GetKey("last_order")
	return am.M{"sealed": sealed, "digest": digest, "last": last}, nil
}
```

## Errors

Return `am.Fail(code, message)` to refuse, or an `*am.Errors` with several errors :

**Refuse a request**

```go
package code

import "apimaker/am"

func Main(g *am.G) (any, error) {
	if g.Req.Body().Get("quantity") == nil {
		return nil, am.Fail(400, "'quantity' is missing.")
	}
	return am.M{"ok": true}, nil
}
```

A call which fails gives an `*am.Errors` with the errors of the server :

**Catch the errors of a call**

```go
package code

import (
	"errors"

	"apimaker/am"
)

func Main(g *am.G) (any, error) {
	_, err := g.Sys.Db.RemoveById(am.RemoveById{Instance: "mysql_8", Database: "inventory", Collection: "customers", Id: 404})
	var refused *am.Errors
	if errors.As(err, &refused) {
		g.Logger.Warn("not removed :", refused.Error())
		return am.M{"removed": false, "code": refused.Code()}, nil
	}
	return am.M{"removed": err == nil}, err
}
```

- Another error answers `500` with its message. A panic answers `500` with `panic: …` and the lines of your code (`at orders.go:14 in helper()`).
- A panic in a goroutine you started ends the runner : recover inside your goroutines.

## Logs, printing and shared values

**Logs and shared values**

```go
package code

import (
	"fmt"

	"apimaker/am"
)

func Main(g *am.G) (any, error) {
	g.Logger.Log("order", 7, "paid")           // "order 7 paid"
	g.Logger.Error(am.M{"step": "payment"})     // maps, slices and structs as indented JSON
	fmt.Println("kept in the console of the account")
	// Lives as long as the runner : Update changes a value while no other request does.
	calls := g.Shared.Update("calls", func(old any) any {
		count, _ := old.(int)
		return count + 1
	})
	return calls, nil
}
```

## Hooks

**Pre hook : only my rows**

```go
package code

import "apimaker/am"

func Main(g *am.G) (any, error) {
	person := g.Req.Auth().Map("authAMDB")
	query := g.Req.Query()
	find := query.Map("find")
	if find == nil {
		find = am.M{}
		query["find"] = find
	}
	find["owner_id"] = person.Get("id")
	return nil, nil
}
```

**Post hook : hide a column**

```go
package code

import "apimaker/am"

func Main(g *am.G) (any, error) {
	for _, row := range g.Res.List("output") {
		delete(am.AsM(row), "password")
	}
	return nil, nil
}
```

## Utility classes

A [utility class](https://docs.apimaker.dev/v4/docs/utility-class/utility-class.html) written in Go is a package. The other Go code of the account imports it by the folder it has in the Git repository, which is the module `apimaker` :

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

```go
package tax

import round "apimaker/src/utility_classes/utils---Round" // another utility class

var Rate = 0.2

func Add(amount float64) float64 {
	return round.Cents(amount * (1 + Rate))
}
```

**A custom API which uses it**

```go
package code

import (
	"apimaker/am"
	tax "apimaker/src/utility_classes/utils---Tax"
)

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

- The folder of a class is `src/utility_classes/<folder>---<name>` (`utils---Tax` for the class `Tax` in `utils/`) : letters of a to z, digits, `.`, `-` and `_`. The name after `package` is yours; name the import as you like.
- The package is built once and shared by all Go code of the account in a sandbox : what it keeps in a variable is kept there. A class which is saved again is another build, which the code gets when it is compiled again on its next run.
- `go build ./...` in the repository builds the same imports : nothing else is needed.

## Test cases

A [test case](https://docs.apimaker.dev/v4/docs/test-cases/test-cases.html#other-languages) written in Go lists its tests in `Tests`, in the place of a `Main`. A test is a `func(g *am.G) error` : the error it returns, or what it panics with, is its failure.

**A test case**

```go
package code

import (
	"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
}

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

- `g.Sys.Test` : `RunCustomApi(am.RunCustomApi{…})`, `RunCustomApiFull(…)` (the whole answer : `Data`, `StatusCode`, `Headers`, `Logs`), `Mock(am.Mock{…})`, `Mocks([]am.Mock{…})`, `ClearMocks(method...)`, `Calls(method...)`.
- The custom API under test can be written in any language.

## Packages

Add `github.com/google/uuid` in [Sandbox Settings](https://docs.apimaker.dev/v4/docs/settings/sandboxSettings.html#packages), then :

**A Go module**

```go
package code

import (
	"github.com/google/uuid"

	"apimaker/am"
)

func Main(g *am.G) (any, error) {
	return am.M{"id": uuid.NewString()}, nil
}
```

- A version without `v` gets one : `1.6.0` is `v1.6.0`; empty is `latest`.
- Modules are downloaded when the image is built; while code runs nothing is downloaded (`GOPROXY=off`).

## How it runs

- Each piece of code is built once as a plugin of the runner (about a second), from the same module as the runner : the packages of the account and `apimaker/am` are shared. Saving starts the build in the background; code which does not compile is reported in the console of the account (`main.go:6:9: undefined: missing`).
- Every request is a goroutine; requests run at the same time, on four threads of Go at most. Code of an API waits for its database most of the time, and Go wakes an idle thread for every goroutine which becomes ready : with a thread for every core of a big machine that costs more than the request. Code which computes on many cores sets `GOMAXPROCS` to the number it wants, with an `ENV` line of the [Dockerfile](https://docs.apimaker.dev/v4/docs/settings/sandboxSettings.html#dockerfile) of Go : `ENV GOMAXPROCS=16`.
- Variables at package level live as long as the runner : protect them with a mutex, or use `g.Shared`.

## In the code editor

The editor completes `g` as Go writes it, the names of your instances, databases and tables, and the columns of a table on the rows a call answers. More in [the code editor in every language](https://docs.apimaker.dev/v4/docs/languages/index.html#editor).

**Where the editor helps**

```go
package code

import "apimaker/am"

func Main(g *am.G) (any, error) {
	// g.  →  Req, Res, Sys, Logger, Shared      g.Sys.Db.  →  GetAll, GetById, Query, Gen, Full…
	g.Logger.Log(g.Req.Body())  // g.Req.  →  Body, Query, Headers, Auth…      g.Res.  →  SetStatusCode, Headers…
	rows, err := g.Sys.Db.GetAll(am.GetAll{Instance: "mysql_8", Database: "inventory", Collection: "customers"})
	//           am.GetAll{  →  Instance, Database, Collection, Find, Limit, Sort…
	//           Instance: "  →  your instances   Collection: "  →  the tables of mysql_8.inventory
	if err != nil {
		return nil, err
	}
	for _, row := range rows {
		g.Logger.Log(row.String("first_name"))  // .String("  →  the columns of customers
	}
	return rows, nil
}
```

- `am.` offers what the package has : the structs of what the calls take (`am.GetAll`, `am.Save`…), `am.M`, `am.Fail`, `am.To`. Between the braces of such a struct the editor offers its fields, each with what it is for ; inside `Find: am.M{"` the columns of the table, inside `Select: "` and `Sort: "` too.
- What a call answers has its type : on the rows of a `GetAll` the columns are offered inside the quotes of `Get`, `Map`, `List`, `String`, `Float`, `Int` and `Bool`, and of an index (`rows[0]["`) ; `changed.` offers `UpdatedRowsCount`, `saved.` offers `Row`, `Rows` and `IsList`, `whole.` (a call of `g.Sys.Db.Full`) `Success`, `Data`, `Errors`…
- `g.Req.` and `g.Res.` offer a method for each key of the request and of the answer ; `g.Req.Auth().` the opened tokens.

## Reference

| `g.` | Go |
|---|---|
| `Req` | `Body()`, `Query()`, `Params()`, `Headers()`, `Auth()`, `ReqInfo()` (each an `am.M`), `EventData()`, `IsApiRequestFromUser()`, and `Set…` for each |
| `Res` | `SetStatusCode`, `SetContentType`, `Headers()`, `Shared()`, `Output()`, `SetOutput`, `Errors()`, `Warnings()` |
| an `am.M` | `Get`, `Map`, `List`, `String`, `Float`, `Int`, `Bool` |
| `Sys.Db` | `GetAll(am.GetAll)`, `GetById(am.GetById)`, `Query(am.Query)`, `SaveSingleOrMultiple(am.Save)`, `MasterSave(am.Save)`, `ArrayOperations(am.ArrayOperations)`, `UpdateById(am.UpdateById)`, `UpdateMany(am.UpdateMany)`, `ReplaceById(am.UpdateById)`, `RemoveById(am.RemoveById)`, `RemoveByQuery(am.RemoveByQuery)`, `Aggregate(am.Aggregate)`, `Count(am.Count)`, `Distinct(am.Distinct)`, `DistinctQuery(am.Distinct)`, `GetAllByStream`, `QueryByStream`; `Sys.Db.Gen.GetAllGen`… |
| `Sys.Db.Full`, `Sys.Db.Gen.Full`, `Sys.System.Full`, `Sys.Cache.Full` | the same calls, each with its whole answer : `am.ApiResponse[T]` (`Success`, `StatusCode`, `Data`, `TotalCount`, `Errors`, `Warnings`) |
| `Sys.System` | `Encrypt`, `EncryptWith`, `Decrypt`, `DecryptWith`, `Hash`, `GetSecret`, `GetSecrets`, `CallExternalApi`, `CallExternalApis`, `GetToken`, `GetTokens`, `ExecuteQuery`, `GetTableMeta`, `GetTableMetas`, `CreateIndexes`, `DropIndexes`, `DropIndexesOfMany`, `GetIndexes`, `GetIndexesOfMany`, `EmitEvent`, `EmitEventWS`, `IsValidDataForTable(s)`, `IsValidDataForCustomAPI(s)`, `MultiTenantInstance(s)Updated`, `IsValidConnectionString(s)`, `GetLock`, `ReleaseLock` |
| `Sys.Cache` | `GetKey`, `GetKeys`, `SetKey`, `SetKeys`, `RemoveKey`, `RemoveKeys`, `ResetCacheDB`, `ResetCacheDBs`, `ResetCacheCustomApis`, `ResetCacheCustomApisOfTenant`, `ResetCacheSystemApis`, `ResetCacheSystemApisOfTenant` |
| `Sys.Call("db.getAll", …)` | any API by its name in JavaScript, with its arguments as they are |
| `Logger` | `Log`, `Debug`, `Info`, `Warn`, `Error` |
| `Shared` | `Get`, `Set`, `Update` |
| errors | `am.Fail(code, message)`, `&am.Errors{List: []am.M{…}}` |

## Debugging

- The debug runner of Go builds your code without optimizations and runs under Delve : the **Debug** tab of the API testing page steps through it.
- A panic stops the code when **Pause on exceptions** is on.
- Code on the native process of a Mac needs the developer mode of the Mac (`sudo DevToolsSecurity -enable`) : Delve works through the debugserver of Xcode.
- Learn more [Debugging](https://docs.apimaker.dev/v4/docs/features/debugging.html).

## Related

- [Languages and runtimes](https://docs.apimaker.dev/v4/docs/languages/index.html) · [Sandbox settings](https://docs.apimaker.dev/v4/docs/settings/sandboxSettings.html) · [The global object g](https://docs.apimaker.dev/v4/docs/pre-defined-terms/global-object-g.html)
