nexus

package module
v0.7.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 24, 2026 License: MIT Imports: 29 Imported by: 0

README

nexus

A Go framework over Gin that lets you write plain handler functions, wires them into REST + GraphQL + WebSocket transports, and ships a live Vue dashboard that renders your service topology, per-endpoint traffic, rate limits, errors, and cron jobs as they happen.

Architecture dashboard

func main() {
    nexus.Run(
        nexus.Config{Addr: ":8080", EnableDashboard: true},
        nexus.ProvideResources(NewMainDB, NewCacheManager),
        auth.Module(auth.Config{Resolve: resolveBearer}),
        advertsModule,
        nexus.AsWorker("cache-invalidation", NewCacheInvalidationWorker),
    )
}

var advertsModule = nexus.Module("adverts",
    nexus.ProvideService(NewAdvertsService),
    nexus.AsQuery(NewGetAllAdverts),
    nexus.AsMutation(NewCreateAdvert,
        auth.Required(),
        auth.Requires("ROLE_CREATE_ADVERT"),
        nexus.Use(ratelimit.NewMiddleware(store, "adverts.createAdvert",
            ratelimit.Limit{RPM: 30, Burst: 5})),
    ),
)

No fx import. No schema assembly. No middleware plumbing. The handler is plain Go, the dashboard is at /__nexus/.

