# client go (AsyncAPI)

Source: https://apigenie.pl/techdocs/reference/cli-generate_client_go_asyncapi

Generate a Go Kafka client package for the Kafka channels of an AsyncAPI document.

## Usage

```sh
apigenie-generator client go [...OPTIONS] INPUT_FILE
```

## Description

`apigenie-generator client go` generates from OpenAPI documents (see
[apigenie-generator client go](/techdocs/reference/cli-generate_client_go)) and from AsyncAPI 3.x documents,
telling them apart by their `openapi` or `asyncapi` field. Documents referencing other files are
bundled first.

From an AsyncAPI document it generates a Go module (Go 1.22) with one package, using the Kafka
client [franz-go](https://github.com/twmb/franz-go) (`github.com/twmb/franz-go/pkg/kgo`, pinned
at v1.18.1 in `go.mod`, with a complete `go.sum`). By default the client is the counterpart of
the application the document describes: it sends the messages of the application's `receive`
operations and receives those of its `send` operations. Set the `perspective` option to
`application` to generate the described application's side instead.
[apigenie-generator server go](/techdocs/reference/cli-generate_server_go_asyncapi) generates the other
side as an application with its own `main.go`.

The generator writes the Kafka channels: the channels of servers with the `kafka` or
`kafka-secure` protocol, and channels with a Kafka binding in documents without servers. Channels
of other protocols are skipped and reported as info.

With `perspective: application`, the client of the Carpet Express Event Platform sends the
shipment events and requests labels:

```go
client, err := events.NewClient(events.Options{
	Brokers:  []string{"localhost:9092"},
	ClientID: "label-printer",
})
if err != nil {
	log.Fatal(err)
}
defer client.Close()

// a sent operation with several messages takes any of them
err = client.SendPublishShipmentEvents(ctx, delivered)

// request/reply: waits for the reply with the request's correlation id
reply, err := client.RequestLabelRender(ctx, request)
switch reply := reply.(type) {
case events.LabelRendered:
	fmt.Println(reply.Payload.URL)
case events.LabelRenderFailed:
	fmt.Println(reply.Payload.Title)
}
```

In the default perspective, it receives them and renders the labels:

```go
type labelFarm struct{}

func (labelFarm) OnRenderLabel(ctx context.Context, message events.RenderLabel) (events.RequestLabelRenderReply, error) {
	var reply events.LabelRendered
	reply.Key = message.Key
	reply.Payload = render(message.Payload)
	return reply, nil // sent to TopicLabelRenderResults with the request's correlation id
}

func (labelFarm) OnUnknown(ctx context.Context, record *kgo.Record) error { return nil }

// received operations are consumed in the background until Close
err = client.ListenRequestLabelRender(labelFarm{})
err = client.ListenPublishShipmentEvents(shipmentTracker{})
```

The package is named by `package_name`, `--package-name` or the document's title (letters and
digits, lower case):

| File | Contents |
| ---- | -------- |
| `types.gen.go` | the types of the schemas the payloads use: structs with `json` tags, enums, and unions as a struct whose `Value` holds one variant of a sealed interface; each with a `Validate` method |
| `messages.gen.go` | a struct per message (`Key`, `Headers`, `Payload`), its headers struct and header constants, and the code writing, reading and validating records |
| `kafka.gen.go` | the server hosts, topics, group and client ids, the handler and message interfaces, and `Client` |
| `runtime_*.gen.go` | the Kafka runtime (consumers, requesters, headers), validation, `Nullable[T]`, `Date` |
| `go.mod`, `go.sum` | the module, requiring franz-go |

- **The client.** `NewClient(options)` creates the producer and, for every operation with a
  reply, one consumer of its reply topic that all of that operation's requests share. `Close`
  stops the listeners, commits the offsets of the records they handled, leaves their consumer
  groups and closes the clients. `Options` takes the brokers, `ClientID` (replacing the
  generated `ProducerClientID`), `ClientOptions` (franz-go options added to every client, e.g.
  `kgo.SASL`, `kgo.DialTLSConfig` or `kgo.AllowAutoTopicCreation`), `GroupIDs` (consumer groups
  by operation id), `OnError` and `ReplyTimeout`.
- **Sent operations.** `Send<Operation>(ctx, message)` validates the message, writes its key,
  its headers (constant headers such as the `ce_specversion` and `ce_type` of CloudEvents
  included) and its JSON payload to the operation's topic, and returns once the brokers have it.
  With several messages, it takes an interface (`PublishShipmentEventsMessage`) that each of them
  implements.
- **Request/reply** with a reply channel: `Request<Operation>(ctx, message)` sets a new
  correlation id unless the message has one, sends the request and waits for the reply with that
  id until the context is done (`Options.ReplyTimeout`, 30 seconds by default, when the context
  has no deadline). It returns the reply message, or an interface of the reply messages
  (`RequestLabelRenderReply`) when there are several. An operation named after the verb isn't
  repeated: `requestLabelRender` gives `RequestLabelRender`.
