---
# System prepended metadata

title: 'Design: Injecting Verb Resources in Go'
tags: [draft, accepted]

---

# Design: Injecting Verb Resources in Go
**Author**: @worstell 

## Description

"Injecting" resources for verbs in Go, a la @sdouglas's JVM approach for providing/tracking resources.

## Motivation

Currently, we identify verb resource usage by detecting known function invocations (e.g. instances of `ftl.Call(...)`) during static analysis. 

Detecting all function invocations is computationally impossible because of features like interfaces, reflection, and dynamic calls. With static analysis, we can make a best-effort approximation, but we cannot know with certainty which functions will be invoked at runtime.

In future, we may use the schema to determine versioning compatibility, e.g. when a module deploys to a new version, determining whether downstream consumers are compatible with the upgrade. Doing so will require a complete/reliable view of verb dependencies.

## Goals

- Deterministically identify resources used by a given verb in Go (databases, verb calls, topics, etc.)

## Design

We'll convert resources to _types_ rather than values. Currently an external verb stub is generated as:

```go
func Time(context.Context, TimeRequest) (TimeResponse, error) {
  ...
}
```

Instead, we will generate them as named type signatures:

```go
type TimeClient func(context.Context, TimeRequest) (TimeResponse, error)
```

Users can use the "client" as a parameter in their verb and invoke it as a typical function call:

```go
//ftl:verb
func Echo(ctx context.Context, req EchoRequest, tc time.TimeClient) (EchoResponse, error) {
    resp, err := tc(ctx, time.TimeRequest{})
  ...
}
```

We'll generate same-module verbs using the same pattern as external verbs, providing them adjacent to the user code in `types.ftl.go`. For consistency, all generated verb signatures (both same-module and external) will be suffixed with `Client` to avoid conflicts in the case of same-module verbs.

FTL sits as an intermediary for all inbound verb calls and thus can provide "actual" values for all injected resources when invoking the verb. If the resource is a verb client, the "actual" underlying operation should simply perform an outbound call.

That is, when invoking `Echo` from the above example, we want to effectively do this:

```go!
Echo(
    ctx, 
    req, 
    func(ctx Context, req TimeRequest) (TimeResponse, error) {
        return ftl.Call(ctx, req)
    }
)
```

To accomplish this, we'll first register any resources used by the verb in the generated `main.go` file: 

```go
func init() {
    reflection.Register(
       reflection.ProvideResourcesForVerb(
         echo.Echo,
         server.VerbClient[time.TimeClient, time.TimeRequest, time.TimeResponse](),
       ),
    )
}
```

Outbound calls will remain as they are now; only supplying a reference to the destination verb and a request. Generated verb stubs will not include any resources in their signatures. 

When processing inbound calls, we'll reference the registry. We'll use reflection to manipulate the verb invocation so that it includes all registered resources as well as the request we received from the caller.

A verb can inject any number/kind of resource in combination.

Extending this concept across other resources, we have—

**Databases**

Old:
```go
var db = ftl.PostgresDatabase("testdb")

//ftl:verb
func Echo(ctx context.Context, req EchoRequest) (EchoResponse, error) {
    db.Exec(...)
  ...
}
```
New:
```go
type TestDB = ftl.PostgresDatabaseHandle

//ftl:verb
func Echo(ctx context.Context, req EchoRequest, db TestDB) (EchoResponse, error) {
    db.Exec(...)
  ...
}
```

**Topics**

Old:
```go
var Invoices = ftl.Topic[Invoice]("invoices")

//ftl:verb
func Echo(ctx context.Context, req EchoRequest) (EchoResponse, error) {
    Invoices.Publish(ctx, Invoice{...})
  ...
}
```
New:
```go
type Invoices = ftl.TopicHandle[Invoice]

//ftl:verb
func Echo(ctx context.Context, req EchoRequest, invoices Invoices) (EchoResponse, error) {
    invoices.Publish(ctx, Invoice{...})
  ...
}
```

**Subscriptions**

Old:
```go
var _ = ftl.Subscription(Invoices, "emailInvoices")

//ftl:subscribe emailInvoices
func SendInvoiceEmail(ctx context.Context, in Invoice) error {
  // ...
}
```
New:
```go
type EmailInvoices = ftl.SubscriptionHandle[Invoices, SendInvoiceEmailClient]

//ftl:verb
func SendInvoiceEmail(ctx context.Context, in Invoice) error {
  // ...
}
```

One challenge of this approach is that it necessitates generating a `Client` stub prior to any usages of a verb as a resource. 

In the above `Subscription` example, the user would need to define their `SendInvoiceEmail` verb, build their module, and only then would they have access to `SendInvoiceEmailClient` to be able to use it in their subscription declaration.

### Required changes

1. Generate external verbs as type signatures rather than function values
2. Generate same-module verbs adjacent to the user code with the same pattern as external verbs
3. Extract resources from verb parameters during schema extraction
4. Update the Go type registry to track resources by verb; update generated `main.go` to perform registrations using the schema
6. Go runtime server code uses reflection to invoke verbs, passing in necessary resource handles rather than invoking directly 
7. Remove `ftl.Call` SDK; update SDKs for Databases, Topics, Subscriptions
8. Refactor internal and customer usages of current SDKs to follow new patterns
9. Surface a build error for any direct invocations of same-module verbs detected


### Open questions

Haven't put much thought into the testing experience; accompanying changes will be necessary here.