Highlights

  • Reflective controllers — write func(svc, deps..., args) (*T, error); nexus's AsRest / AsQuery / AsMutation introspect the signature and wire the transport. No graph.NewResolver[T](...).With... boilerplate.
  • Module-first architecture viewnexus.Module("name", opts...) groups endpoints as one card on the dashboard; services appear as typed dependency nodes that both handlers and service constructors can point at. nexus.ProvideService inspects the constructor's params and draws service → resource / service → service edges automatically.
  • Built-in authauth.Module ships a pluggable authentication surface (bearer / cookie / api-key extraction, cached identity resolution, per-op auth.Required / auth.Requires(perms) bundles, admin invalidation + live auth.reject trace events on the dashboard's Auth tab).
  • Background workersnexus.AsWorker wraps long-lived listeners (DB LISTEN/NOTIFY, queue consumers, sweepers) with framework-owned lifecycle, ctx cancellation, panic recovery, and a card on the architecture view that shows their dep graph.
  • One middleware API for three transportsmiddleware.Middleware bundles a gin.HandlerFunc + graph.FieldMiddleware from a single definition; nexus.Use(mw) attaches it to any kind of endpoint.
  • Live dashboard — Architecture, Endpoints, Crons, Rate limits, Auth, Traces tabs. Traffic animations pulse on real requests — inbound lanes, per-op resource edges, and service-level edges all light up in sync. Error dialog shows recent errors with IP + timestamp (scales to 1000 events via virtualized scrolling).
  • Per-op observability, free — every handler gets a request + error counter and streams a request.op event, all without any user code.
  • Layered rate limiting — global (engine-root gin middleware) + per-op (graph field middleware), hot-swappable from the dashboard at runtime.
  • Cross-transport cron jobs — schedule bare handlers; control (pause/resume/trigger-now) lives on the dashboard.
  • fx under the hood, not in your importsnexus.Run/Module/Provide/Invoke wrap fx so you get DI + lifecycle without importing go.uber.org/fx.

Install

go get github.com/paulmanoni/nexus

Requires Go 1.25+.

Quick start

package main

import (
    "context"

    "github.com/paulmanoni/nexus"
)

// Service wrapper — distinct Go type per logical service so fx can
// route by type (no named tags).
type AdvertsService struct{ *nexus.Service }

func NewAdvertsService(app *nexus.App) *AdvertsService {
    return &AdvertsService{app.Service("adverts").Describe("Job adverts catalog")}
}

// Typed DB handle — same pattern. Fx resolves by type, compile-time
// routing, no string lookups.
type MainDB struct{ *DB }

func NewMainDB() *MainDB { /* Open, migrate, return wrapper */ }

// Every dep the handler declares shows up on the dashboard:
//   - *AdvertsService  →  grounds the op under the "adverts" service
//   - *MainDB          →  draws an edge from adverts → main resource
//   - nexus.Params[T]  →  resolve context + typed args bundle
func NewListAdverts(svc *AdvertsService, db *MainDB, p nexus.Params[struct{}]) (*Response, error) {
    return fetch(p.Context, db)
}

func main() {
    nexus.Run(
        nexus.Config{
            Addr:            ":8080",
            DashboardName:   "Adverts",
            TraceCapacity:   1000,
            EnableDashboard: true,
        },
        nexus.ProvideResources(NewMainDB),
        nexus.Module("adverts",
            nexus.Provide(NewAdvertsService),
            nexus.AsQuery(NewListAdverts),
        ),
    )
}

Open http://localhost:8080/__nexus/. Fire a request → packet animation on the Architecture tab.

Core concepts

App and Config

nexus.Run(cfg, opts...) builds and runs the app. Block until SIGINT/SIGTERM, then gracefully shuts down. Config covers environment-level knobs; options are the building blocks of your graph.

nexus.Run(nexus.Config{
    Addr:            ":8080",
    DashboardName:   "Adverts",
    TraceCapacity:   1000,
    EnableDashboard: true,

    // GraphQL toggles — one switch, all services
    DisablePlayground: false,
    GraphQLDebug:      false,

    // App-wide rate limit (applies to every HTTP path)
    GlobalRateLimit: ratelimit.Limit{RPM: 600, Burst: 50},
})

Option builders:

Option Produces
nexus.Module(name, opts...) Named group of options. Stamps module name onto every endpoint for the architecture view.
nexus.Provide(fns...) Constructor(s) into the dep graph.
nexus.ProvideService(fn) Provide + introspect: detects resource / service deps from the constructor's params and records them for the Architecture view.
nexus.ProvideResources(fns...) Like Provide, but auto-registers resources via NexusResourceProvider.
nexus.Supply(vals...) Ready-made values into the dep graph.
nexus.Invoke(fn) Side-effect at start-up; receives deps via function params.
nexus.AsRest(method, path, fn, opts...) REST endpoint from a reflective handler.
nexus.AsQuery(fn, opts...) / AsMutation(fn, opts...) GraphQL op, auto-mounted by the framework.
nexus.AsWorker(name, fn) Long-lived background task; framework manages lifecycle + records status.
nexus.Use(middleware.Middleware) Cross-transport middleware — works on REST + GraphQL.
auth.Module(auth.Config{Resolve: ...}) Built-in auth surface: extraction + cached resolution + per-op enforcement bundles.
Reflective handlers

Write handlers as plain Go functions. Signature convention:

func NewOp(svc *XService, deps..., p nexus.Params[ArgsStruct]) (*Response, error)
  • First *Service-wrapper dep grounds the op under that service. Auto-routing picks the single-service default when omitted.
  • context.Context anywhere in the deps list is special-cased (filled from p.Context).
  • Last param (if it's a struct, or nexus.Params[T]) carries user-supplied args.
  • Return must be (T, error) — T becomes the GraphQL return type, flow-through for REST.
type CreateArgs struct {
    Title        string `graphql:"title,required" validate:"required,len=3|120"`
    EmployerName string `graphql:"employerName,required" validate:"required,len=2|200"`
}

func NewCreateAdvert(svc *AdvertsService, db *MainDB,
                     p nexus.Params[CreateArgs]) (*AdvertResponse, error) {
    return create(p.Context, db, p.Args.Title, p.Args.EmployerName)
}

Tags drive the schema + validators:

  • graphql:"name,required" — field name + NonNull marker
  • validate:"required,len=3|120"graph.Required() + graph.StringLength(3, 120), introspected by the dashboard as chips
Service + typed resource wrappers

Resources (DBs, caches, queues) are typed wrappers that own their dashboard metadata. No resourcesModule, no string matching.

type MainDB struct{ *DB }

func (m *MainDB) NexusResources() []resource.Resource {
    return []resource.Resource{
        resource.NewDatabase("main", "GORM — sqlite",
            map[string]any{"engine": "sqlite", "schema": "main"},
            m.IsConnected, resource.AsDefault()),
    }
}

Any handler that takes *MainDB as a dep auto-draws the service → main edge on the Architecture graph — no Using(...) call required.

Cross-transport middleware
// Build once — bundle carries Gin + Graph realizations.
authMw := middleware.Middleware{
    Name:        "auth",
    Description: "Bearer token validation",
    Kind:        middleware.KindBuiltin,
    Gin:         authGinHandler,
    Graph:       authResolverMiddleware,
}

// Apply anywhere.
nexus.AsRest("POST", "/secure", NewSecureHandler, nexus.Use(authMw))
nexus.AsMutation(NewMutate,                    nexus.Use(authMw))

// Global — every HTTP path (REST + GraphQL + WS upgrade + dashboard)
nexus.Config{
    GlobalMiddleware: []middleware.Middleware{requestID, logger, cors},
}

Built-ins that ship:

  • ratelimit.NewMiddleware(store, key, limit) — token-bucket with per-IP option
  • metrics — auto-attached to every op, no user code
Rate limits

Layered by default:

Layer How Where
Global Config.GlobalRateLimit gin middleware on engine root
Per-op nexus.Use(ratelimit.NewMiddleware(...)) per-handler
Runtime override Rate limits tab in dashboard hot-swappable without redeploy

Store swap for multi-replica:

nexus.Config{
    RateLimitStore: ratelimit.NewRedisStore(redisClient), // not yet shipped
    // or just
    Cache: cache.NewManager(cfg, logger), // app auto-uses this for the store
}
Metrics + error dialog

Every op automatically gets:

  • Request counter (atomic, ~70 ns)
  • Error counter
  • Ring of recent error events (IP, timestamp, message) capped at 1000
  • request.op trace event emitted on every handler exit

The Architecture tab shows ⚡N (request count) and ⚠N (errors) chips per op. Click the error chip → paginated dialog with filter over IP/message + virtualized scrolling that stays snappy at thousands of events.

Auth (built-in)

auth.Module owns the plumbing — token extraction, identity caching, per-op enforcement, context propagation — and leaves resolution (token → Identity) as the single plug you wire:

import "github.com/paulmanoni/nexus/auth"

nexus.Run(nexus.Config{...},
    auth.Module(auth.Config{
        // Required: turn a raw token into an Identity.
        Resolve: func(ctx context.Context, tok string) (*auth.Identity, error) {
            u, err := myAPI.ValidateToken(ctx, tok)
            if err != nil { return nil, err }
            return &auth.Identity{
                ID:    u.ID,
                Roles: u.Roles,
                Extra: u,   // user-defined payload, typed-accessible later
            }, nil
        },
        Cache: auth.CacheFor(15 * time.Minute),

        // Optional: match your existing error envelope
        OnUnauthenticated: func(c *gin.Context, err error) {
            c.AbortWithStatusJSON(401, pkg.Response[any]{Success: false, Message: "UnAuthorized"})
        },
    }),
    advertsModule,
)

Per-op gating (cross-transport):

nexus.AsMutation(NewCreateAdvert,
    auth.Required(),                         // 401 if no identity
    auth.Requires("ROLE_CREATE_ADVERT"),     // 403 if missing permission
)

Resolver access:

func NewListAdverts(db *MainDB, p nexus.Params[struct{}]) (*Response, error) {
    user, ok := auth.User[MyUser](p.Context)
    if !ok {
        // Required() would have caught this earlier, but a direct
        // check is idiomatic for handlers using auth.Optional().
    }
    return fetch(p.Context, db, user.ID)
}

Token extraction strategies ship ready-made — auth.Bearer(), auth.Cookie(name), auth.APIKey(header), auth.Chain(...) — plus the typed auth.User[T] generic accessor, auth.AnyOf/auth.AllOf permission helpers, and a *auth.Manager handle (fx-injected) for logout flows:

func NewLogoutHandler(am *auth.Manager) func(context.Context, nexus.Params[TokenArgs]) (OK, error) {
    return func(ctx context.Context, p nexus.Params[TokenArgs]) (OK, error) {
        am.Invalidate(p.Args.Token)         // single-session logout
        // or am.InvalidateByIdentity(userID) → sweeps every cached session for that user
        return OK{}, nil
    }
}

Dashboard's Auth tab renders the cached identity table, recent 401/403 rejections (live via auth.reject trace events), and per-row "invalidate" buttons — all driven off GET /__nexus/auth + POST /__nexus/auth/invalidate.

Workers

nexus.AsWorker wraps long-lived background tasks (DB LISTEN/NOTIFY loops, queue consumers, sweepers) with framework-owned lifecycle:

nexus.AsWorker("cache-invalidation",
    func(ctx context.Context, db *OatsDB, cache *CacheManager, logger *zap.Logger) error {
        // Wait for dependencies to come up
        for !db.IsConnected() {
            select {
            case <-ctx.Done(): return ctx.Err()
            case <-time.After(time.Second):
            }
        }

        listener := pq.NewListener(db.ConnectionString(), 10*time.Second, time.Minute, nil)
        defer listener.Close()
        if err := listener.Listen("cache_invalidation"); err != nil { return err }

        for {
            select {
            case <-ctx.Done():
                return nil                  // clean stop on fx.Stop
            case n := <-listener.Notify:
                handleInvalidation(ctx, cache, n)
            }
        }
    })

The framework starts the function on its own goroutine at fx.Start, cancels ctx at fx.Stop, recovers panics, and records Status / LastError on the registry. The worker appears as a dedicated card on the Architecture view with its dep graph (resources + services it took as params) drawn as outgoing edges — same visual language as services.

Signature requirements:

  • First param MUST be context.Context.
  • Remaining params are fx-injected deps (resources, services, loggers, whatever's in the graph).
  • Optional error return sets LastError on the registry; context.Canceled / nil is a clean stop.
Cron jobs
app.Cron("refresh-cache", "*/5 * * * *").
    Describe("Refresh advert cache").
    Handler(func(ctx context.Context) error {
        return refreshCache(ctx)
    })

Dashboard Crons tab: schedule, last run, last result, pause/resume, trigger-now.

Cache

cache.Manager is go-cache in-memory by default; switches to Redis when env is configured. Always present on App — nexus uses it for metrics persistence automatically.

mgr := app.Cache()
_ = mgr.Set(ctx, "k", value, 5*time.Minute)

Dashboard

Mounted at /__nexus/ when EnableDashboard: true. Six tabs:

Tab What it shows
Architecture Module containers + endpoints, service-dep nodes, worker cards, resource nodes, external "Clients" node, dashed system boundary. Per-op and service-level edges both pulse on live traffic (green for success, red ✕ for rejections).
Endpoints REST path and GraphQL op-name list; per-endpoint tester (curl + Playground for GraphQL), arg validators rendered as chips.
Crons Schedule table, pause/resume, trigger-now.
Rate limits Declared vs effective limit per endpoint, inline edit (RPM/burst/perIP) with save/reset.
Auth Cached identities (redacted tokens), live 401/403 stream, per-identity invalidation. Renders a "not configured" prompt when auth.Module isn't wired.
Traces WebSocket stream of request events, filterable.

Tab selection persists via ?tab= in the URL — shareable, bookmarkable, survives refresh.

Traces tab

Gating the dashboard

Opt the whole /__nexus/* surface (JSON APIs, WebSocket events, embedded UI) into your own auth / permission chain by passing middleware bundles:

nexus.Run(
    nexus.Config{
        EnableDashboard: true,
        DashboardMiddleware: []middleware.Middleware{
            {Name: "auth",  Kind: middleware.KindBuiltin, Gin: bearerAuthGin},
            {Name: "admin", Kind: middleware.KindCustom,  Gin: requireAdminGin},
        },
    },
    // ...
)

Middleware runs on the /__nexus route group before any dashboard handler, so one chain covers every dashboard request. Bundles with a nil Gin realization are ignored (the dashboard is HTTP-only). nexus.WithDashboardMiddleware(...) is the equivalent AppOption for callers using nexus.New.

HTTP surface:

Route Returns
GET /__nexus/ Embedded Vue UI
GET /__nexus/endpoints Services + endpoints (services carry ResourceDeps / ServiceDeps from ProvideService)
GET /__nexus/resources Resource snapshots (health probed live)
GET /__nexus/workers AsWorker registrations + live Status / LastError / deps
GET /__nexus/middlewares { middlewares: [...], global: [ordered names] }
GET /__nexus/stats Per-endpoint counters (RecentErrors stripped)
GET /__nexus/stats/:service/:op/errors Full error ring for one endpoint
GET /__nexus/ratelimits Store snapshot
POST /__nexus/ratelimits/:service/:op Override a limit
DELETE /__nexus/ratelimits/:service/:op Reset to declared
GET /__nexus/crons, POST /.../:name/{trigger,pause,resume} Cron control
GET /__nexus/auth { identities, cachingEnabled } — cached auth state
POST /__nexus/auth/invalidate Body `{id?
GET /__nexus/events WebSocket: trace + request.op + auth.reject events
Developing the UI
cd dashboard/ui
npm install
npm run dev       # Vite dev server
npm run build     # dist/ gets embedded into Go binaries via //go:embed

Benchmarks

Microbenchmarks of the per-request hot paths on an Apple M1 Pro:

Path ns/op allocs
metrics.Record (success, single key) 73 0
metrics.Record (parallel, 10 cores) 238 0
ratelimit.Allow (single key) 134 1
callHandler (reflective invoke) 477 5
bindGqlArgs (map → struct) 250 4
direct function call (baseline) 0.3 0

A request going through AsQuery with args, metrics, and one rate limit therefore pays on the order of 73 + 134 + 477 + 250 ≈ 1 μs of nexus-side work. The surrounding cost (Gin routing, graphql-go query parsing, JSON encoding, your handler, any DB/cache roundtrip) is measured by your own load test, not by this README.

Run the microbenchmarks:

go test ./metrics ./ratelimit ./ -bench=. -benchmem -run 'x^'

For end-to-end numbers on your own workload, load-test a real endpoint:

vegeta attack -rate=10000 -duration=30s -targets=targets.txt | vegeta report

Examples

Path Shows
examples/petstore Minimal REST + WebSocket + tracing.
examples/fxapp Multi-domain app wired via nexus.Module (fx hidden).
examples/graphapp GraphQL via reflective AsQuery/AsMutation, typed DB wrappers, rate limits, validators, metrics.
examples/wstest WebSocket echo playground.

Run any example:

go run ./examples/graphapp

Package layout

nexus/                top-level App, Run, Module, Provide, ProvideService, AsWorker, Use, Cron, options
├── auth/             built-in authentication surface (extractors, identity cache, per-op bundles, dashboard routes)
├── graph/            absorbed go-graph resolver builder + validators
├── registry/         services, endpoints, resources, workers, middleware metadata
├── resource/         Database/Cache/Queue abstractions + health probing
├── trace/            ring-buffer bus + per-request middleware + op events
├── transport/
│   ├── rest/         REST builder
│   ├── gql/          GraphQL HTTP adapter (Playground, auth hook)
│   └── ws/           WebSocket builder + Hub
├── middleware/       Info + cross-transport Middleware bundle
├── metrics/          per-endpoint counters, error ring, cache-backed store
├── ratelimit/        token-bucket store, Gin + Graph middleware factories
├── cron/             scheduler, dashboard HTTP, event emission
├── cache/            Redis + in-memory hybrid (nexus uses it as a default)
├── multi/            N named instances behind .Using(name) (legacy pattern)
├── db/               opinionated GORM helpers (Manager.DB(ctx), ConnectionString, GetCtx)
├── dashboard/        /__nexus HTTP surface + embedded Vue UI
└── examples/         runnable demos

Testing

go build ./...
go vet ./...
go test ./...
go test ./... -bench=. -benchmem -run 'x^'

License

MIT

Documentation

Overview

Package nexus is a thin framework over Gin that registers every endpoint (REST, GraphQL, WebSocket) into a central registry, traces every request into an in-memory event bus, and exposes both under /__nexus for tooling — notably the Vue dashboard.

nexus does NOT replace the caller's GraphQL layer: hand it a *graphql.Schema (typically built with github.com/paulmanoni/nexus/graph) and it mounts + introspects.

Index

Constants

View Source
const DefaultGraphQLPath = "/graphql"

DefaultGraphQLPath is the mount path nexus.AsQuery / AsMutation use when a service hasn't called AtGraphQL.

View Source
const GqlFieldGroup = "nexus.graph.fields"

GqlFieldGroup is the single fx value-group name every reflective GraphQL registration feeds. fxmod's auto-mount Invoke reads this group, partitions entries by ServiceType, and mounts one schema per service.

Variables

This section is empty.

Functions

func ClientIPFromCtx added in v0.3.0

func ClientIPFromCtx(ctx context.Context) string

ClientIPFromCtx pulls the IP stashed via WithClientIP (or ratelimit.WithClientIP). Empty when absent.

func Run added in v0.3.0

func Run(cfg Config, opts ...Option)

Run builds and runs the app. Blocks until SIGINT/SIGTERM, then gracefully shuts the HTTP server + cron scheduler. Returns nothing — identical to fx.App.Run(). For tests where you need explicit Start/Stop control, build the app via a test helper that calls fxBootOptions.

func main() {
    nexus.Run(
        nexus.Config{Addr: ":8080", EnableDashboard: true},
        nexus.Provide(NewDBManager),
        advertsModule,
    )
}

func WithClientIP added in v0.3.0

func WithClientIP(ctx context.Context, ip string) context.Context

WithClientIP is a thin pass-through to ratelimit.WithClientIP so nexus callers can thread IP into context without importing the lower-level ratelimit package. Kept here for API consistency with other nexus helpers.

Types

type App

type App struct {
	// contains filtered or unexported fields
}

func New

func New(opts ...AppOption) *App

func (*App) Bus

func (a *App) Bus() *trace.Bus

func (*App) Cache added in v0.3.0

func (a *App) Cache() *cache.Manager

func (*App) Cron added in v0.3.0

func (a *App) Cron(name, schedule string) *CronBuilder

Cron starts building a scheduled job. Finalize with .Handler(fn). Jobs run bare — middleware chains do not apply — but the scheduler still emits request.start / request.end trace events so they appear on the dashboard Traces tab.

app.Cron("refresh-pets", "*/5 * * * *").
    Describe("Warm the pet cache").
    Handler(func(ctx context.Context) error { return nil })

func (*App) Engine

func (a *App) Engine() *gin.Engine

func (*App) Metrics added in v0.3.0

func (a *App) Metrics() metrics.Store

func (*App) OnResourceUse

func (a *App) OnResourceUse(target UseReporter)

OnResourceUse installs an auto-attach hook onto any UseReporter (typically a *multi.Registry or a user wrapper around one). Whenever code calls target.UsingCtx(ctx, "resource-name") during a request, the hook:

  1. reads the current trace.Span from ctx so we know which service made the call
  2. AttachResource(service, resource) on the registry — edge appears live
  3. emits a "downstream" trace event so the Traces tab shows the lookup

Calls with no span in context (e.g. UsingCtx fired from main or a cron job outside the trace middleware) are silently ignored — there's no service to attribute the usage to.

func (*App) RateLimiter added in v0.3.0

func (a *App) RateLimiter() ratelimit.Store

func (*App) Register

func (a *App) Register(r resource.Resource)

Register adds a resource (database, cache, queue) to the app so its health shows up on the dashboard. Use Service.Attach(r) to also draw an edge between the owning service(s) and the resource.

func (*App) Registry

func (a *App) Registry() *registry.Registry

func (*App) Run

func (a *App) Run(addr string) error

func (*App) Scheduler added in v0.3.0

func (a *App) Scheduler() *cron.Scheduler

func (*App) ServeHTTP

func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request)

func (*App) Service

func (a *App) Service(name string) *Service

type AppOption added in v0.3.0

type AppOption func(*App)

AppOption is the functional-option type for nexus.New. Named AppOption (not Option) so nexus.Option can be reserved for the top-level fx-wrapping builder type used by nexus.Provide / Module / Invoke / Run.

func WithCache added in v0.3.0

func WithCache(m *cache.Manager) AppOption

WithCache installs a user-provided cache.Manager instead of the default one nexus creates. Pass the same Manager users' app code receives from fx (e.g. via cache.Module) so every cache consumer — user code, metrics, rate-limit overrides — hits one store.

func WithDashboard

func WithDashboard() AppOption

WithDashboard mounts /__nexus/endpoints (always) and /__nexus/events (if tracing is on).

func WithDashboardMiddleware added in v0.3.1

func WithDashboardMiddleware(bundles ...middleware.Middleware) AppOption

WithDashboardMiddleware gates the /__nexus surface behind one or more middleware bundles. Each bundle's Gin realization runs in registration order before any dashboard handler. Typical use:

nexus.WithDashboardMiddleware(
    middleware.Middleware{Name: "auth",  Gin: bearerAuth},
    middleware.Middleware{Name: "admin", Gin: requireAdminRole},
)

Bundles without a Gin realization are skipped silently — the dashboard is an HTTP surface, so graph-only bundles don't apply.

func WithDashboardName

func WithDashboardName(name string) AppOption

WithDashboardName sets the brand shown in the dashboard header and the browser tab title. Defaults to "Nexus". The name is served over /__nexus/config so the client picks it up without a rebuild.

func WithEngine

func WithEngine(e *gin.Engine) AppOption

WithEngine supplies a pre-configured Gin engine. Without it, nexus builds a bare engine with just Recovery so the caller can bring their own logger.

func WithMetricsStore added in v0.3.0

func WithMetricsStore(s metrics.Store) AppOption

WithMetricsStore swaps the default cache-backed metrics store. Useful when you want a Prometheus- or StatsD-backed implementation; the built-in dashboard /__nexus/stats endpoint reads from whichever Store is installed.

func WithRateLimitStore added in v0.3.0

func WithRateLimitStore(s ratelimit.Store) AppOption

WithRateLimitStore swaps the default in-memory rate-limit store for a custom implementation — typically ratelimit.NewRedisStore(...) in a multi-replica deploy. Pass this to nexus.New / via nexus.Config when you want limit state (counters, overrides) to survive restarts or be shared across processes.

func WithTracing

func WithTracing(capacity int) AppOption

WithTracing enables per-request trace events, buffered in a ring of the given capacity. Required for the dashboard's event stream to show anything.

type Config added in v0.3.0

type Config struct {
	// Addr is the HTTP listen address (default ":8080").
	Addr string

	// DashboardName is the brand shown in the dashboard header and tab title
	// (default "Nexus"). Served over /__nexus/config so you can change it
	// per-environment without rebuilding the UI.
	DashboardName string

	// TraceCapacity is the ring-buffer size for request traces. 0 disables
	// tracing — the Traces tab will stay empty.
	TraceCapacity int

	// EnableDashboard mounts /__nexus/* if true.
	EnableDashboard bool

	// DisablePlayground turns OFF the GraphQL Playground served on GET
	// <service>/<path>. Default is enabled. Flip in prod wiring to hide
	// the interactive console.
	DisablePlayground bool

	// GraphQLDebug skips query validation + response sanitization in
	// go-graph. Dev-only. Default false.
	GraphQLDebug bool

	// GraphQLPretty pretty-prints JSON responses. Convenient while
	// exploring; ship off in prod.
	GraphQLPretty bool

	// GlobalRateLimit applies across every endpoint — the whole app
	// consumes from one bucket. Combine with per-op nexus.RateLimit()
	// declarations for layered protection: the request must pass both
	// the global bucket and the op's bucket. Zero disables.
	//
	// Set PerIP to scope the global bucket per caller IP.
	GlobalRateLimit ratelimit.Limit

	// GlobalMiddleware stacks on the Gin engine root, so every REST
	// endpoint, GraphQL POST, WebSocket upgrade, and dashboard request
	// flows through it in registration order. Use for cross-cutting
	// concerns (request-id, logger, CORS, global rate limit, auth pre-
	// gate, etc.). Each bundle's Gin field runs; nil Gin realizations
	// are skipped silently. Per-op middleware (via nexus.Use on a
	// registration) layers on top.
	GlobalMiddleware []middleware.Middleware

	// RateLimitStore replaces the default in-memory rate-limit store.
	// Set when you want to share the store between the app and externally-
	// built middleware bundles (ratelimit.NewMiddleware consumes a Store),
	// or for persistence / multi-replica via a Redis-backed implementation.
	// Nil → app builds its own MemoryStore at boot (or cache-backed when
	// Cache is set — see below).
	RateLimitStore ratelimit.Store

	// MetricsStore replaces the default metrics store. Parallel to
	// RateLimitStore — explicit wins over Cache-driven defaults.
	MetricsStore metrics.Store

	// DashboardMiddleware gates the /__nexus surface behind user-supplied
	// middleware — typically auth + permission checks. Each bundle's
	// Gin realization runs in registration order on the /__nexus route
	// group BEFORE any dashboard handler, covering the JSON API,
	// WebSocket events, and the embedded Vue UI in one pass.
	//
	//	nexus.Config{
	//	    EnableDashboard: true,
	//	    DashboardMiddleware: []middleware.Middleware{
	//	        {Name: "auth",  Gin: bearerAuth},
	//	        {Name: "admin", Gin: requireAdminRole},
	//	    },
	//	}
	//
	// Bundles whose Gin field is nil are ignored for the dashboard (no
	// graph-only protection makes sense here — the dashboard itself
	// isn't GraphQL).
	DashboardMiddleware []middleware.Middleware

	// Cache is an optional nexus cache.Manager. When set, nexus uses it
	// as the default backing for metrics + rate-limit stores so counters
	// and overrides benefit from the app's cache tier (Redis when
	// configured via env, go-cache otherwise) without extra wiring.
	//
	// Explicit RateLimitStore / MetricsStore settings still win — this
	// is just the default when those are nil.
	Cache *cache.Manager
}

Config drives how nexus.Run builds the app. Supply it as the first argument to nexus.Run; users never construct a *App directly when using the top-level builder.

type CronBuilder added in v0.3.0

type CronBuilder struct {
	// contains filtered or unexported fields
}

CronBuilder accumulates optional metadata before Handler registers the job with the scheduler.

func (*CronBuilder) Describe added in v0.3.0

func (c *CronBuilder) Describe(desc string) *CronBuilder

Describe sets a human-readable description shown on the dashboard.

func (*CronBuilder) Handler added in v0.3.0

func (c *CronBuilder) Handler(fn func(ctx context.Context) error)

Handler finalizes the registration. A bad schedule is logged and the job is dropped; the app keeps running.

func (*CronBuilder) Service added in v0.3.0

func (c *CronBuilder) Service(name string) *CronBuilder

Service groups the cron under a service on the dashboard. Optional.

type GqlField added in v0.3.0

type GqlField struct {
	Kind        graph.FieldKind
	ServiceType reflect.Type
	Service     *Service        // nil if dep[0] didn't unwrap (misuse)
	Module      string          // nexus.Module name this field was declared under; "" if unscoped
	Field       any             // graph.QueryField or graph.MutationField
	DepTypes    []reflect.Type  // for resource auto-attach
	Deps        []reflect.Value // for resource auto-attach (NexusResourceProvider)
	// RateLimit is the baseline rate limit this op declared. Auto-mount
	// publishes it to the registry so the dashboard can render it and
	// — once operator overrides land — show the effective limit beside
	// the declared one.
	RateLimit *ratelimit.Limit
}

GqlField is the shared-group payload that AsQuery / AsMutation produce and fxmod's auto-mount Invoke consumes. Exported so consumers building their own mount logic can see what's in the graph, but most users never touch it.

type GqlOption added in v0.3.0

type GqlOption interface {
	// contains filtered or unexported methods
}

GqlOption tunes a GraphQL registration. An interface (not a func type) so a single value — notably the nexus.Use cross-transport bundle — can satisfy both GqlOption and RestOption by implementing each's applyToX.

func Deprecated added in v0.3.0

func Deprecated(reason string) GqlOption

Deprecated marks the field deprecated. The reason shows up in SDL and the dashboard "deprecated" badge.

func Desc added in v0.3.0

func Desc(s string) GqlOption

Desc sets the resolver's description (shown on the dashboard and in SDL documentation).

func GraphMiddleware added in v0.3.0

func GraphMiddleware(name, description string, mw graph.FieldMiddleware) GqlOption

GraphMiddleware attaches a named graph-only middleware to the resolver. Equivalent to go-graph's WithNamedMiddleware — the name appears in FieldInfo.Middlewares for dashboard rendering (and "auth", "cors", etc. get labelled "builtin" via nexus/middleware.Builtins).

For cross-transport middleware, prefer nexus.Use(middleware.Middleware{...}) — it accepts the same bundle on REST and GraphQL alike.

func Middleware deprecated added in v0.3.0

func Middleware(name, description string, mw graph.FieldMiddleware) GqlOption

Middleware is a deprecated alias for GraphMiddleware. Exists so existing call sites keep compiling while codebases migrate to nexus.Use for cross-transport middleware.

Deprecated: use GraphMiddleware for graph-only middleware, or nexus.Use with a middleware.Middleware bundle for cross-transport.

func OnService added in v0.3.0

func OnService[S any]() GqlOption

OnService routes this registration onto the given service wrapper type without requiring the handler to take it as a dep. Use when the handler is minimal (`func NewListQuestions(q *QuestionsDB) (...)`) but still belongs to a particular service on the dashboard.

nexus.AsQuery(NewListQuestions, nexus.OnService[*AdvertsService]())

The resolver still needs the owning service to have been provided into the fx graph elsewhere so MountGraphQL can pick up the field.

func Op added in v0.3.0

func Op(name string) GqlOption

Op overrides the inferred op name.

func RateLimit added in v0.3.0

func RateLimit(l ratelimit.Limit) GqlOption

RateLimit declares a baseline rate limit for this op. The auto-mount registers it with the app's rate-limit store and wires an enforcement middleware that consults the store on every request. Operators can override the effective limit live from the dashboard — the declared baseline stays in source-of-truth, the override survives in the store.

nexus.AsMutation(NewCreateAdvert,
    nexus.RateLimit(ratelimit.Limit{RPM: 30, PerIP: true}),
)

Burst defaults to RPM/6 when zero (10-second burst window). Set PerIP to true to scope the bucket to the caller's IP; leave false for a shared global bucket.

func WithArgValidator added in v0.3.0

func WithArgValidator(arg string, vs ...graph.Validator) GqlOption

WithArgValidator adds one or more validators to a named arg, beyond what the struct tags declare. Useful for project-specific rules (graph.Custom validators that call into other code).

type MiddlewareOption added in v0.3.0

type MiddlewareOption struct {
	// contains filtered or unexported fields
}

MiddlewareOption carries a Middleware across the AsRest/AsQuery/... call sites. Each transport's option type embeds / converts this, so a single nexus.Use(...) expression can appear wherever the transport accepts it.

func Use added in v0.3.0

Use attaches a transport-agnostic middleware bundle to a registration. Works on AsRest, AsQuery, AsMutation, (future AsSubscription / AsWebSocket) — each transport picks the realization it understands from the bundle (Gin for REST/WS upgrade, Graph for GraphQL). Missing fields are silently ignored so a single bundle can degrade gracefully across transports.

rl := ratelimit.NewMiddleware(store, key, ratelimit.Limit{RPM: 30})
fx.Provide(
    nexus.AsMutation(NewCreateAdvert, nexus.Use(rl)),
    nexus.AsRest("POST", "/quick", NewQuick, nexus.Use(rl)),
)

For app-wide coverage (every REST endpoint + GraphQL POST + WS upgrade + the dashboard itself) put the middleware in Config.GlobalMiddleware instead of naming it on each registration.

type NexusResourceProvider added in v0.3.0

type NexusResourceProvider interface {
	NexusResources() []resource.Resource
}

NexusResourceProvider is implemented by managers that know the external resources they front. A manager's NexusResources slice is used in two places:

  1. Boot-time registration via nexus.ProvideResources — each returned resource.Resource is added to the app registry so it appears on the dashboard with its health, description, and details.
  2. Service attachment via the GraphQL auto-mount — whenever a resolver names this manager as a dep, every resource in the slice gets linked to the owning service by name, drawing the architecture edge.

A manager may list more resources than any one handler uses; the edge is drawn per named resource on every service that mentions the manager.

func (m *DBManager) NexusResources() []resource.Resource {
    var out []resource.Resource
    m.Each(func(name string, db *DB) {
        out = append(out, resource.NewDatabase(name, ...))
    })
    return out
}

type Option

type Option interface {
	// contains filtered or unexported methods
}

Option composes a nexus app. Everything returned by Provide, Supply, Invoke, Module, AsRest, AsQuery, AsMutation, AsWebSocket, AsSubscription is an Option, ready to pass to Run. fx is an implementation detail — user code imports only nexus.

func AsMutation added in v0.3.0

func AsMutation(fn any, opts ...GqlOption) Option

AsMutation is the mutation analogue of AsQuery.

func AsQuery added in v0.3.0

func AsQuery(fn any, opts ...GqlOption) Option

AsQuery registers a GraphQL query from a plain Go handler. The handler's signature is inspected reflectively:

  • First param should be the service wrapper (e.g. *AdvertsService). Its type is used as the fx value-group key so MountGraphQL[*AdvertsService] picks up this query.
  • Subsequent params are fx-injected deps.
  • Optional last param is an args struct. Field tags drive arg config: graphql:"name" — arg name (defaults to lowercased field name) graphql:"name,required" — NonNull validate:"required" — graph.Required() validate:"len=3|120" — graph.StringLength(3, 120) validate:"int=1|100" — graph.Int(1, 100) validate:"oneof=a|b|c" — graph.OneOf("a","b","c") Chain multiple rules with commas.
  • Return type must be (T, error). T is the resolver's return; pointer and slice wrappers are honored.

Op name defaults to the handler's func name, stripping a leading "New" and lowercasing the first rune ("NewGetAllAdverts" → "getAllAdverts"). Override with nexus.Op("explicit").

fx.Provide(
    nexus.AsQuery(NewGetAllAdverts),
    nexus.AsMutation(NewCreateAdvert,
        nexus.Middleware("auth", "Bearer token", AuthMw)),
)

func AsRest added in v0.3.0

func AsRest(method, path string, fn any, opts ...RestOption) Option

AsRest registers a REST endpoint from a plain Go handler. The handler's signature is inspected via reflection:

  • Leading params are fx-injected deps. The first such param should be your service wrapper (see docs on Service) — its type grounds the endpoint in a service node on the dashboard.
  • The optional last param is an "args" struct whose tags direct gin on how to bind from the request: uri:"id" → ShouldBindUri query:"x" → ShouldBindQuery header:"x" → ShouldBindHeader form:"x" → ShouldBind (multipart/url-encoded) json:"x" → ShouldBindJSON (for non-GET; default when other binders are absent)
  • The return may be (T, error), (T), (error), or nothing. T gets JSON-marshalled with status 200 (201 for POST) on success; errors become status 500 with {"error": "..."}.

Returns an fx.Option; drop it into fx.Provide.

fx.Provide(
    nexus.AsRest("GET", "/pets",     NewListPets),
    nexus.AsRest("POST", "/pets",    NewCreatePet),
    nexus.AsRest("GET", "/pets/:id", NewGetPet),
)

func AsRestHandler added in v0.7.3

func AsRestHandler(method, path string, factory any, opts ...RestOption) Option

AsRestHandler registers a REST endpoint whose handler is a plain gin.HandlerFunc supplied by a *factory* function. The factory is the fx-resolved piece: its parameters are the deps needed to build the handler (controllers, resources, other services), its single return is the gin.HandlerFunc that serves requests.

Use this when the handler already manages its own request binding and response shaping (typical for code migrated from ad-hoc Gin routes) but you still want module annotation, metrics, and the dashboard packet-animation treatment AsRest provides:

nexus.Module("oats-rest",
    nexus.AsRestHandler("POST", "/api/devices/register",
        func(d *DeviceController) gin.HandlerFunc { return d.RegisterDevice },
        nexus.Description("Register a device"),
        auth.Required(),
    ),
)

Factory signature requirements:

  • Zero or more parameters (fx-injected deps).
  • Exactly one return value of type gin.HandlerFunc.

On the dashboard this endpoint appears under its enclosing nexus.Module (same grouping as AsRest / AsQuery), with metrics + trace middleware attached so request.op events drive the live packet animation.

func AsSubscription added in v0.3.0

func AsSubscription(fn any, opts ...GqlOption) Option

AsSubscription is reserved for a follow-up: subscriptions use a separate builder (SubscriptionResolver[T] with PubSub + channel plumbing) that we haven't taught the reflective path yet. Use graph.NewSubscriptionResolver directly for now; once the reflective SubscriptionResolverFromType exists this helper will mirror AsQuery/AsMutation.

func AsWebSocket added in v0.3.0

func AsWebSocket(path string, fn any, opts ...WsOption) Option

AsWebSocket is reserved. WebSocket endpoints have a multi-callback shape (OnConnect / OnMessage / OnClose) or use Hub-managed messaging — neither collapses into the single-function reflective signature that AsRest and AsQuery/AsMutation share. For now, continue declaring WS endpoints via (*nexus.Service).WebSocket(path).OnMessage(...).Mount().

A follow-up will introduce a handler shape like:

func NewPetsStream(svc *PetsService, hub *ws.Hub, conn *websocket.Conn) error { ... }

with AsWebSocket reflecting on it, registering the ws.Builder, and returning an fx.Invoke.

func AsWorker added in v0.7.0

func AsWorker(name string, fn any) Option

AsWorker registers a long-lived background worker. The framework owns lifecycle — it starts the worker on fx.Start in its own goroutine, cancels its context on fx.Stop, and records status + last-error on the registry so the dashboard can surface it.

Signature requirements:

  • First parameter MUST be context.Context. The framework supplies a context that cancels when the app stops; workers are expected to honor it and return.

  • Remaining parameters are fx-injected deps (same rules as a constructor — they must exist in the graph).

  • Return is optional: a single (error) return lets the worker report a fatal error. context.Canceled / nil is treated as a clean stop; anything else sets Status="failed" + LastError.

    nexus.AsWorker("cache-invalidation", func(ctx context.Context, db *OatsDB, cache *CacheManager, logger *zap.Logger) error { for !db.IsConnected() { select { case <-ctx.Done(): return ctx.Err() case <-time.After(time.Second): } } listener := pq.NewListener(db.ConnectionString(), ...) defer listener.Close() _ = listener.Listen("cache_invalidation") for { select { case <-ctx.Done(): return nil case n := <-listener.Notify: handle(n, cache) } } })

Resource / service deps (for the architecture graph) are detected the same way nexus.ProvideService does it — any param implementing NexusResourceProvider contributes its resources, any param whose type is a service wrapper contributes a service dep.

A worker panic is caught and reported as Status=failed; the app keeps running. For restart semantics, wrap your worker body in a loop that re-dials on ctx.Done() exit OR let the operator restart the app.

func Invoke added in v0.3.0

func Invoke(fns ...any) Option

Invoke runs a function at startup, resolving its parameters from the graph. Use for side-effects on boot — attaching resources, registering hooks, seeding state. Multiple Invoke options run in registration order.

nexus.Invoke(func(app *nexus.App, dbs *DBManager) {
    app.OnResourceUse(dbs)
})

func Module added in v0.3.0

func Module(name string, opts ...Option) Option

Module groups options under a name. Mirrors fx.Module's logging — the group name appears in startup/shutdown logs and in error messages, which helps when several modules touch the same service or resource. The name is also stamped onto every AsQuery/AsMutation/AsRest registration inside the module so the dashboard's architecture view can group endpoints by module container.

var advertsModule = nexus.Module("adverts",
    nexus.Provide(NewAdvertsService),
    nexus.AsQuery(NewGetAllAdverts),
    nexus.AsMutation(NewCreateAdvert, …),
)

func Provide added in v0.3.0

func Provide(fns ...any) Option

Provide registers one or more constructor functions with the dep graph. Return types are entered into the graph; parameter types are resolved from it. Same semantics as fx.Provide.

nexus.Provide(NewDBManager, NewCacheManager)

func ProvideResources added in v0.3.0

func ProvideResources(fns ...any) Option

ProvideResources is like Provide but also auto-registers each constructed instance's resources at boot. For any fn whose returned value implements NexusResourceProvider, every resource.Resource it reports is passed to app.Register; if it also satisfies UseReporter (e.g. *multi.Registry), app.OnResourceUse is wired automatically so resolver→resource edges appear on first UsingCtx call.

This replaces the old pattern of a "resources" module full of resource.NewDatabase / NewCache calls — managers now describe their resources themselves via NexusResources() []resource.Resource, and a single ProvideResources does all the wiring.

nexus.ProvideResources(ProvideDBs, NewCacheManager)

Types that don't implement either interface fall through to a plain Provide — so it's safe to pass mixed providers.

func ProvideService added in v0.6.0

func ProvideService(fn any) Option

ProvideService is like Provide but inspects the constructor's parameters for resources (NexusResourceProvider) and other services (service-wrapper types — pointer to a struct that anonymously embeds *nexus.Service) and records them onto the service's registry entry. The dashboard uses those records to draw architecture edges at the SERVICE layer:

func NewAdvertsService(app *nexus.App, users *UsersService, db *DBManager) *AdvertsService {
    return &AdvertsService{Service: app.Service("adverts")}
}

nexus.ProvideService(NewAdvertsService)
// → registry sees: adverts.ServiceDeps = ["users"]
//                  adverts.ResourceDeps = [<whatever db.NexusResources() returns>]

The constructed service still flows into fx's dep graph the same way fx.Provide would wire it, so downstream consumers (other services, resolvers) get it injected normally. The ONLY side effect is the registry metadata that feeds the architecture view — nothing observable happens at runtime.

Only the first return value is inspected; trailing (T, error) returns are supported. Constructors that don't return a service wrapper are still Provide'd (same as plain Provide), but no service deps are recorded — the option gracefully no-ops in that case.

func Raw added in v0.3.0

func Raw(opt fx.Option) Option

Raw is an escape hatch: accept any fx.Option and route it through nexus. For features nexus hasn't mirrored yet (fx.Decorate, fx.Replace, named values via fx.Annotate with ParamTags, etc.) or one-off integrations. Normal apps never need it.

nexus.Raw(fx.Decorate(wrapLogger))

func Supply added in v0.3.0

func Supply(values ...any) Option

Supply puts concrete values into the graph (no constructor). Useful for config structs or pre-built instances created outside the fx graph.

nexus.Supply(nexus.Config{Addr: ":8080"})   // rare — Run takes Config directly
nexus.Supply(myAlreadyBuiltClient)          // typical

type Params added in v0.3.0

type Params[T any] struct {
	Context context.Context
	Args    T
	Source  any
	Info    graphql.ResolveInfo
}

Params is the bundle a reflective resolver receives when it wants more than just typed args — namely the resolve context, parent source, or schema info. Use it as the last parameter of an AsQuery / AsMutation handler (or AsRest, where only Context is filled).

func NewCreateAdvert(
    svc *AdvertsService,
    dbs *DBManager,
    cache *CacheManager,
    p nexus.Params[CreateAdvertArgs],
) (*AdvertResponse, error) {
    advert := Advert{Title: p.Args.Title, EmployerName: p.Args.EmployerName}
    return create(p.Context, advert)
}

The type parameter T is the args struct — its fields carry the same `graphql:"..."` and `validate:"..."` tags as the legacy flat-args form. Use Params[struct{}] for resolvers that need Context/Source/Info but have no user-supplied args.

For simple handlers that only need a context.Context, you can still take that as a plain parameter; Params[T] is additive, not required.

type RestOption added in v0.3.0

type RestOption interface {
	// contains filtered or unexported methods
}

RestOption tunes an AsRest registration. Interface (not a func) so nexus.Use can satisfy both GqlOption and RestOption from a single value. The one-off func-shaped helpers below adapt via restOptionFn.

func Description added in v0.3.0

func Description(s string) RestOption

Description sets the human-readable description shown on the dashboard.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service is a named group of endpoints. Services are the nodes the dashboard draws in the architecture view.

A Service also carries the GraphQL mount path for reflective controllers registered via nexus.AsQuery / AsMutation. The auto-mount step inside fxmod.Module reads this path at fx.Start and wires the schema onto the engine. Default is "/graphql"; override via (*Service).AtGraphQL.

func (*Service) AtGraphQL added in v0.3.0

func (s *Service) AtGraphQL(path string) *Service

AtGraphQL overrides the GraphQL mount path for this service. Most apps keep the default ("/graphql") — override only when you need a per-service path (e.g. "/admin/graphql") or want to unify multiple nexus apps behind the same reverse proxy.

app.Service("admin").AtGraphQL("/admin/graphql").Describe("Admin ops")

func (*Service) Attach

func (s *Service) Attach(r resource.Resource) *Service

Attach links a resource to this service so the dashboard draws an edge. If the resource isn't already registered, Attach registers it too — that's convenient for ad-hoc services but means typos silently create orphan nodes. For centrally-declared resources, prefer .Using("name") instead.

func (*Service) Auth added in v0.3.0

func (s *Service) Auth(fn UserDetailsFn) *Service

Auth wires a Bearer-token → user hook. Resolvers read the user via graph.GetRootInfo(p, "details", &user) after successful authentication. Per-service because different services often use different auth mechanisms (admin vs public).

func (*Service) Describe

func (s *Service) Describe(desc string) *Service

func (*Service) GraphQLPath added in v0.3.0

func (s *Service) GraphQLPath() string

GraphQLPath returns the mount path set via AtGraphQL (or the default). Read by the auto-mount Invoke; users rarely need this.

func (*Service) MountGraphQL

func (s *Service) MountGraphQL(path string, schema *graphql.Schema, opts ...gql.Option)

MountGraphQL attaches schema (assembled by go-graph or graphql-go) and auto-registers every operation into the nexus registry. Pass gql.With* options for auth (UserDetailsFn), Playground, Pretty, and DEBUG.

func (*Service) Name added in v0.3.0

func (s *Service) Name() string

Name returns the service's identifier (same string used on the dashboard and passed to *App.Service). Exposed so framework internals (the auto- mount Invoke, service wrappers) can identify a service without reaching into the private field.

func (*Service) REST

func (s *Service) REST(method, path string) *rest.Builder

func (*Service) Using

func (s *Service) Using(names ...string) *Service

Using attaches already-registered resources by name so the dashboard draws edges. An empty string resolves to the default database (the resource of kind Database marked resource.AsDefault(), or the lexically-first if none is marked). Unknown names are attached anyway so the registry shows a disconnected edge — surfacing the typo rather than hiding it.

app.Service("adverts").Using("").MountGraphQL(...)               // default DB
app.Service("qb").Using("questions", "session").MountGraphQL(...) // explicit

func (*Service) UsingDefaults

func (s *Service) UsingDefaults() *Service

UsingDefaults attaches the default resource of every kind that has at least one registered (database, cache, queue). Useful for services that touch the common "main DB + session cache" pair without naming either.

func (*Service) WebSocket

func (s *Service) WebSocket(path string) *ws.Builder

type UseReporter

type UseReporter interface {
	OnUse(func(ctx context.Context, name string))
}

UseReporter is satisfied by any type that exposes an OnUse hook with this exact signature. multi.Registry and anything embedding it fit — including the project's own DBManager wrapper. This is a structural interface so nexus doesn't need to import nexus/multi directly.

type UserDetailsFn added in v0.3.0

type UserDetailsFn func(ctx context.Context, token string) (context.Context, any, error)

UserDetailsFn, when set on a service, routes GraphQL requests through graph.NewHTTP so resolvers can read the authenticated user via graph.GetRootInfo(p, "details", &user). Returning an error aborts the request with the framework's standard unauthenticated shape.

type WsOption added in v0.3.0

type WsOption func(*wsConfig)

WsOption is a placeholder type so callers can migrate option calls in advance of the implementation.

Directories

Path Synopsis
Package auth is nexus's built-in authentication surface.
Package auth is nexus's built-in authentication surface.
Package cache provides a Redis + in-memory hybrid cache for nexus apps, ported from the oats_applicant implementation.
Package cache provides a Redis + in-memory hybrid cache for nexus apps, ported from the oats_applicant implementation.
Package cron registers and runs scheduled jobs alongside the app's HTTP surface.
Package cron registers and runs scheduled jobs alongside the app's HTTP surface.
Package dashboard mounts the nexus introspection surface under /__nexus.
Package dashboard mounts the nexus introspection surface under /__nexus.
Package db is nexus's driver-agnostic GORM manager.
Package db is nexus's driver-agnostic GORM manager.
examples
fxapp command
An example showing nexus's top-level builder: nexus.Run composes modules, nexus.Provide / Invoke / Module replace fx.Provide / Invoke / Module — no go.uber.org/fx import in user code.
An example showing nexus's top-level builder: nexus.Run composes modules, nexus.Provide / Invoke / Module replace fx.Provide / Invoke / Module — no go.uber.org/fx import in user code.
graphapp command
Example: a complete GraphQL service wired with nexus's reflective controller API.
Example: a complete GraphQL service wired with nexus's reflective controller API.
petstore command
wstest command
Package graph provides a modern, secure GraphQL handler for Go with built-in authentication, validation, and an intuitive builder API.
Package graph provides a modern, secure GraphQL handler for Go with built-in authentication, validation, and an intuitive builder API.
Package metrics records per-endpoint request counts + errors so the dashboard can show at-a-glance health next to each op: how busy it is, whether it's failing, and if so, what the last error was.
Package metrics records per-endpoint request counts + errors so the dashboard can show at-a-glance health next to each op: how busy it is, whether it's failing, and if so, what the last error was.
Package middleware defines nexus's cross-transport middleware model.
Package middleware defines nexus's cross-transport middleware model.
Package multi routes N named instances of the same type behind a single .Using(name) dispatcher.
Package multi routes N named instances of the same type behind a single .Using(name) dispatcher.
Package ratelimit provides the rate-limit primitives nexus uses to throttle endpoints: a Limit shape, a Store interface (pluggable to memory, Redis, or any backend), and a token-bucket MemoryStore.
Package ratelimit provides the rate-limit primitives nexus uses to throttle endpoints: a Limit shape, a Store interface (pluggable to memory, Redis, or any backend), and a token-bucket MemoryStore.
Package registry stores metadata about every endpoint a nexus app exposes.
Package registry stores metadata about every endpoint a nexus app exposes.
Package resource defines the abstractions nexus uses to know about databases, caches, message queues, and other external dependencies so they show up in the dashboard's Architecture view with health status.
Package resource defines the abstractions nexus uses to know about databases, caches, message queues, and other external dependencies so they show up in the dashboard's Architecture view with health status.
Package trace captures request-lifecycle events (start, end, downstream calls, logs) into an in-memory ring buffer and fans them out to subscribers such as the dashboard.
Package trace captures request-lifecycle events (start, end, downstream calls, logs) into an in-memory ring buffer and fans them out to subscribers such as the dashboard.
transport
gql
Package gql mounts a GraphQL schema (typically assembled by github.com/paulmanoni/nexus/graph) onto Gin and introspects its operations into the nexus registry.
Package gql mounts a GraphQL schema (typically assembled by github.com/paulmanoni/nexus/graph) onto Gin and introspects its operations into the nexus registry.
rest
Package rest wires REST endpoints onto a Gin engine and records metadata about them in the nexus registry.
Package rest wires REST endpoints onto a Gin engine and records metadata about them in the nexus registry.
ws
Package ws wires WebSocket endpoints onto a Gin engine using gorilla/websocket and records metadata about them in the nexus registry.
Package ws wires WebSocket endpoints onto a Gin engine using gorilla/websocket and records metadata about them in the nexus registry.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL