client

package
v1.23.1 Latest Latest
Warning

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

Go to latest
Published: Jun 23, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package client embeds and serves the nexus client SDK — a JS/TS runtime, generated TypeScript types, and Vue 3 composables — directly from the Go binary. No separate npm package; the SDK ships with the app.

Mount installs five routes under cfg.Path (default "/__nexus/client"):

GET  <path>/manifest.json    application/json
GET  <path>/client.js        application/javascript (ESM)
GET  <path>/client.d.ts      application/typescript (paired with client.js)
GET  <path>/vue.js           application/javascript (ESM, Vue 3)
GET  <path>/vue.d.ts         application/typescript (paired with vue.js)

The manifest is the foundation. It enumerates every endpoint (REST, GraphQL, WebSocket), their typed args/return schemas, the running auth strategy, and a deduped pool of named struct types. client.js fetches it on construct; client.d.ts is generated from it at boot.

Index

Constants

View Source
const (
	DefaultTokenField = "data.token"
	DefaultCSRFCookie = "csrftoken"
	DefaultCSRFHeader = "X-CSRFToken"
)

Default auth-section hints applied when auth.Config leaves them unset (see AuthMeta.WithDefaults). The CSRF pair follows the Django / Laravel convention (csrftoken + X-CSRFToken) rather than the Angular one (XSRF-TOKEN + X-XSRF-TOKEN) so the common server-rendered-cookie setup works with zero config; the token field targets the {status, data:{token}} envelope Go REST handlers typically ship, with the SDK's heuristic walk as a safety net for top-level {token} responses (see extractLoginToken in nexus-client.js — these constants are mirrored there).

View Source
const DefaultPath = "/__nexus/client"

DefaultPath is the URL prefix the SDK routes mount under when Config.Path is empty. Sits beside the dashboard at /__nexus so the framework's "introspection + tooling surface" stays under one namespace.

View Source
const SchemaVersion = "client.v1"

SchemaVersion is the contract version emitted in Manifest.Version. SDK consumers gate on the major: a "client.v1" manifest is guaranteed to be readable by any v1-aware consumer. Additive changes (a new EndpointInfo field) leave the version untouched; field removal or shape change bumps the major.

Variables

This section is empty.

Functions

func GenerateClientDTS added in v0.28.6

func GenerateClientDTS(m Manifest) string

GenerateClientDTS projects a SDK manifest into the TypeScript declaration paired with client.js. Every symbol is a TOP-LEVEL export (no `declare module` wrappers) so the file auto-pairs with client.js whether the consumer imports it via the runtime URL '/__nexus/client/client.js' or as a relative on-disk path './sdk/client.js' after `nexus client --out` (or Config.Client.OutDir).

Output shape:

  1. // header — schema version + "do not edit" banner
  2. `export interface <Name>` per entry in manifest.Refs (deduped pool of named struct types referenced by endpoints)
  3. `export interface RestEndpoints` discriminated map keyed by "<METHOD> <PATH>" → { args, return }
  4. `export interface GraphqlOps` keyed by op name
  5. `export interface WSMessages` keyed by WS path → msgType → args
  6. ExtractRestMethod / ExtractRestPath helpers
  7. The runtime surface as top-level exports: NexusError, NexusClient, AuthNamespace, CrudHandle, WSHandle, WSEnvelope, TokenStore, memoryTokenStore, localStorageTokenStore.

Pure function: same manifest in, same string out. Caller (Handler.build) memoizes via sync.Once.

func GenerateDTS

func GenerateDTS(m Manifest) string

GenerateDTS retained for back-compat — returns the client-paired declaration. Older callers that wrote a single client.d.ts to disk continue to work; they should migrate to also writing vue.d.ts via GenerateVueDTS so Vue imports type-check.

func GenerateNexusTS added in v0.28.6

func GenerateNexusTS(m Manifest) string

GenerateNexusTS produces an OPTIONAL barrel file that re-exports every composable + the discriminated maps + manifest-derived named types from one place. Apps that prefer importing directly from './vue.js' can ignore (or delete) this file entirely — the page-shared NexusClient now picks up Vite env vars (VITE_NEXUS_API, VITE_NEXUS_TOKEN) inside useNexus() itself, so no client-construction wiring is needed.

