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 ¶
- func Error(status int, message string) error
- func Fail(status int, data any) error
- func NotFound() error
- func Parent[T any](c *Ctx) (T, error)
- func Redirect(status int, location string) error
- type ActionFunc
- type Actions
- type App
- type Ctx
- func (c *Ctx) BasePath() string
- func (c *Ctx) Cookie(name string) (string, bool)
- func (c *Ctx) Deadline() (time.Time, bool)
- func (c *Ctx) DeleteCookie(name string)
- func (c *Ctx) Depends(keys ...string)
- func (c *Ctx) Done() <-chan struct{}
- func (c *Ctx) Err() error
- func (c *Ctx) Form() (url.Values, error)
- func (c *Ctx) Header(name string) string
- func (c *Ctx) JSON(status int, v any) error
- func (c *Ctx) Locale() string
- func (c *Ctx) Param(name string) string
- func (c *Ctx) Params() map[string]string
- func (c *Ctx) Path(p string) string
- func (c *Ctx) RouteID() string
- func (c *Ctx) SetCookie(ck *http.Cookie)
- func (c *Ctx) SetHeader(name, value string)
- func (c *Ctx) SetLocale(locale string)
- func (c *Ctx) T(key string, params ...map[string]any) string
- func (c *Ctx) Text(status int, s string) error
- func (c *Ctx) URL() *url.URL
- func (c *Ctx) Value(key any) any
- func (c *Ctx) Writer() http.ResponseWriter
- type Deferred
- type FailError
- type FallbackDef
- type HTTPError
- type HandlerFunc
- type HookFunc
- type LayoutDef
- type LoadFunc
- type Manifest
- type Option
- func WithAddr(addr string) Option
- func WithBodyLimit(n int64) Option
- func WithContentSecurityPolicy(policy string) Option
- func WithContentSecurityPolicyReportOnly(policy string) Option
- func WithHook(h HookFunc) Option
- func WithLogger(l *slog.Logger) Option
- func WithMatcher(name string, match func(value string) bool) Option
- func WithMiddleware(mw ...func(http.Handler) http.Handler) Option
- func WithPlugins(plugins ...Plugin) Option
- func WithSSRTimeout(d time.Duration) Option
- func WithSSRWorkers(n int) Option
- func WithTrustedOrigins(origins ...string) Option
- func WithoutCSRFProtection() Option
- func WithoutSSR() Option
- type PageDef
- type Plugin
- type RedirectError
- type RouteDef
- type Setup
- func (s *Setup) Handle(pattern string, h http.Handler)
- func (s *Setup) Head(html string)
- func (s *Setup) HeadFunc(fn func(c *Ctx, nonce string) string)
- func (s *Setup) Hook(h HookFunc)
- func (s *Setup) Logger() *slog.Logger
- func (s *Setup) Matcher(name string, match func(value string) bool)
- func (s *Setup) Middleware(mw func(http.Handler) http.Handler)
- func (s *Setup) Name() string
- func (s *Setup) OnShutdown(fn func(ctx context.Context) error)
- func (s *Setup) TrustOrigin(origin string)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func 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 ¶
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 Parent ¶
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.
Types ¶
type ActionFunc ¶
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.
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 ¶
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 ¶
Close releases the app's resources, such as server-rendering VMs. ListenAndServe and Serve call it on shutdown.
func (*App) ListenAndServe ¶
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.
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) BasePath ¶ added in v1.0.0
BasePath returns the app's base path from tern.json, such as "/app", or "".
func (*Ctx) DeleteCookie ¶
DeleteCookie removes a cookie set with the default path "/".
func (*Ctx) Depends ¶ added in v0.4.0
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) Locale ¶ added in v1.1.0
Locale returns the request's locale, such as "de", or "" if the app has no src/locales. Tern picks it from a "lang" route parameter, the locale cookie (set by setLocale in the browser), Accept-Language, and finally the default locale. A load that reads it reruns when it changes.
func (*Ctx) Param ¶
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) Path ¶ added in v1.0.0
Path prefixes an app path such as "/projects/1" with the base path, for links built in Go. kit.Redirect adds the base path itself.
func (*Ctx) SetCookie ¶
SetCookie adds a Set-Cookie header; setting the same cookie again in a request replaces it. 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) SetLocale ¶ added in v1.1.0
SetLocale overrides the request's locale, for example from a user's saved preference in a hook. Unsupported locales are ignored.
func (*Ctx) T ¶ added in v1.1.0
T translates key into the request's locale, for text produced on the server such as validation errors. params fill placeholders like {name} and plurals; see the guide for the message syntax.
func (*Ctx) URL ¶
URL returns the request URL. A load that reads it runs again on every client navigation that changes the URL.
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 ¶
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 ¶
MarshalJSON encodes a placeholder that the client replaces with a Promise.
func (*Deferred[T]) UnmarshalJSON ¶
UnmarshalJSON ignores its input: deferred values are not available to kit.Parent, only to the browser.
type FailError ¶
FailError is returned by an action to report a failed submission, typically a validation error. Its Data is sent to the page as `form`.
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 ¶
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.
type HandlerFunc ¶
HandlerFunc handles an endpoint request, such as an exported GET.
type HookFunc ¶
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 ¶
LoadFunc is a Load or LayoutLoad function with its result type erased. Generated code builds it with LoadOf.
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
// BasePath is the URL prefix the app is served under, from tern.json,
// such as "/app"; "" serves it at the root.
BasePath string
// Locales holds src/locales/<locale>.json, or nil without i18n.
Locales fs.FS
// DefaultLocale is the fallback locale; "" picks "en" or the first.
DefaultLocale 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.
Manifest and the *Def types are the interface between generated code and the runtime. They are not covered by Tern's compatibility promise and may change in minor releases: `go get -tool` keeps the CLI that generates them and the kit package that reads them at the same version.
type Option ¶
type Option func(*options)
Option configures an App.
func WithAddr ¶
WithAddr sets the address ListenAndServe listens on. The default is HOST:PORT from the environment, or ":3000".
func WithBodyLimit ¶
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
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
WithContentSecurityPolicyReportOnly is like WithContentSecurityPolicy but sends Content-Security-Policy-Report-Only, to test a policy without enforcing it.
func WithLogger ¶
WithLogger sets the logger for unexpected errors and server events. The default logs to slog.Default().
func WithMatcher ¶
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 ¶
WithMiddleware wraps the app in standard net/http middleware. The first middleware added is the outermost.
func WithPlugins ¶ added in v1.0.0
WithPlugins adds plugins. They are set up in order, after the other options, so a plugin's hooks and middleware run after those added directly with WithHook and WithMiddleware.
func WithSSRTimeout ¶
WithSSRTimeout bounds how long a server render may take before the page falls back to client rendering. The default is 5 seconds.
func WithSSRWorkers ¶
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 ¶
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 Plugin ¶ added in v1.0.0
type Plugin interface {
// Name identifies the plugin in logs and errors.
Name() string
// Setup registers the plugin's behavior. It runs once, when the App is
// created; an error stops the App from starting.
Setup(s *Setup) error
}
Plugin extends an App with reusable behavior: hooks, middleware, matchers, extra HTTP handlers, tags in every page's <head>, and work to do on shutdown. Add plugins with WithPlugins.
type Plugin struct{}
func (Plugin) Name() string { return "hello" }
func (Plugin) Setup(s *kit.Setup) error {
s.Handle("GET /hello", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "hello")
}))
return nil
}
type RedirectError ¶
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.
type Setup ¶ added in v1.0.0
type Setup struct {
// contains filtered or unexported fields
}
Setup is how a plugin registers its behavior.
func (*Setup) Handle ¶ added in v1.0.0
Handle serves pattern with h, before Tern's routes. Patterns use net/http.ServeMux syntax, such as "GET /healthz" or "/metrics".
func (*Setup) Head ¶ added in v1.0.0
Head adds HTML to the <head> of every page, such as a <meta> tag or an analytics script. Scripts should use the per-request nonce; see Setup.HeadFunc.
func (*Setup) HeadFunc ¶ added in v1.0.0
HeadFunc adds HTML computed per request to the <head> of every page. nonce is the request's Content-Security-Policy nonce, for inline scripts.
func (*Setup) Hook ¶ added in v1.0.0
Hook adds a hook that runs before every page, data, action and endpoint request.
func (*Setup) Middleware ¶ added in v1.0.0
Middleware wraps the App in net/http middleware.
func (*Setup) OnShutdown ¶ added in v1.0.0
OnShutdown registers work to do when the App shuts down, such as flushing buffers. Functions run in reverse order of registration.
func (*Setup) TrustOrigin ¶ added in v1.1.0
TrustOrigin allows cross-origin form posts and endpoint requests from origin, as WithTrustedOrigins does. CORS plugins use it.