> ## Documentation Index
> Fetch the complete documentation index at: https://forst-lang.org/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Providers (DI)

> Declare shared runtime services with use and wire them at entry points using with blocks.

Backend functions depend on runtime services (loggers, clocks, DB handles). **Providers** declare those dependencies with **`use`** and wire them at entry points with **`with`**. Request data stays separate from shared infrastructure.

## Two primitives

| Keyword                | Role                                                                    |
| ---------------------- | ----------------------------------------------------------------------- |
| **`use`**              | Bind a contract type inside a function: "this code depends on X"        |
| **`with { … } { … }`** | Wire concrete implementations at an entry point (handler, `main`, test) |

**Parameters carry data** (order IDs, request bodies). **Providers carry services** the host supplies (logger, clock, repo). Keeping them separate clarifies what flows through your API boundary and what is ambient context.

## Declare dependencies with `use`

Contracts are ordinary types: shapes with methods, or imported Go interfaces:

<Tabs>
  <Tab title="Forst">
    ```ft theme={"languages":{"custom":["/languages/forst.json"]}}
    type Logger = {
    	info(msg String)
    }

    type Clock = {
    	now(): Int
    }

    func logExpiry(id String) {
    	use logger: Logger
    	logger.info("expire " + id)
    }

    func expireToken(token Token) {
    	use clock: Clock
    	if token.expiresAt < clock.now() {
    		logExpiry(token.id)
    	}
    }
    ```
  </Tab>

  <Tab title="Generated Go">
    ```go theme={"languages":{"custom":["/languages/forst.json"]}}
    // forst build
    type Logger interface {
    	info(msg string)
    }

    type Clock interface {
    	now() int
    }

    type Token struct {
    	expiresAt int
    	id        string
    }

    func logExpiry(providers Providers_logExpiry, id string) {
    	providers.Logger.info("expire " + id)
    }

    func expireToken(providers Providers_expireToken, token Token) {
    	clock := providers.Clock
    	if token.expiresAt < clock.now() {
    		logExpiry(providers, token.id)
    	}
    }
    ```
  </Tab>
</Tabs>

The compiler aggregates `use` sites into **`Providers(f)`** for each function and propagates requirements transitively through calls. You can see the obligation chain in diagnostics and LSP hovers: which services a handler needs, and which entry point must supply them.

## Wire at the boundary

Entry points satisfy every Provider the call graph needs using a **`with { … } { … }`** block. Production wiring and test wiring use the same mechanism:

<Tabs>
  <Tab title="Forst">
    ```ft theme={"languages":{"custom":["/languages/forst.json"]}}
    func main() {
    	with {
    		Logger: &ProductionLogger{},
    		Clock:  &SystemClock {},
    	} {
    		expireToken(Token { id: "x", expiresAt: 1 })
    	}
    }
    ```
  </Tab>

  <Tab title="Generated Go">
    ```go theme={"languages":{"custom":["/languages/forst.json"]}}
    // forst build
    func main() {
    	providers := Providers_expireToken{
    		Logger: &ProductionLogger{},
    		Clock:  &SystemClock{},
    	}
    	expireToken(providers, Token{id: "x", expiresAt: 1})
    }
    ```
  </Tab>
</Tabs>

Wiring keys use the **root contract name** (`Logger`, `Clock`). Values must structurally satisfy the contract: plain structs with matching methods, or Go types that implement an imported interface.

### Void contract methods

Contract methods without a return type (such as **`info(msg String)`**) must stay void on implementations. A trailing **`fmt.Println(...)`** in the method body can infer a non-void **`Result`** return from Go's **`(int, error)`** signature; the compiler reports that mismatch. Use **`println`** for side-effect logging in void provider methods, or declare an explicit return type if you intend to return the call result. See [Return inference and implicit returns](/docs/language/errors-and-result#return-inference-and-implicit-returns).

### Shadowing

Nested **`with`** blocks merge with outer wiring. When the inner block supplies the **same contract key**, it **shadows** the outer value for that scope; other keys still forward from outside:

<Tabs>
  <Tab title="Forst">
    ```ft theme={"languages":{"custom":["/languages/forst.json"]}}
    with {
    	Logger: &ProductionLogger {},
    	Clock:  &SystemClock {},
    } {
    	with { Clock: &FakeClock { fixedMs: 2000 } } {
    		// Clock shadows outer; Logger still &ProductionLogger
    		expireToken(Token { id: "t1", expiresAt: 500 })
    	}
    }
    ```
  </Tab>

  <Tab title="Generated Go">
    ```go theme={"languages":{"custom":["/languages/forst.json"]}}
    // forst build
    outer := Providers_expireToken{
    	Logger: &ProductionLogger{},
    	Clock:  &SystemClock{},
    }
    inner := Providers_expireToken{
    	Logger: outer.Logger,
    	Clock:  &FakeClock{fixedMs: 2000},
    }
    expireToken(inner, Token{id: "t1", expiresAt: 500})
    ```
  </Tab>
</Tabs>

## Tests

Tests reuse the same `with` blocks to swap fakes without a separate DI framework:

<Tabs>
  <Tab title="Forst">
    ```ft theme={"languages":{"custom":["/languages/forst.json"]}}
    func TestExpireToken_expired(t *testing.T) {
    	token := Token { id: "t1", expiresAt: 500 }
    	with {
    		Logger: &NopLogger {},
    		Clock:  &FakeClock { fixedMs: 1000 },
    	} {
    		expireToken(token)
    	}
    }
    ```
  </Tab>

  <Tab title="Generated Go">
    ```go theme={"languages":{"custom":["/languages/forst.json"]}}
    // forst build
    func TestExpireToken_expired(t *testing.T) {
    	token := Token{id: "t1", expiresAt: 500}
    	providers := Providers_expireToken{
    		Logger: &NopLogger{},
    		Clock:  &FakeClock{fixedMs: 1000},
    	}
    	expireToken(providers, token)
    }
    ```
  </Tab>
</Tabs>

## Compared to other approaches

The Providers RFC surveyed these patterns before settling on **`use`** and **`with`**. See the [Providers RFC hub](https://github.com/forst-lang/forst/tree/main/examples/in/rfc/providers) for the full prior art writeup.

| Approach                            | Limitation                                                                                                                                                     |
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Per function parameters**         | Every helper gains extra arguments. Domain input and infrastructure blur together as the call graph grows.                                                     |
| **Go service struct**               | Wiring lives in `main` as interface fields on one struct. A new dependency deep in the tree does not propagate to the compiler as a wiring obligation.         |
| **`context.Context`**               | `WithValue` is untyped. Keys are easy to miss or collide. Nothing tracks who must supply what at compile time.                                                 |
| **DI frameworks** (Fx, Wire, dig)   | These tools rely on runtime containers or codegen indirection. Wiring and failures can be hard to trace in stack traces.                                       |
| **Effect / ZIO Layers**             | Requirements live in the environment type and runtime. Layer graphs add ceremony compared to one struct literal at the boundary.                               |
| **Kotlin context parameters**       | Implicit context in signatures works on the JVM. Go has no native equivalent, and Forst lowers to explicit struct parameters.                                  |
| **Test harness DSLs**               | Special test-only keywords split production wiring from test wiring. Readers and agents learn two different surfaces.                                          |
| **Mock frameworks** (Jest, testify) | Expectations couple tests to framework APIs. Structural fakes wired with the same boundary pattern as production are harder to reuse across integration tests. |
| **Global singletons**               | Dependencies hide in package state. Tests need globals, init hooks, or monkey patching.                                                                        |
| **Plain Go test helpers**           | A hand written `newTestApp(t)` helper drifts from what handlers actually need. Nothing checks completeness when a leaf adds a new service.                     |
| **Forst `use` / `with`**            | Obligations are inferred at compile time. Wiring stays explicit at entry points. The compiler emits plain Go struct literals with no runtime container.        |

Forst treats `context.Context` separately from Providers. Declare services with **`use`** so requirements stay typed and visible. Use **`context`** for cancellation and request scoped values. Use **`with`** blocks for service wiring. Sidecar and TypeScript exports stay data only. The host wires Providers in Go before exposing a handler.

## What gets emitted

Providers lower to a **deduped struct parameter** at the Go boundary. Production and test wiring compile to ordinary struct literals: readable Go, no runtime container.

## Caveats

**Providers** are **experimental**. The RFC surface is still evolving. Pin your compiler version and run `task example:providers` when upgrading.

### Host wiring only

Providers lower in Go. They are **not** exported to TypeScript or sidecar invoke payloads. Functions with unsatisfied **`Providers(f)`** are omitted from generated clients. Wire **`with`** blocks in Go **`main`** before exposing handlers.

See [Call Forst from Node § Caveats](/docs/interop/node/call-forst#caveats) and [Generate client types § Caveats](/docs/interop/node/generate-types#caveats).

### Void contract methods

A trailing **`fmt.Println(...)`** in a void provider method can infer a non-void **`Result`** return from Go's **`(int, error)`** signature. Use **`println`** for side effect logging, or declare an explicit return type. See [Return inference and implicit returns](/docs/language/errors-and-result#return-inference-and-implicit-returns).

### `context.Context` is separate

Providers are typed services declared with **`use`**. Cancellation and request scoped values stay in **`context`**, not in Provider wiring.

## Try it

From the Forst repository:

```bash theme={"languages":{"custom":["/languages/forst.json"]}}
task example:providers
task example:providers-cross_pkg
forst test examples/in/rfc/providers/
```

Deep dive: [Providers RFC hub](https://github.com/forst-lang/forst/tree/main/examples/in/rfc/providers)

## Related

<CardGroup cols={2}>
  <Card title="Go interop" icon="https://mintcdn.com/forst/r5GJChnfkgCSJa-b/icons/golang.svg?fit=max&auto=format&n=r5GJChnfkgCSJa-b&q=85&s=e676ee132c42dfabaf125e76fe733c20" href="/docs/interop/go" width="205" height="77" data-path="icons/golang.svg">
    Import Go interfaces as Provider contracts.
  </Card>

  <Card title="Editor workflow" icon="code" href="/docs/workflow/editor">
    LSP hovers show Provider obligations and related diagnostics.
  </Card>
</CardGroup>