Designed as a STARTER scaffold — Dump uses WriteIfMissing so a second boot doesn't clobber edits the developer has made.

Use it when you want a single import path:

import { useNexus, useGqlQuery, useAuth } from '@/sdk/nexus'
import type { Pet, User } from '@/sdk/nexus'

Skip it when bare imports from './vue.js' / './client.js' are fine — useNexus() is a singleton either way.

func GenerateReactDTS added in v0.83.0

func GenerateReactDTS(m Manifest) string

GenerateReactDTS projects a manifest into the .d.ts paired with react.js. Same sibling-import shape as vue.d.ts: pulls the discriminated maps + runtime classes from './client.js' and re-exports them through React-shaped hook signatures.

React primitives (useState / useEffect / etc.) are NOT imported in the .d.ts — every export is a function with a plain return type. The runtime's React import is the host app's responsibility, same as Vue.

func GenerateVueDTS added in v0.28.6

func GenerateVueDTS(m Manifest) string

GenerateVueDTS projects a manifest into the .d.ts paired with vue.js. Like GenerateClientDTS, all exports are top-level and the file imports the shared types it needs from the sibling './client.js' module. That sibling import resolves whether the consumer is importing vue.js by URL (path-mapped to the dumped dir) or by a plain relative path.

Vue's reactive primitives are imported from 'vue' — host-provided.

func MergePathsConfig added in v0.28.4

func MergePathsConfig(configPath, outDir string, stdout io.Writer) error

MergePathsConfig writes (or merges into) a jsconfig.json / tsconfig.json at configPath, adding compilerOptions.paths entries that map the runtime URL imports back to the SDK files in outDir. Existing fields (compilerOptions.target, include, exclude, custom paths entries) are preserved entry-for-entry.

File shape is identical for jsconfig and tsconfig — caller picks the filename. compilerOptions.baseUrl defaults to "." when missing (required for paths to resolve).

Exported for the same reason as WriteIfChanged: the CLI flag (--tsconfig / --jsconfig) and the in-process Dump path share the same merge logic.

func ReactJS added in v0.83.0

func ReactJS() []byte

ReactJS returns the embedded nexus-react.js bytes. Same role as VueJS for the React hooks module — pair the import with React's own runtime (the host app must resolve 'react' via importmap, CDN, or bundler entry).

func RuntimeJS

func RuntimeJS() []byte

RuntimeJS returns the embedded nexus-client.js bytes. Public so the `nexus client --out` CLI can dump the runtime to disk without re-embedding a copy in cmd/nexus. Returns the same byte slice the HTTP handler serves at <path>/client.js.

func VitePluginDTS added in v1.22.2

func VitePluginDTS() []byte

VitePluginDTS returns the embedded nexus-vite-plugin.d.ts bytes — the TypeScript declarations dumped beside nexus-vite-plugin.js so a strict vite.config.ts gets a typed default export instead of `any`.

func VitePluginJS added in v0.32.0

func VitePluginJS() []byte

VitePluginJS returns the embedded nexus-vite-plugin.js bytes — the build-time auto-select transformer that rewrites nx.query / nx.mutate calls to fetch only the fields the consumer reads. Dumped into OutDir alongside client.js / vue.js when OutDir is configured; users wire it into vite.config.ts manually.

func VueJS

func VueJS() []byte

VueJS returns the embedded nexus-vue.js bytes. Same role as RuntimeJS for the Vue 3 composables module.

func WriteIfChanged added in v0.28.4

func WriteIfChanged(path string, body []byte, stdout io.Writer) error

WriteIfChanged writes body to path only when the file is missing or its current contents differ from body. Logs "wrote" with the byte count on a real write, "unchanged" when the disk copy already matched. Skipping the no-op write preserves mtime — file watchers (vite, webpack-dev-server, JetBrains' indexer) don't re-trigger on idempotent re-runs.

Bytes-equal comparison rather than hash because the SDK files are tens of KB at most; the explicit byte-slice equality is allocation-free for the common no-change case.

Exported because the CLI (cmd/nexus/client_cmd.go) and the in-process Dump path share the same write contract — keeping one helper means a fix for either site lands everywhere.

func WriteIfMissing added in v0.28.6

func WriteIfMissing(path string, body []byte, stdout io.Writer) error

WriteIfMissing writes body to path only when the file does not already exist. The first run scaffolds it; subsequent runs observe the user's edits and skip. Distinct from WriteIfChanged (which compares bytes and rewrites on drift) — used for the nexus.ts wiring file that the developer is expected to edit.

Types

type AuthInfo

type AuthInfo struct {
	ExtractorInfo
	// TokenField, when set, names where the access token lives in a
	// login response — a dotted path like "token", "accessToken", or
	// "data.token". The SDK reads it instead of guessing common shapes
	// (see extractLoginToken in nexus-client.js). Empty by default; the
	// framework leaves it unset so apps/extensions populate it via a
	// custom client.Config.Manifest projection when their login handler
	// uses a non-standard envelope.
	TokenField string `json:"tokenField,omitempty"`
	// CSRFCookie / CSRFHeader configure the double-submit CSRF defense
	// the SDK applies to state-changing requests under cookie-based auth
	// strategies (cookie/chain/custom). Empty falls back to the SDK
	// defaults ("csrftoken" / "X-CSRFToken", the Django/Laravel
	// convention; see DefaultCSRFCookie / DefaultCSRFHeader). auth.Module
	// populates these from auth.Config (defaulted via AuthMeta.WithDefaults)
	// so a wired app carries them explicitly.
	CSRFCookie      string `json:"csrfCookie,omitempty"`
	CSRFHeader      string `json:"csrfHeader,omitempty"`
	LoginPath       string `json:"loginPath,omitempty"`
	LoginTransport  string `json:"loginTransport,omitempty"`
	LoginName       string `json:"loginName,omitempty"`
	LogoutPath      string `json:"logoutPath,omitempty"`
	LogoutTransport string `json:"logoutTransport,omitempty"`
	LogoutName      string `json:"logoutName,omitempty"`
	MePath          string `json:"mePath,omitempty"`
	MeTransport     string `json:"meTransport,omitempty"`
	MeName          string `json:"meName,omitempty"`
}

AuthInfo describes the running auth surface. The extractor shape tells the SDK where to put the access token; LoginPath / LogoutPath / MePath (plus their *Transport / *Name siblings) are discovered from endpoints marked via nexus.AuthRoute.

Transport is "rest" or "graphql" — the SDK switches between rest() and _gql() dispatch based on this. Name is the GraphQL op name when Transport == "graphql"; empty for REST. The legacy *Path fields stay populated for both transports (the GraphQL mount path) so older SDK builds continue to resolve auth flows.

type AuthMeta added in v1.18.1

type AuthMeta struct {
	TokenField string // dotted path to the login-response token ("data.token")
	CSRFCookie string // non-HttpOnly cookie the SDK reads for CSRF double-submit
	CSRFHeader string // header the SDK echoes the cookie value into
}

AuthMeta carries the auth-section hints that come from auth.Config rather than from the registry's endpoints — where the login token lives in a response, and the CSRF double-submit cookie/header names. nexus.New bridges these from auth.Module via App.SetClientAuthMeta; buildManifest derives the login/logout/me paths from endpoints while this overlay fills the config-sourced fields. Zero value = no overlay (every empty field is skipped, so a manifest with no auth module is untouched); pass through WithDefaults to materialize the framework defaults.

func (AuthMeta) Empty added in v1.18.1

func (m AuthMeta) Empty() bool

Empty reports whether m carries no hints, letting callers skip the overlay (and the cache invalidation it implies) when auth.Config set nothing.

func (AuthMeta) WithDefaults added in v1.18.1

func (m AuthMeta) WithDefaults() AuthMeta

WithDefaults returns a copy of m with every empty field filled from the Default* constants. auth.Module runs config through this before bridging, so an app that sets nothing still gets the Django-style CSRF pair and the data.token field in its manifest. Fields the app set explicitly are preserved.

type Config

type Config struct {
	// Enabled gates the entire mount. Default false.
	Enabled bool

	// DevDisabled opts OUT of the dev-only auto-mount. Under
	// `nexus dev` (NEXUS_DEV=1) the framework mounts the client SDK's
	// manifest + runtime routes automatically when the app didn't
	// enable them itself — so the SPA's vite proxy auto-syncs module
	// RoutePrefixes (read from the live /__nexus/client/manifest.json)
	// and the SDK is available without ceremony. The implicit dev
	// mount never dumps files (OutDir is forced empty) — routes only.
	// Set DevDisabled to keep the SDK closed even in dev ("closed
	// manually"). No effect in production (NEXUS_DEV is never set
	// there) or when Enabled is already true.
	DevDisabled bool

	// Path is the URL prefix the SDK routes mount under. Default
	// DefaultPath ("/__nexus/client"). The dashboard at /__nexus/*
	// and the SDK at /__nexus/client/* share an obvious namespace;
	// the runtime files (client.js, vue.js, *.d.ts) are always
	// served unauthenticated since browsers fetch them anonymously
	// before any login. The MANIFEST is gated by the Public flag.
	Path string

	// Public, when true, exposes the FULL manifest (every endpoint,
	// every schema, every named ref, every WS path) on the
	// unauthenticated /manifest.json route — the v0.28.x default.
	//
	// Default false: the public manifest is stripped to the safe
	// minimum (Version, BasePath, Auth section, auth-flagged
	// endpoints only) so an anonymous browser can still complete
	// the login flow without leaking the API surface to scrapers.
	//
	// What works on each setting (runtime, browser-side):
	//
	//	             Public: false (default)   Public: true
	//	nx.auth.*    ✓                          ✓
	//	nx.rest      ✓ (path/args caller-side)  ✓
	//	nx.ws        ✓ (path caller-side)       ✓
	//	nx.query     ✗ (op lookup needs full)   ✓
	//	nx.mutate    ✗                          ✓
	//	nx.crud      ✗                          ✓
	//
	// TypeScript completion is unaffected by this flag — types
	// come from the dumped sdk/client.d.ts (vendored at build
	// time), not the runtime manifest.
	//
	// Recommended pattern for production: leave Public false on
	// public-facing deployments; flip to true only on internal
	// admin/dev listeners (compose with nexus.IfDeployment).
	//
	// Auto-derived from nexus.Config.Introspection: when
	// Introspection is true, Public is forced true at Mount time.
	// The two flags are the same "schema visibility" lever at
	// different layers — gating one without the other was security
	// theatre because GraphQL's __schema query already exposed more
	// than the skinny manifest would. Set Introspection: true and
	// the runtime manifest follows automatically; you no longer need
	// to repeat Public: true.
	Public bool

	// Middleware applies to every SDK route. Empty by default. For
	// the manifest specifically, prefer the Public flag — Middleware
	// gates the runtime .js files too, which most apps don't want.
	Middleware []httpx.HandlerFunc

	// Manifest overrides the default per-build manifest projection.
	// Most apps leave this nil; the framework's default reads the
	// live registry. Override for multi-tenant filtering or custom
	// type-stripping. Called once on first request, then cached;
	// see Handler.Reload to invalidate.
	Manifest func() Manifest

	// OutDir, when non-empty, makes Mount also dump the SDK files
	// (client.js + client.d.ts, vue.js + vue.d.ts, manifest.json) to
	// disk on startup so a frontend's filesystem-based tooling
	// (TypeScript compiler, Vite, JetBrains, VS Code) can resolve
	// types and runtime imports without a manual `nexus client --out`
	// step. Each .js sits next to its .d.ts so TS auto-pairs them
	// regardless of whether the import uses the runtime URL or a
	// plain relative path.
	//
	// Idempotent: files are byte-compared and skipped when content
	// matches, preserving mtime — file-watcher reloads + IDE
	// re-indexing don't fire on no-op restarts.
	//
	// Recommend leaving empty in production builds. Apps that need
	// it gated to dev only can branch on an env var when building
	// the Config (this field intentionally has no built-in env
	// magic — explicit beats implicit for "writes files to your
	// project tree").
	//
	// Auto-detection: when left empty AND a frontend dir is
	// detected (web/, frontend/, client/, app/ — anything containing
	// vite.config.ts), Mount fills this with `./<dir>/sdk`. Set
	// explicitly to override or to disable the dump in non-standard
	// layouts.
	OutDir string

	// TSConfig, when non-empty, makes Mount merge path mappings
	// (the runtime URL imports → OutDir files) into the named
	// jsconfig.json or tsconfig.json on startup. Existing fields
	// (compilerOptions.target, include, exclude, custom paths)
	// survive untouched. Same idempotent contract as OutDir.
	//
	// Only takes effect when OutDir is also set — the path
	// mappings need a target.
	//
	// Auto-detection: when left empty AND a frontend dir is
	// detected, Mount fills this with `./<dir>/tsconfig.json` IF
	// that file exists. Missing tsconfig (jsconfig-only or no TS
	// config at all) keeps the field empty so the dump path
	// doesn't try to read a phantom file.
	TSConfig string

	// ViteConfig, when non-empty, makes Mount auto-attach the
	// nexus-vite-plugin (auto-select) to a Vite config on startup.
	// Two idempotent edits land in the file:
	//
	//   1. an `import nexusAutoSelect from '<rel>/nexus-vite-plugin.js'`
	//      after the last top-level import,
	//   2. a `nexusAutoSelect()` entry inside the first `plugins:`
	//      array.
	//
	// Path can be absolute or relative to the Go binary's CWD.
	// Only takes effect when OutDir is set — the plugin file lives
	// under OutDir. Re-running is a no-op once both edits have
	// landed; the framework keys idempotence off the literal
	// "nexusAutoSelect" identifier in the file.
	//
	// Recommend gating this behind a dev-mode flag in production
	// builds — there's no reason to mutate a checked-in config on
	// every prod boot.
	//
	// Auto-detection: when left empty AND a frontend dir is
	// detected (vite.config.ts present), Mount fills this with
	// `./<dir>/vite.config.ts`. Together with the OutDir +
	// TSConfig defaults, this means the canonical scaffold layout
	// only needs `client.Config{Enabled: true}`.
	ViteConfig string

	// SkipAssets, when true, suppresses the static SDK asset routes
	// (client.js, client.d.ts, vue.js, vue.d.ts). The manifest and
	// contributions routes still mount — they're the codegen surface
	// and don't depend on the runtime SDK.
	//
	// Set this for apps that consume the typed codegen tree
	// (`nexus generate frontend`) instead of importing from
	// /__nexus/client/*.js at runtime. frontend.Plugin wires it
	// automatically based on its RuntimeSDK field; direct
	// client.Mount / nexus.ClientUse callers default to false
	// (assets served), preserving back-compat.
	SkipAssets bool

	// Unguarded disables the introspection-network gate that nexus.New
	// otherwise prepends to the SDK routes. By default the client
	// surface — the manifest (a full API map when Public) and the
	// .d.ts (the full type surface) — is locked down to the same peers
	// the dashboard allows: open under `nexus dev` / Introspection,
	// 404 to everyone else in a locked-down production binary. Set
	// Unguarded only when you deliberately serve the runtime SDK to the
	// public (e.g. a public SPA that fetches the runtime manifest at
	// load time instead of vendoring sdk/ at build time). Prefer
	// build-time vendoring (`nexus client --out`) over flipping this —
	// a public manifest is an attacker's API map.
	Unguarded bool
}

Config tunes the embedded SDK auto-mount. Zero value Enabled=false so omitting Config.Client from nexus.Config is safe — apps that don't ship an SDK don't pay the embed cost.

func ApplyFrontendDefaults added in v1.12.0

func ApplyFrontendDefaults(cfg Config) Config

ApplyFrontendDefaults is the exported entry to applyFrontendDefaults, for the nexus.Config.SDK one-switch path which builds a Config outside this package and needs the same OutDir/TSConfig/ViteConfig auto-detection.

func ApplyVisibilityDefaults added in v0.37.4

func ApplyVisibilityDefaults(cfg Config, introspection bool) Config

ApplyVisibilityDefaults aligns Client.Public with the framework's top-level Introspection toggle. When introspection is open, the runtime manifest is open too — gating Public separately is security theatre because anything the skinny projection hides is already reachable through GraphQL's __schema query when introspection is on. When introspection is closed, Public's explicit value (or the default false) stands as-is.

This collapses the two-flag posture down to one in the common case: users only need to flip Introspection, and the runtime-manifest exposure follows automatically. The pathological "introspection on, manifest skinny" combination is removed; if someone genuinely needs that, they're better served by a custom route gate downstream of Mount.

type ContributionFileRec added in v0.54.0

type ContributionFileRec struct {
	Path string `json:"path"`
	Body string `json:"body"`
}

ContributionFileRec is one rendered artifact. Path is forward-slash relative to whatever OutDir the consumer picks; Body is the TS/JS source ready to land on disk.

type ContributionPluginRec added in v0.54.0

type ContributionPluginRec struct {
	Name  string                `json:"name"`
	Files []ContributionFileRec `json:"files,omitempty"`
}

ContributionPluginRec groups one plugin's files under its name. Empty Files entries get omitted from the wire — a plugin that emits nothing for the requested framework leaves no trace.

type ContributionsBuilder added in v0.54.0

type ContributionsBuilder func(framework string) (ContributionsResponse, error)

ContributionsBuilder is the closure Mount accepts to make contributions HTTP-reachable. Takes the framework requested via the ?framework= query parameter and returns the merged response. Returning an error produces a 500 with the error text — the CLI treats this as a hard failure (a misbehaving contributor blocks codegen; better to fail loudly than ship a broken tree).

nil signals "no contributions route" — Mount skips registration entirely. Legacy callers (nexus.ClientUse, the old Config.Client auto-mount) pass nil so the route is opt-in.

type ContributionsResponse added in v0.54.0

type ContributionsResponse struct {
	Version   string                  `json:"version"`
	Framework string                  `json:"framework,omitempty"`
	Plugins   []ContributionPluginRec `json:"plugins,omitempty"`
}

ContributionsResponse is the wire format served at GET <path>/contributions.json. The CLI's frontend codegen merges these files into the renderer's output tree alongside the per-op typed exports — phase 3's escape hatch for plugin-specific TS (auth's useAuth composable, oauth2's flow helpers) that the transport-neutral renderer doesn't know how to emit.

Body is plain TS source as a JSON string. Binary contributions aren't supported in v1 — TS/JS contributors are the entire audience and base64 padding would just bloat the wire.

type EndpointInfo

type EndpointInfo struct {
	Service     string            `json:"service"`
	Module      string            `json:"module,omitempty"`
	Transport   string            `json:"transport"` // "rest" | "graphql" | "websocket"
	Method      string            `json:"method"`
	Path        string            `json:"path"`
	Name        string            `json:"name"`
	Description string            `json:"description,omitempty"`
	Args        *registry.TypeRef `json:"args,omitempty"`
	Return      *registry.TypeRef `json:"return,omitempty"`

	// AuthRequired is true when the endpoint is gated by auth.Required
	// (or any middleware whose name starts with "auth:" — covers
	// Required + Requires).
	AuthRequired bool `json:"authRequired,omitempty"`

	// RequiresPerm is the list of permission strings auth.Requires
	// declared. Empty for unauthed endpoints or auth.Required without
	// specific perms. Derived from middleware names of the form
	// "auth:requires:perm-a,perm-b".
	RequiresPerm []string `json:"requiresPerm,omitempty"`

	// AuthFlow is "login" | "logout" | "me" when the endpoint was
	// marked via nexus.AuthRoute. Empty for normal endpoints.
	AuthFlow string `json:"authFlow,omitempty"`

	Deprecated        bool   `json:"deprecated,omitempty"`
	DeprecationReason string `json:"deprecationReason,omitempty"`
}

EndpointInfo is one callable surface — a REST route, a GraphQL op, or a WebSocket message handler.

type ExtractorInfo

type ExtractorInfo struct {
	Strategy   string          `json:"strategy"`
	HeaderName string          `json:"headerName,omitempty"`
	CookieName string          `json:"cookieName,omitempty"`
	Chain      []ExtractorInfo `json:"chain,omitempty"`
}

ExtractorInfo describes how the running auth module pulls tokens off requests. Mirrors auth.ExtractorInfo (the canonical definition) — duplicated here so the SDK manifest stays auth-package-independent and avoids the nexus → client → auth import cycle. nexus.New is responsible for translating from auth.ExtractorInfo via the AuthInfo callback on client.Config.

Strategy values match auth.ExtractorInfo.Strategy:

"bearer"  HeaderName populated; SDK sends "Authorization: Bearer"
"cookie"  CookieName populated; SDK sets credentials: 'include'
"apikey"  HeaderName populated; SDK sends a bare header value
"chain"   Chain populated with the underlying strategies
"custom"  user-supplied extractor; SDK falls back to credentials

type Handler

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

Handler holds the live state of a mounted SDK surface — the cached manifest + .d.ts strings and a sync.Once that gates the first build. Returned by Mount so tests (and apps that need to invalidate the cache) can call Reload().

func Mount

func Mount(e httpx.Router, reg *registry.Registry, authInfo func() ExtractorInfo, schemaRefs func() map[string]registry.NamedType, basePath string, cfg Config) *Handler

Mount installs the SDK routes on engine. Idempotent: re-mounting rebuilds the cached manifest by invalidating the Once. Called automatically by nexus.New when Config.Client.Enabled.

authInfo may be nil — apps without auth.Module wired pass nil and the manifest's Auth section is omitted. When non-nil, the callback returns the active extractor strategy + parameters; nexus.New builds it from *auth.Manager.Info() and supplies it here.

schemaRefs is the live pool of named-struct types walked at endpoint registration; the manifest's Refs section reads from it. basePath is the deployment-wide route prefix (app.routePrefix), stamped onto Manifest.BasePath so the SDK prepends it to every call.

When cfg.OutDir is set, Mount also dumps the SDK files to disk after registering routes so frontend tooling (TS compiler, IDE) can resolve types/imports without a manual `nexus client --out` step. Dump errors are logged but don't fail Mount — dev-tool convenience shouldn't crash boot.

func MountWithContributions added in v0.54.0

func MountWithContributions(e httpx.Router, reg *registry.Registry, authInfo func() ExtractorInfo, schemaRefs func() map[string]registry.NamedType, basePath string, cfg Config, contributions ContributionsBuilder) *Handler

MountWithContributions is Mount + an optional contributions builder. When build is non-nil, an additional GET <path>/contributions.json route is registered so the CLI's frontend codegen can fetch plugin contributions alongside the manifest. nil reproduces the legacy Mount behavior (manifest + static assets only).

Kept as a sibling rather than baked into Mount's signature so existing callers (nexus.ClientUse, test fixtures) don't churn. frontend.Plugin is the canonical caller of the with-contributions form; it's the only place that has the *App pointer needed to build the closure.

func (*Handler) AutoDumpConfig added in v0.54.0

func (h *Handler) AutoDumpConfig() (outdir, tsconfig, viteconfig string)

AutoDumpConfig returns the OutDir / TSConfig / ViteConfig the handler was mounted with — the three knobs that drive the SDK auto-dump fired from nexus.New's OnStart hook. Returning a tuple instead of exposing the Config wholesale keeps the handler's other fields encapsulated (cfg.Public, cfg.Middleware, etc. belong to the wire layer, not the auto-dump path).

Empty outdir signals "no dump configured" — the caller (the fx.OnStart hook in integration.go) skips the dump entirely.

func (*Handler) Dump added in v0.28.4

func (h *Handler) Dump(outDir, tsconfig, viteConfig string, stdout io.Writer) error

Dump writes the embedded SDK runtime + the live manifest + generated .d.ts to outDir. Optionally merges path mappings into tsconfig (or jsconfig — same shape) at the given path so an IDE resolves the runtime URL imports back to the dumped files.

Idempotent: WriteIfChanged compares bytes before writing, so re-running against an already-up-to-date target preserves the existing files' mtime — no file-watch / IDE-reindex churn on no-op runs.

outDir is created if missing. tsconfig is optional; pass "" to skip the IDE-config step. Errors short-circuit; partial writes (some files written, then a failure) are possible but rare — the helper writes small files in a fixed order and the filesystem operations themselves rarely fail mid-batch.

stdout receives one line per file ("wrote …" / "unchanged …"). Pass io.Discard to silence.

func (*Handler) Manifest

func (h *Handler) Manifest() Manifest

Manifest returns the projected SDK manifest, building it from the registry on first call and caching the result. Exposed so tests can introspect the manifest without going through the HTTP layer.

When cfg.Public is false (the default), the result is the FULL manifest — the same shape used internally for code generation and for the public route when Public is true. Tests + the .d.ts generator always see the full shape so type output is identical regardless of the runtime gating.

publicManifest() is the projection used for the unauthenticated HTTP route only.

func (*Handler) Reload

func (h *Handler) Reload()

Reload drops the cached manifest + .d.ts so the next request rebuilds. Used by tests and by apps that register endpoints via late mount paths. Production callers usually don't need it — the registry stops mutating after fx.Start, so the first build is the only build.

func (*Handler) SetAuthInfo

func (h *Handler) SetAuthInfo(fn func() ExtractorInfo)

SetAuthInfo installs (or replaces) the auth-info provider used to populate the manifest's Auth section on next build. Wired by auth.Module's option chain when both the SDK and auth are in the same app — separates the cycle (client/ doesn't import auth/, auth/ doesn't import client/, the bridge lives in nexus/).

Calls Reload internally so the cached manifest rebuilds with the new auth shape on the next request.

func (*Handler) SetAuthMeta added in v1.18.1

func (h *Handler) SetAuthMeta(meta AuthMeta)

SetAuthMeta installs the auth-config hints (login-token location + CSRF names) overlaid onto the manifest's Auth section on next build. Wired by auth.Module alongside SetAuthInfo. Calls Reload semantics internally so the cached manifest rebuilds with the new hints.

type Manifest

type Manifest struct {
	Version   string                        `json:"version"`
	BasePath  string                        `json:"basePath"`
	Services  []ServiceInfo                 `json:"services,omitempty"`
	Endpoints []EndpointInfo                `json:"endpoints,omitempty"`
	Resources []ResourceInfo                `json:"resources,omitempty"`
	WS        []WSPathInfo                  `json:"ws,omitempty"`
	Auth      *AuthInfo                     `json:"auth,omitempty"`
	Refs      map[string]registry.NamedType `json:"refs,omitempty"`

	// Projected marks the stripped (non-Public) manifest served to
	// anonymous browsers — auth flows only, no schemas, no GraphQL/CRUD
	// ops. The runtime SDK reads it to turn the otherwise-cryptic
	// "no op named X" miss into a "the server is serving the stripped
	// manifest" hint. Omitted (false) on the full manifest.
	Projected bool `json:"projected,omitempty"`
}

Manifest is the SDK-tailored shape served at GET <path>/manifest.json. Distinct from manifest.Manifest (the deploy-time, admin-token-gated document): public, type-rich, and scoped to what an SDK consumer needs to call into the app.

type ResourceInfo

type ResourceInfo struct {
	Name        string `json:"name"`
	Kind        string `json:"kind"`
	Description string `json:"description,omitempty"`
}

ResourceInfo is the SDK view of a resource — light enough to drive a "where does this go?" picker in a UI without leaking orchestration details.

type ServiceInfo

type ServiceInfo struct {
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
}

ServiceInfo is the SDK-friendly view of a service. Drops topology fields (ResourceDeps, ServiceDeps, Remote) the SDK doesn't need.

type WSMessage

type WSMessage struct {
	Type        string            `json:"type"`
	Args        *registry.TypeRef `json:"args,omitempty"`
	Description string            `json:"description,omitempty"`
}

WSMessage is one (msgType → args) entry on a WS path.

type WSPathInfo

type WSPathInfo struct {
	Path     string      `json:"path"`
	Service  string      `json:"service"`
	Messages []WSMessage `json:"messages"`
}

WSPathInfo groups every typed AsWS message under one WebSocket path so the SDK can build a single connection handle per path with a typed dispatch table.

Jump to

Keyboard shortcuts

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