Documentation
¶
Overview ¶
Package nexus deployment annotations.
DeployAs marks a module as a candidate deployment unit — when the framework later supports independent rollout (`nexus gen clients` + `NEXUS_DEPLOYMENT` boot selector), the tag identifies which binary the module belongs to. In today's monolith mode the tag is metadata only: it surfaces on the dashboard so readers can see the planned split topology before any splitting happens.
var users = nexus.Module("users",
nexus.DeployAs("users-svc"),
nexus.Provide(NewUsersService),
nexus.AsRest("GET", "/users/:id", NewGetUser),
)
Untagged modules are "always local" — they ride along with whichever deployment is active. Reserve DeployAs for modules with a real chance of being peeled out (separate codebase boundary, separate scaling needs, separate on-call rotation).
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
- func ClientIPFromCtx(ctx context.Context) string
- func DeploymentFromEnv() string
- func RoutePrefix(p string) routePrefixOption
- func Run(cfg Config, opts ...Option)
- func WithClientIP(ctx context.Context, ip string) context.Context
- type App
- func (a *App) Bus() *trace.Bus
- func (a *App) Cache() *cache.Manager
- func (a *App) Cron(name, schedule string) *CronBuilder
- func (a *App) Deployment() string
- func (a *App) Engine() *gin.Engine
- func (a *App) Metrics() metrics.Store
- func (a *App) OnResourceUse(target UseReporter)
- func (a *App) RateLimiter() ratelimit.Store
- func (a *App) Register(r resource.Resource)
- func (a *App) Registry() *registry.Registry
- func (a *App) Run(addr string) error
- func (a *App) Scheduler() *cron.Scheduler
- func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (a *App) Service(name string) *Service
- func (a *App) Version() string
- type AppOption
- func WithCache(m *cache.Manager) AppOption
- func WithDashboard() AppOption
- func WithDashboardMiddleware(bundles ...middleware.Middleware) AppOption
- func WithDashboardName(name string) AppOption
- func WithDeployment(name string) AppOption
- func WithEngine(e *gin.Engine) AppOption
- func WithGraphQLPath(path string) AppOption
- func WithMetricsStore(s metrics.Store) AppOption
- func WithRateLimitStore(s ratelimit.Store) AppOption
- func WithTracing(capacity int) AppOption
- func WithVersion(v string) AppOption
- type Config
- type CronBuilder
- type GqlField
- type GqlOption
- func Deprecated(reason string) GqlOption
- func Desc(s string) GqlOption
- func GraphMiddleware(name, description string, mw graph.FieldMiddleware) GqlOption
- func Middleware(name, description string, mw graph.FieldMiddleware) GqlOptiondeprecated
- func OnService[S any]() GqlOption
- func Op(name string) GqlOption
- func RateLimit(l ratelimit.Limit) GqlOption
- func WithArgValidator(arg string, vs ...graph.Validator) GqlOption
- type MiddlewareOption
- type NexusResourceProvider
- type Option
- func AsMutation(fn any, opts ...GqlOption) Option
- func AsQuery(fn any, opts ...GqlOption) Option
- func AsRest(method, path string, fn any, opts ...RestOption) Option
- func AsRestHandler(method, path string, factory any, opts ...RestOption) Option
- func AsSubscription(fn any, opts ...GqlOption) Option
- func AsWS(path, msgType string, fn any, opts ...WSOption) Option
- func AsWorker(name string, fn any) Option
- func DeployAs(tag string) Option
- func Invoke(fns ...any) Option
- func Module(name string, opts ...Option) Option
- func Provide(fns ...any) Option
- func ProvideResources(fns ...any) Option
- func ProvideService(fn any) Option
- func Raw(opt fx.Option) Option
- func Supply(values ...any) Option
- type Params
- type RestOption
- type Service
- func (s *Service) AtGraphQL(path string) *Service
- func (s *Service) Attach(r resource.Resource) *Service
- func (s *Service) Auth(fn UserDetailsFn) *Service
- func (s *Service) Describe(desc string) *Service
- func (s *Service) GraphQLPath() string
- func (s *Service) MountGraphQL(path string, schema *graphql.Schema, opts ...gql.Option)
- func (s *Service) Name() string
- func (s *Service) REST(method, path string) *rest.Builder
- func (s *Service) Using(names ...string) *Service
- func (s *Service) UsingDefaults() *Service
- func (s *Service) WebSocket(path string) *ws.Builder
- type UseReporter
- type UserDetailsFn
- type WSOption
- type WSSession
- func (s *WSSession) ClientID() string
- func (s *WSSession) Context() context.Context
- func (s *WSSession) Emit(eventType string, data any)
- func (s *WSSession) EmitToClient(eventType string, data any, clientIDs ...string)
- func (s *WSSession) EmitToRoom(eventType string, data any, room string)
- func (s *WSSession) EmitToUser(eventType string, data any, userIDs ...string)
- func (s *WSSession) JoinRoom(room string)
- func (s *WSSession) LeaveRoom(room string)
- func (s *WSSession) Metadata() map[string]any
- func (s *WSSession) Send(eventType string, data any) error
- func (s *WSSession) SendRaw(data []byte)
- func (s *WSSession) UserID() string
Constants ¶
const DefaultGraphQLPath = "/graphql"
DefaultGraphQLPath is the mount path nexus.AsQuery / AsMutation use when a service hasn't called AtGraphQL.
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
ClientIPFromCtx pulls the IP stashed via WithClientIP (or ratelimit.WithClientIP). Empty when absent.
func DeploymentFromEnv ¶ added in v0.9.0
func DeploymentFromEnv() string
DeploymentFromEnv reads NEXUS_DEPLOYMENT. The single-binary, multi-shape pattern: the same compiled binary boots as different deployment units based on the env var alone — no rebuild, no flags. Returns "" when unset, which the framework treats as monolith mode.
func main() {
nexus.Run(nexus.Config{
Deployment: nexus.DeploymentFromEnv(),
// ...
}, allModules...)
}
func RoutePrefix ¶ added in v0.7.5
func RoutePrefix(p string) routePrefixOption
RoutePrefix prepends a string to the paths of the REST endpoints it applies to. Two usage patterns:
// Module-wide: every AsRest / AsRestHandler in the module sees "/api/v1".
nexus.Module("adverts", nexus.RoutePrefix("/api/v1"),
nexus.AsRest("GET", "/adverts", NewListAdverts), // → /api/v1/adverts
nexus.AsRest("POST", "/adverts", NewCreateAdvert),
)
// Per-endpoint:
nexus.AsRest("GET", "/health", NewHealth, nexus.RoutePrefix("/ops"))
The prefix is stored verbatim — include (or omit) the leading slash yourself; nexus does not normalize. Stacking within a single Module concatenates left-to-right, so `Module(..., RoutePrefix("/a"), RoutePrefix("/b"), AsRest("GET", "/x", ...))` mounts at /a/b/x. Prefixes do NOT stack across nested Module calls — the inner module already stamped its children before the outer sees them. If you need stacking, compose the prefix string explicitly.
func Run ¶ added in v0.3.0
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
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 (*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) Deployment ¶ added in v0.9.0
Deployment is the deployment-unit name this binary boots as, or "" for monolith. Read by future cross-module clients to choose the in-process shortcut over HTTP.
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:
- reads the current trace.Span from ctx so we know which service made the call
- AttachResource(service, resource) on the registry — edge appears live
- 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
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
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 ¶
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 WithDeployment ¶ added in v0.9.0
WithDeployment names the deployment unit this binary runs as. Empty = monolith. Mirrors Config.Deployment for callers using the lower-level nexus.New(...) entry point.
func WithEngine ¶
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 WithGraphQLPath ¶ added in v0.7.7
WithGraphQLPath overrides the default GraphQL mount path used by services that don't call (*Service).AtGraphQL themselves. Empty falls back to DefaultGraphQLPath ("/graphql").
func WithMetricsStore ¶ added in v0.3.0
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
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 ¶
WithTracing enables per-request trace events, buffered in a ring of the given capacity. Required for the dashboard's event stream to show anything.
func WithVersion ¶ added in v0.9.0
WithVersion stamps the binary's version onto /__nexus/config. Used by generated clients to detect peer-version skew across services in a split deployment.
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
// GraphQLPath overrides the default mount path for auto-generated
// GraphQL services. Empty falls back to "/graphql". Per-service
// paths via (*Service).AtGraphQL(p) still win over this default;
// use this Config field to change where the auto-mount fallback
// service (the one created when no *Service dep is present on a
// handler) and any other service that doesn't call AtGraphQL
// will mount their schema.
//
// nexus.Config{GraphQLPath: "/api/graphql"}
GraphQLPath string
// 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
// Deployment names the deployment unit this binary runs as. Empty
// = monolith mode (every module is local — current behavior).
// When set, the framework knows which DeployAs-tagged modules are
// "local" vs "remote" so future codegen'd clients can pick the
// in-process or HTTP path accordingly. Today this field is
// metadata only — surfaced on /__nexus/config so the dashboard
// can render the active deployment.
//
// Convention: pass DeploymentFromEnv() in main() so a single
// binary can boot as different units across environments without
// recompiling.
Deployment string
// Version stamps the binary's version on /__nexus/config. Used by
// generated clients to detect peer-version skew across services
// in a split deployment ("service A is on v2, service B on v1"
// is the source of most weird microservice bugs). Defaults to
// "dev" when unset. Stamp via -ldflags at release:
//
// go build -ldflags "-X main.version=$GIT_SHA"
// nexus.Config{Version: version}
Version string
}
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
// Deployment is the DeployAs tag of the enclosing module; "" when
// the module is always-local. Forwarded to the registry entry so
// dashboard consumers can group by deployment unit.
Deployment string
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
Deprecated marks the field deprecated. The reason shows up in SDL and the dashboard "deprecated" badge.
func Desc ¶ added in v0.3.0
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
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 RateLimit ¶ added in v0.3.0
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.
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
func Use(m middleware.Middleware) MiddlewareOption
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
NexusResourceProvider is implemented by managers that know the external resources they front. A manager's NexusResources slice is used in two places:
- 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.
- 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
AsMutation is the mutation analogue of AsQuery.
func AsQuery ¶ added in v0.3.0
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
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 AsWS ¶ added in v0.9.0
AsWS registers one message-type-scoped handler on a WebSocket endpoint. Multiple AsWS calls with the same path share a single connection pool — the framework dispatches inbound messages by their envelope `type` to the matching handler.
type ChatPayload struct{ Text string }
func NewChatSend(svc *ChatSvc, sess *nexus.WSSession,
p nexus.Params[ChatPayload]) error {
sess.EmitToRoom("chat.message",
map[string]string{"text": p.Args.Text, "user": sess.UserID()},
"lobby")
return nil
}
nexus.AsWS("/events", "chat.send", NewChatSend, auth.Required())
Wire protocol — every message is wrapped in the framework's envelope:
{ "type": "chat.send", "data": {...}, "timestamp": <unix> }
The built-in types `ping`, `authenticate`, `subscribe`, `unsubscribe` are handled by the hub directly and never reach user handlers.
Handler signature: same reflective convention as AsRest / AsQuery —
- fx-injected deps anywhere (service wrappers, resources, other services);
- an optional *nexus.WSSession parameter gets the live connection handle;
- an optional nexus.Params[T] carries the decoded message payload in Args;
- return (error) — a non-nil error is sent back on the same connection as an `error` envelope event. The connection stays open.
Middleware (auth.Required, rate limits, etc.) on the FIRST AsWS call for a path is installed on the HTTP upgrade route and gates every subsequent connection. Middleware declared on later AsWS calls for the same path is ignored with a warning log — all dispatches share one upgrade route.
func AsWorker ¶ added in v0.7.0
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 DeployAs ¶ added in v0.9.0
DeployAs records the deployment tag for the enclosing nexus.Module. Multiple DeployAs calls in one Module are not allowed — the last one wins, matching the way Module() handles duplicated RoutePrefix today.
func Invoke ¶ added in v0.3.0
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
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
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
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
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
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))
type Params ¶ added in v0.3.0
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
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 ¶
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) GraphQLPath ¶ added in v0.3.0
GraphQLPath returns the mount path set via AtGraphQL (or the default). Read by the auto-mount Invoke; users rarely need this.
func (*Service) MountGraphQL ¶
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
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) Using ¶
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 ¶
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.
type UseReporter ¶
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
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.9.0
type WSOption interface {
// contains filtered or unexported methods
}
WSOption tunes an AsWS registration. Interface (not a func) so nexus.Use and auth.Required() — which return middleware bundles — can satisfy both RestOption and WSOption from a single value.
type WSSession ¶ added in v0.9.0
type WSSession struct {
// contains filtered or unexported fields
}
WSSession is the per-connection handle injected into AsWS handlers. Every handler that declares `*nexus.WSSession` as a parameter receives the session tied to the connection that produced the current inbound message. Safe for concurrent use — wraps a ws.Hub under the hood.
The framework uses the same envelope protocol as the built-in ws.Hub:
{ "type": "chat.send", "data": {...}, "timestamp": <unix> }
Emit / EmitToUser / EmitToRoom / EmitToClient publish in that shape; SendRaw is the escape hatch for non-envelope payloads.
func (*WSSession) ClientID ¶ added in v0.9.0
ClientID is the UUID the hub minted for this connection at upgrade time.
func (*WSSession) Context ¶ added in v0.9.0
Context returns a context cancelled when the connection disconnects. Safe to pass downstream — long-running work will unblock on hangup.
func (*WSSession) Emit ¶ added in v0.9.0
Emit broadcasts an envelope to every connection on this endpoint.
func (*WSSession) EmitToClient ¶ added in v0.9.0
EmitToClient sends an envelope to the connections with the given IDs.
func (*WSSession) EmitToRoom ¶ added in v0.9.0
EmitToRoom sends an envelope to every connection subscribed to room.
func (*WSSession) EmitToUser ¶ added in v0.9.0
EmitToUser sends an envelope to every connection authed as one of userIDs.
func (*WSSession) JoinRoom ¶ added in v0.9.0
JoinRoom subscribes this connection to a room. Matching server-side helper for the client's `{"type":"subscribe","room":"..."}` message.
func (*WSSession) Metadata ¶ added in v0.9.0
Metadata is the map the hub's identify hook populated at upgrade time. Read freely; mutation is not safe across goroutines.
func (*WSSession) Send ¶ added in v0.9.0
Send wraps data in an envelope and unicasts it to this connection.
Source Files
¶
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. |
|
cmd
|
|
|
nexus
command
Command nexus is the developer CLI for the nexus framework.
|
Command nexus is the developer CLI for the nexus framework. |
|
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
|
|
|
wsecho
command
Command wsecho is a runnable demo of nexus.AsWS: one WebSocket path (/events) with two typed message handlers (chat.send + chat.typing) sharing one connection pool.
|
Command wsecho is a runnable demo of nexus.AsWS: one WebSocket path (/events) with two typed message handlers (chat.send + chat.typing) sharing one connection pool. |
|
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. |

