kit

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Nov 9, 2026 License: MIT Imports: 32 Imported by: 0

Documentation

Overview

Package kit is the server runtime for Tern applications.

A route directory's Go package defines what the route does on the server, by exporting:

func Load(ctx *kit.Ctx) (T, error)        // data for +page.svelte
func LayoutLoad(ctx *kit.Ctx) (T, error)  // data for +layout.svelte
var Actions = kit.Actions{...}            // form actions for the page
func GET(ctx *kit.Ctx) error              // endpoint handlers: GET, POST, …

T must encode to a JSON object: a struct or a map. Its JSON is passed to the Svelte component as data, with TypeScript types generated from T.

Slow data can be streamed: put a Deferred in the data, created with Defer, and render it with {#await} in Svelte.

Errors control the response. Return Error or NotFound for an expected error page, Redirect to redirect, and Fail from an action to report a validation error. Any other error is logged and shown as "Internal Error".

`tern gen` writes tern.gen.go, which wires every route into a Manifest and defines ternApp. A typical main.go:

func main() {
	app, err := ternApp(kit.WithHook(auth))
	if err != nil {
		log.Fatal(err)
	}
	if err := app.ListenAndServe(); err != nil {
		log.Fatal(err)
	}
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Error

func Error(status int, message string) error

Error returns an expected error with the given status and a message that is safe to show to users. Return it from Load, an action or an endpoint to render the nearest +error.svelte.

Any other error returned from your code is treated as unexpected: it is logged, and users see only "Internal Error".

func Fail

func Fail(status int, data any) error

Fail returns an action failure with a 4xx status. The page re-renders with data available as the `form` prop, so it can show errors next to fields and keep the user's input.

func NotFound

func NotFound() error

NotFound returns a 404 error.

func Parent

func Parent[T any](c *Ctx) (T, error)

Parent waits for the layouts above the current load, merges their data, and decodes it into T. Use it in Load or LayoutLoad to build on data a parent layout loaded:

type Parent struct{ User User `json:"user"` }
p, err := kit.Parent[Parent](ctx)

Loads that do not call Parent run fully in parallel.

func Redirect

func Redirect(status int, location string) error

Redirect returns an error that redirects to location with a 3xx status. Use 303 after a form submission, 307 or 308 to keep the request method, and 301 or 302 for permanent and temporary moves.

Types

type ActionFunc

type ActionFunc func(*Ctx) (any, error)

ActionFunc handles a form submission. Its result is sent to the page as the `form` prop. Use Action to write actions with a typed result, so the generated `form` type is precise.

func Action

func Action[T any](f func(*Ctx) (T, error)) ActionFunc

Action adapts a typed action function:

var Actions = kit.Actions{
	"default": kit.Action(create),
}

func create(ctx *kit.Ctx) (Created, error) { ... }

type Actions

type Actions map[string]ActionFunc

Actions maps action names to handlers. A form posting to the page with no ?/name runs "default"; <form action="?/create"> runs "create".

type App

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

App serves a Tern application. It implements http.Handler, so it can be used with any server or router; App.ListenAndServe runs it with production-ready defaults.

func New

func New(m *Manifest, opts ...Option) (*App, error)

New creates an App from a generated manifest. Generated code calls it for you; see the ternApp function in tern.gen.go.

func (*App) Close

func (a *App) Close() error

Close releases the app's resources, such as server-rendering VMs. ListenAndServe and Serve call it on shutdown.

func (*App) ListenAndServe

func (a *App) ListenAndServe() error

ListenAndServe runs the app until it receives SIGINT or SIGTERM, then shuts down gracefully, letting in-flight requests finish for up to 10 seconds.

It listens on the address set with WithAddr, or HOST:PORT from the environment, defaulting to port 3000 on all interfaces.

func (*App) Serve

func (a *App) Serve(ctx context.Context) error

Serve runs the app until ctx is canceled, then shuts down gracefully.

func (*App) ServeHTTP

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

ServeHTTP implements http.Handler.

type Ctx

type Ctx struct {
	// Request is the incoming request.
	Request *http.Request
	// Locals holds request-scoped values, typically set by a hook (for
	// example the signed-in user) and read by loads and actions.
	Locals map[string]any
	// contains filtered or unexported fields
}

Ctx is the request context passed to Load, LayoutLoad, actions, endpoints and hooks. It implements context.Context, so it can be passed directly to database calls and other APIs that take a context.

func (*Ctx) Cookie

func (c *Ctx) Cookie(name string) (string, bool)

Cookie returns the value of a request cookie.

func (*Ctx) Deadline

func (c *Ctx) Deadline() (time.Time, bool)

Deadline implements context.Context.

func (*Ctx) DeleteCookie

func (c *Ctx) DeleteCookie(name string)

DeleteCookie removes a cookie set with the default path "/".

func (*Ctx) Depends added in v0.4.0

func (c *Ctx) Depends(keys ...string)

Depends declares that the running load depends on keys, such as "app:projects" or a URL it fetches. Calling invalidate("app:projects") in the browser reruns every load that depends on it.

On client navigation, a layout's load only runs again when something it used changed: a parameter read with Param, the URL read with URL, the data of a parent it read with Parent, or an invalidated dependency. Reading Request directly is not tracked; use these methods instead.

func (*Ctx) Done

func (c *Ctx) Done() <-chan struct{}

Done implements context.Context.

func (*Ctx) Err

func (c *Ctx) Err() error

Err implements context.Context.

func (*Ctx) Form

func (c *Ctx) Form() (url.Values, error)

Form parses and returns the submitted form, including multipart forms.

func (*Ctx) Header

func (c *Ctx) Header(name string) string

Header returns a request header value.

func (*Ctx) JSON

func (c *Ctx) JSON(status int, v any) error

JSON writes v as a JSON response. It is meant for endpoints.

func (*Ctx) Param

func (c *Ctx) Param(name string) string

Param returns the value of a route parameter, or "" if it is absent. A load that reads a parameter runs again when the parameter changes.

func (*Ctx) Params

func (c *Ctx) Params() map[string]string

Params returns all route parameters. The map must not be modified.

func (*Ctx) RouteID

func (c *Ctx) RouteID() string

RouteID returns the matched route's ID, e.g. "/blog/slug.param".

func (*Ctx) SetCookie

func (c *Ctx) SetCookie(ck *http.Cookie)

SetCookie adds a Set-Cookie header. Unset fields get safe defaults: Path "/", SameSite=Lax, and Secure unless the request is plain HTTP to localhost. Set HttpOnly yourself for cookies scripts must not read, such as session cookies. Loads may call it concurrently.

func (*Ctx) SetHeader

func (c *Ctx) SetHeader(name, value string)

SetHeader sets a response header. Loads may call it concurrently.

func (*Ctx) Text

func (c *Ctx) Text(status int, s string) error

Text writes a plain-text response. It is meant for endpoints.

func (*Ctx) URL

func (c *Ctx) URL() *url.URL

URL returns the request URL. A load that reads it runs again on every client navigation that changes the URL.

func (*Ctx) Value

func (c *Ctx) Value(key any) any

Value implements context.Context.

func (*Ctx) Writer

func (c *Ctx) Writer() http.ResponseWriter

Writer returns the response writer for endpoints, with headers and cookies set through the Ctx already applied. Do not use it in loads.

type Deferred

type Deferred[T any] struct {
	// contains filtered or unexported fields
}

Deferred is a value that a load starts computing but does not wait for. The page renders as soon as the rest of the data is ready, and the deferred value streams to the browser when it resolves. In Svelte it is a Promise:

type Data struct {
	Post     Post                     `json:"post"`
	Comments kit.Deferred[[]Comment] `json:"comments"`
}

{#await data.comments}Loading…{:then comments}…{:catch error}…{/await}

Create one with Defer.

func Defer

func Defer[T any](ctx *Ctx, fn func() (T, error)) Deferred[T]

Defer runs fn in the background and returns its result as a Deferred, so slow data does not hold up the page. fn should respect ctx for cancellation; it runs only for the lifetime of the request.

func (Deferred[T]) MarshalJSON

func (x Deferred[T]) MarshalJSON() ([]byte, error)

MarshalJSON encodes a placeholder that the client replaces with a Promise.

func (*Deferred[T]) UnmarshalJSON

func (x *Deferred[T]) UnmarshalJSON([]byte) error

UnmarshalJSON ignores its input: deferred values are not available to kit.Parent, only to the browser.

type FailError

type FailError struct {
	Status int
	Data   any
}

FailError is returned by an action to report a failed submission, typically a validation error. Its Data is sent to the page as `form`.

func (*FailError) Error

func (e *FailError) Error() string

type FallbackDef

type FallbackDef struct {
	// Layouts are indices into Manifest.Layouts: the root layout, if any.
	Layouts []int
}

FallbackDef renders 404s for URLs that match no route.

type HTTPError

type HTTPError struct {
	Status  int
	Message string
}

HTTPError is an expected error with an HTTP status, such as a 404. Its message is shown to the user, so it must not contain secrets.

func (*HTTPError) Error

func (e *HTTPError) Error() string

type HandlerFunc

type HandlerFunc func(*Ctx) error

HandlerFunc handles an endpoint request, such as an exported GET.

type HookFunc

type HookFunc func(*Ctx) error

HookFunc runs before every page, data, action and endpoint request. It can read the request, set Locals, and return an error such as kit.Redirect to stop the request.

type LayoutDef

type LayoutDef struct {
	Dir string
	// Component is the +layout.svelte path relative to the project root,
	// or "" if the directory only has a LayoutLoad.
	Component string
	Load      LoadFunc
}

LayoutDef is a layout directory.

type LoadFunc

type LoadFunc func(*Ctx) (any, error)

LoadFunc is a Load or LayoutLoad function with its result type erased. Generated code builds it with LoadOf.

func LoadOf

func LoadOf[T any](f func(*Ctx) (T, error)) LoadFunc

LoadOf adapts a typed load function.

type Manifest

type Manifest struct {
	// Layouts are shared by routes and referenced by index.
	Layouts []LayoutDef
	// Routes, in matching priority order.
	Routes []RouteDef
	// Fallback describes what renders when no route matches.
	Fallback FallbackDef
	// AppHTML is the contents of src/app.html.
	AppHTML string
	// Client is the built client: Vite's output directory, including
	// .vite/manifest.json. It may be nil in development.
	Client fs.FS
	// ClientEntry is the Vite manifest key of the client entry module.
	ClientEntry string
	// ServerBundle is the server renderer built by `vite build --ssr`, or
	// "" to render on the client only.
	ServerBundle string
}

Manifest describes an app's routes and assets. It is produced by `tern gen` in tern.gen.go; you do not write it by hand.

type Option

type Option func(*options)

Option configures an App.

func WithAddr

func WithAddr(addr string) Option

WithAddr sets the address ListenAndServe listens on. The default is HOST:PORT from the environment, or ":3000".

func WithBodyLimit

func WithBodyLimit(n int64) Option

WithBodyLimit sets the maximum request body size for actions and endpoints, in bytes. The default is 1 MiB.

func WithContentSecurityPolicy added in v0.4.0

func WithContentSecurityPolicy(policy string) Option

WithContentSecurityPolicy sends a Content-Security-Policy header on every page. Tern adds a per-request nonce to script-src (creating the directive if needed) and puts it on its own scripts, so a strict policy such as "default-src 'self'" works without 'unsafe-inline'. Use %tern.nonce% in src/app.html for your own inline scripts.

func WithContentSecurityPolicyReportOnly added in v0.4.0

func WithContentSecurityPolicyReportOnly(policy string) Option

WithContentSecurityPolicyReportOnly is like WithContentSecurityPolicy but sends Content-Security-Policy-Report-Only, to test a policy without enforcing it.

func WithHook

func WithHook(h HookFunc) Option

WithHook adds a hook. Hooks run in the order they are added.

func WithLogger

func WithLogger(l *slog.Logger) Option

WithLogger sets the logger for unexpected errors and server events. The default logs to slog.Default().

func WithMatcher

func WithMatcher(name string, match func(value string) bool) Option

WithMatcher registers a parameter matcher used by directories such as id.param.integer. The built-in matchers "integer" and "uuid" are always available.

func WithMiddleware

func WithMiddleware(mw ...func(http.Handler) http.Handler) Option

WithMiddleware wraps the app in standard net/http middleware. The first middleware added is the outermost.

func WithSSRTimeout

func WithSSRTimeout(d time.Duration) Option

WithSSRTimeout bounds how long a server render may take before the page falls back to client rendering. The default is 5 seconds.

func WithSSRWorkers

func WithSSRWorkers(n int) Option

WithSSRWorkers sets how many pages can render on the server at once. Each worker is a JavaScript VM using roughly 5 to 20 MB of memory. The default is GOMAXPROCS.

func WithTrustedOrigins

func WithTrustedOrigins(origins ...string) Option

WithTrustedOrigins allows cross-origin form submissions and endpoint requests from the given origins, such as "https://example.com".

func WithoutCSRFProtection

func WithoutCSRFProtection() Option

WithoutCSRFProtection disables cross-origin request protection. Only use it if something in front of the app already provides it.

func WithoutSSR

func WithoutSSR() Option

WithoutSSR renders every page on the client only. Use `const SSR = false` in a route's package to turn it off for one page instead.

type PageDef

type PageDef struct {
	// Component is the +page.svelte path relative to the project root.
	Component string
	Load      LoadFunc
	Actions   Actions
	// NoSSR disables server-side rendering for this page; set by
	// `const SSR = false` in the route's package.
	NoSSR bool
}

PageDef is a route's page.

type RedirectError

type RedirectError struct {
	Status   int
	Location string
}

RedirectError tells Tern to redirect the request.

func (*RedirectError) Error

func (e *RedirectError) Error() string

type RouteDef

type RouteDef struct {
	// ID is the route ID, e.g. "/blog/slug.param".
	ID string
	// Layouts are indices into Manifest.Layouts, outermost first.
	Layouts []int
	// Page is nil for endpoint-only routes.
	Page *PageDef
	// Endpoint maps HTTP methods to handlers.
	Endpoint map[string]HandlerFunc
	// Prerender renders the page to static HTML at build time; set by
	// `const Prerender = true` in the route's package.
	Prerender bool
	// Entries lists the parameters to prerender a dynamic route with; set
	// by an exported Entries function in the route's package.
	Entries func() ([]map[string]string, error)
}

RouteDef is a page, an endpoint, or both.

Jump to

Keyboard shortcuts

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