---
title: Forge Integration
description: How Forge exposes Vessel for app services and extension wiring
---

[Forge](/docs/forge) uses Vessel as its internal dependency injection engine. Every Forge application creates a Vessel container at startup and exposes it via `app.Container()`. Forge also re-exports Vessel functions under the `forge` package for convenience, along with backward-compatible named-registration helpers.

## Accessing the Container

```go
import "github.com/xraph/forge"

app := forge.New(
    forge.WithAppName("my-app"),
    forge.WithAppVersion("1.0.0"),
)

// The container is a vessel.Vessel instance
c := app.Container()
```

`forge.Container` is a type alias for `vessel.Vessel`, so all Vessel functions work directly with the value returned by `app.Container()`.

## Core Services Registered by Forge

When a Forge app is created, these services are automatically registered in the container -- both by name (for backward compatibility) and by type (for constructor injection):

| Service | Type | Name Key |
|---|---|---|
| Logger | `forge.Logger` | `"logger"` |
| Config Manager | `forge.ConfigManager` | `"forge.config"` |
| Metrics | `forge.Metrics` | `"metrics"` |
| Health Manager | `forge.HealthManager` | `"forge.health"` |
| Router | `forge.Router` | `"forge.router"` |

### Resolving Core Services

By type (recommended):

```go
logger, err := forge.Inject[forge.Logger](app.Container())
metrics, err := forge.Inject[forge.Metrics](app.Container())
```

By name:

```go
logger, err := forge.InjectNamed[forge.Logger](app.Container(), "logger")
```

Forge also provides convenience helpers:

```go
logger, err := forge.GetLogger(app.Container())
metrics, err := forge.GetMetrics(app.Container())
healthMgr, err := forge.GetHealthManager(app.Container())
```

## Forge Re-exports

Forge re-exports all Vessel DI functions under the `forge` package. You can use either `vessel.*` or `forge.*` -- they call the same underlying functions.

### Constructor Registration and Resolution

| Forge function | Vessel equivalent |
|---|---|
| `forge.Provide(c, constructor, opts...)` | `vessel.Provide(c, constructor, opts...)` |
| `forge.ProvideConstructor(c, constructor, opts...)` | `vessel.Provide(c, constructor, opts...)` |
| `forge.ProvideValue[T](c, value, opts...)` | `vessel.ProvideValue[T](c, value, opts...)` |
| `forge.Inject[T](c)` | `vessel.Inject[T](c)` |
| `forge.MustInject[T](c)` | `vessel.MustInject[T](c)` |
| `forge.InjectNamed[T](c, name)` | `vessel.InjectNamed[T](c, name)` |
| `forge.MustInjectNamed[T](c, name)` | `vessel.MustInjectNamed[T](c, name)` |
| `forge.InjectGroup[T](c, group)` | `vessel.InjectGroup[T](c, group)` |
| `forge.MustInjectGroup[T](c, group)` | `vessel.MustInjectGroup[T](c, group)` |
| `forge.HasType[T](c)` | `vessel.HasType[T](c)` |
| `forge.HasTypeNamed[T](c, name)` | `vessel.HasTypeNamed[T](c, name)` |

### Named Registration Helpers (Forge-only)

These are backward-compatible helpers that Forge provides on top of Vessel. They accept a `func(Container) (T, error)` factory and translate it into the appropriate `vessel.ProvideNamed` call.

| Forge function | What it does |
|---|---|
| `forge.RegisterSingleton[T](c, name, factory)` | `vessel.ProvideNamed` + `AsSingleton()` |
| `forge.RegisterTransient[T](c, name, factory)` | `vessel.ProvideNamed` + `AsTransient()` |
| `forge.RegisterScoped[T](c, name, factory)` | `vessel.ProvideNamed` + `AsScoped()` |
| `forge.RegisterValue[T](c, name, value)` | `vessel.ProvideValue` + `WithName(name)` |
| `forge.Resolve[T](c, name)` | `vessel.InjectNamed[T](c, name)` |
| `forge.Must[T](c, name)` | `vessel.MustInjectNamed[T](c, name)` |

### Wrapper Type Aliases

| Forge alias | Vessel type |
|---|---|
| `forge.LazyRef[T]` | `vessel.Lazy[T]` |
| `forge.OptionalLazyRef[T]` | `vessel.OptionalLazy[T]` |
| `forge.ProviderRef[T]` | `vessel.Provider[T]` |

Factory helpers:

```go
forge.NewLazyRef[T](c, name)         // vessel.NewLazy[T](c, name)
forge.NewOptionalLazyRef[T](c, name) // vessel.NewOptionalLazy[T](c, name)
forge.NewProviderRef[T](c, name)     // vessel.NewProvider[T](c, name)
```

## Writing Extensions

Extensions register their services during the `Register` phase using the container from `app.Container()`.

```go
import (
    "context"
    "github.com/xraph/forge"
    "github.com/xraph/vessel"
)

type MyExtension struct {
    app forge.App
}

func (e *MyExtension) Register(app forge.App) error {
    e.app = app
    c := app.Container()

    // Register services using vessel (or forge -- equivalent)
    vessel.Provide(c, NewMyService)

    return nil
}

func (e *MyExtension) Start(ctx context.Context) error {
    svc, err := vessel.Inject[*MyService](e.app.Container())
    if err != nil {
        return err
    }
    return svc.Start(ctx)
}

func (e *MyExtension) Stop(ctx context.Context) error {
    return nil
}
```

### Depending on Other Extensions

Use constructor injection to depend on services from other extensions. Because constructors are resolved by type, there is no need to coordinate string keys across extensions.

```go
// This extension depends on the database extension's DatabaseManager
func NewMyService(manager *database.DatabaseManager, logger forge.Logger) (*MyService, error) {
    db, err := manager.SQL("primary")
    if err != nil {
        return nil, err
    }
    return &MyService{db: db, logger: logger}, nil
}

func (e *MyExtension) Register(app forge.App) error {
    return vessel.Provide(app.Container(), NewMyService)
}
```

### Resolving Services During Registration

If you need a service to be fully started before using it during the `Register` phase, use the container's `ResolveReady` method:

```go
func (e *MyExtension) Register(app forge.App) error {
    ctx := context.Background()

    svc, err := app.Container().ResolveReady(ctx, "database-manager")
    if err != nil {
        return fmt.Errorf("database required: %w", err)
    }

    dbManager := svc.(*database.DatabaseManager)
    e.redis, _ = dbManager.Redis("cache")
    return nil
}
```

## Interface Resolution Across Extensions

Use interface types for loose coupling between extensions:

```go
// Extension A registers a concrete implementation as an interface
vessel.Provide(app.Container(), NewEventService, vessel.As(new(core.EventBus)))

// Extension B resolves by interface type
eventBus, err := vessel.Inject[core.EventBus](app.Container())
```

## Which Package to Import

| Scenario | Recommendation |
|---|---|
| Standalone library (no Forge) | `import "github.com/xraph/vessel"` |
| Forge application code | Either works; `forge` is convenient for type aliases |
| Forge extension | `vessel` for registration, `forge` for types like `forge.Logger` |
| Shared package used in both | `import "github.com/xraph/vessel"` |