- **Received operations.** `Listen<Operation>(handler)` consumes the operation's topic in the
  background, in its consumer group, until `Close`. The handler interface
  (`PublishShipmentEventsHandler`) has an `On<Message>` method per message and `OnUnknown`.
  Several messages are told apart by a payload property or a header with a distinct constant
  value (e.g. `type` or `ce_type`); otherwise the first message whose payload decodes strictly
  and is valid is chosen. Records that aren't valid messages, and handler errors, go to
  `Options.OnError` (logged by default). An operation with a reply gets `On<Message>(ctx,
  message) (Reply, error)`: the reply is sent to the reply topic with the request's correlation
  id copied.

Group and client ids come from the Kafka operation bindings as for
[apigenie-generator server go](/techdocs/reference/cli-generate_server_go_asyncapi#description).

Not supported, and reported as warnings: Avro payloads and payloads of other non-JSON schema
formats (the messages are skipped, and operations left without a message too), reply addresses
given by runtime expressions, and Kafka topics with parameters. Security (SASL/SCRAM, mTLS) is not
configured: pass `kgo.SASL` or `kgo.DialTLSConfig` in `Options.ClientOptions`. `Validate`
doesn't check `not`, `if`/`then`/`else` and `dependencies`, tuples are `[]any` (only their item
count is checked), and `contentEncoding: base64` strings stay strings.

## Extensions

| Extension | Effect |
| --------- | ------ |
| `x-go-name` | name of the type of a schema, or of the field of a property |
| `x-go-type` | Go type of a schema, e.g. `decimal.Decimal`; with `x-go-type-import: {path: ...}` for its package |
| `x-go-type-skip-optional-pointer` | the optional property isn't a pointer |
| `x-go-json-ignore` | the field gets the tag `json:"-"` |
| `x-omitempty` | `true` or `false` overrides the `omitempty` of the field |
| `x-enum-varnames`, `x-enumNames` | names of the enum constants |
| `x-enum-descriptions` | comments of the enum constants |
| `x-apigenie.codegen.name` | name of the type of a component schema, when it has no `x-go-name` |
| `x-apigenie.codegen.disabled` | `true` on a channel or an operation leaves it out (with the operations of a channel left out) |

The extensions can live in an overlay for this target instead of in the document, e.g.
`go.overlay.yaml`:

```yaml
overlay: 1.0.0
info:
  title: Go types
  version: 1.0.0
actions:
  - target: $.components.schemas.Order
    update:
      x-go-name: PurchaseOrder
  - target: $.components.schemas.Order.properties.total
    update:
      x-go-type: decimal.Decimal
      x-go-type-import:
        path: github.com/shopspring/decimal
```

```sh
apigenie-generator client go asyncapi.yaml --overlay go.overlay.yaml -o out
```

## Options

### -c, --config

Path to the configuration file to use. See the "Configuration" section for more information.

### --overlay OVERLAY

Path to an OpenAPI Overlay (1.0 to 1.2) applied to the input before generating, e.g. one setting the
extensions of the "Extensions" section. Can be repeated: the overlays apply in order, after those
listed under `commands.generate.client_go.overlay` in the configuration file (with paths relative to
it). Warnings and errors about a value an overlay set point to the overlay. The overlays apply to
the AsyncAPI document bundled with the files it references.

### --strict

Makes an overlay action whose target selects nothing an error (exit status 2, nothing is generated)
instead of a warning (`OVERLAY:LINE:COL: action #N (TARGET) selects no nodes.`).

### -o, --output

Directory where the module is generated.

### --package-name

Name of the Go package, as letters and digits in lower case (`carpet-express-events` gives
`carpetexpressevents`); defaults to the title of the API (`events` when it gives no valid
name). Its kebab case is the base of default group and client ids.

### --package-version

Ignored: Go modules have no version in `go.mod`.

### -p, --property

Applies to OpenAPI documents only; using it with an AsyncAPI document is an error.

## Configuration

The options are read from the `asyncapi` section of the configuration of `apigenie-generator client go`;
its other keys configure the generation from OpenAPI documents.

```yaml
version: '1.0'
commands:
  generate:
    client_go:
      asyncapi:
        module_path: github.com/acme/events
        package_name: events
        initialisms: [SKU]
        perspective: counterpart
```

| Option | Description |
| ------ | ----------- |
| `module_path` | Module path of `go.mod` (default: `example.com/<package>`) |
| `package_name` | Name of the package, a lower-case Go identifier (default: from `--package-name` or the title) |
| `initialisms` | Words written in capitals in identifiers, besides the common ones (`ID`, `URL`, ...) |
| `perspective` | `counterpart` (default): the counterpart of the described application; `application`: the described application, sending what it sends |
