Skip to content

Server functions

wybthon.server_functions

server_functions

Server functions: Python that runs on the server, called from the browser like any async def.

Decorate a module-level async def with server_function, and calling it from a component sends its arguments to the server, runs it there, and returns its result, as Solid 2.0's "use server" does:

from dataclasses import dataclass

from wybthon import server_function


@dataclass
class Todo:
    id: int
    title: str


@server_function
async def list_todos(done: bool = False) -> list[Todo]:
    from app.server.db import database  # never imported in the browser

    return [Todo(*row) for row in database().execute("select id, title from todos where done = ?", [done])]
  • On the server (while a page renders, or in another server function), the call runs the function directly.
  • In the browser, the build has removed the function's body and every import only that body used, so neither reaches the page. The call posts its arguments to {base}_wyb/fn/{module}:{name} and awaits the answer. Arguments and results cross in Wybthon's wire format (wybthon._wire): JSON plus dataclasses, enums, tuples, sets, dates, Decimal, UUID, and bytes. On the server, every argument is rebuilt and validated from the function's own annotations, so a client can't send anything the function didn't declare; a mismatch is a 400.
  • Errors. An exception the function raises arrives in the browser as ServerError with the exception's type name. Its message is sent in development, and in production only for a ServerError the function raised itself, so internals don't leak.
  • Responses. Raise or return redirect, reload, or respond to navigate, revalidate queries, or set the status and headers.
  • Mutations revalidate. A POST call (the default) is treated as a mutation: when it completes, the browser revalidates every query on the page, unless the function named what it changed with respond(value, revalidate=[...]). @server_function(method="GET") declares a read, which revalidates nothing; a server function wrapped in query is a GET automatically.
  • The request. get_request_event returns the request the call came in with, so a server function can read cookies and headers and call set_cookie or http_header.

Server functions need a server: a project with server = true in its wybthon.toml, served by wyb dev, wyb serve, or wybthon.asgi under any ASGI server.

Classes:

Name Description
ServerResponse

Base of the responses a server function may raise or return.

Redirect

Navigate to url (see redirect).

Reload

Revalidate queries without navigating (see reload).

Respond

A result with a status, headers, or revalidation keys (see respond).

ServerFunction

A function that runs on the server; returned by server_function.

Functions:

Name Description
redirect

A redirect, for a server function, query, or form action to raise or return.

reload

Revalidate queries on the current page without navigating.

respond

Return value with a status, headers, or the query keys this call changed.

set_transport

Send browser-side server function calls through transport (tests and tools).

server_function

Make an async def run on the server, wherever it's called from.

Attributes:

Name Type Description
FN_PATH

Where server functions are served, under the app's base path.

FN_PATH module-attribute

FN_PATH = '_wyb/fn/'

Where server functions are served, under the app's base path.

ServerResponse

ServerResponse()

Bases: Exception

Base of the responses a server function may raise or return.

Redirect

Redirect(url: str, status: int, revalidate: list[str] | None, headers: dict[str, str] | None)

Bases: ServerResponse

Navigate to url (see redirect).

Reload

Reload(revalidate: list[str] | None, headers: dict[str, str] | None)

Bases: ServerResponse

Revalidate queries without navigating (see reload).

Respond

Respond(value: Any, status: int, revalidate: list[str] | None, headers: dict[str, str] | None)

Bases: ServerResponse

A result with a status, headers, or revalidation keys (see respond).

ServerFunction

ServerFunction(fn: Callable[P, Awaitable[R]], method: Method)

A function that runs on the server; returned by server_function.

Call it like the function it wraps: await save(todo).

Attributes:

Name Type Description
id

The function's id, module:qualname.

method Method

"POST" (a mutation, the default) or "GET" (a read).

url property

url: str

The URL the browser calls, under the app's base path.

redirect

redirect(url: str, status: int = 302, *, revalidate: str | list[str] | None = None, headers: dict[str, str] | None = None) -> Redirect

A redirect, for a server function, query, or form action to raise or return.

In the browser, the router navigates to url (replacing the history entry); a JavaScript-free form post gets a 303 to it. Raised from a query while a page renders on the server, it becomes the page's response. revalidate names the queries to refetch first; by default a redirect from a mutation revalidates everything the next page reads.

@form_action
@server_function
async def log_in(form: FormData) -> None:
    user = await authenticate(form["email"], form["password"])
    set_cookie("session", user.session_token)
    raise redirect("/dashboard")

reload

reload(*, revalidate: str | list[str] | None = None, headers: dict[str, str] | None = None) -> Reload

Revalidate queries on the current page without navigating.

revalidate names query keys (or key prefixes); omit it to revalidate every query on the page.

respond

respond(value: Any = None, *, status: int = 200, headers: dict[str, str] | None = None, revalidate: str | list[str] | None = None) -> Respond

Return value with a status, headers, or the query keys this call changed.

revalidate="" (or []) marks a POST call that changed nothing, so the page's queries aren't refetched:

@server_function
async def log_search(term: str) -> None:
    analytics.record(term)
    return respond(revalidate="")

set_transport

set_transport(transport: Transport | None, *, base: str | None = None) -> None

Send browser-side server function calls through transport (tests and tools).

wybthon.testing.serve uses it to connect components rendered in CPython to an in-process ASGI app. With a transport set, calls are made remotely even outside a browser. None restores the default.

server_function

server_function(fn: Callable[P, Awaitable[R]]) -> ServerFunction[P, R]
server_function(*, method: Method = 'POST') -> Callable[[Callable[P, Awaitable[R]]], ServerFunction[P, R]]
server_function(fn: Any = None, /, *, method: Method = 'POST') -> Any

Make an async def run on the server, wherever it's called from.

Use it bare (@server_function) for a mutation, called with POST, or with method="GET" for a read. See wybthon.server_functions for the whole contract.

Parameters:

Name Type Description Default
fn Any

The async def to run on the server. It must be defined at a module's top level, so the browser and the server name it the same way.

None
method Method

"POST" (the default; a mutation that revalidates the page's queries when it completes) or "GET" (a read).

'POST'

Returns:

Type Description
Any

A ServerFunction with the same

Any

signature.

handle async

handle(event: RequestEvent, fn_id: str, method: str, query: str, headers: dict[str, str], body: bytes) -> tuple[int, dict[str, str], bytes, ServerFunction[..., Any] | None, Any]

Run a server function for an HTTP request.

Returns (status, headers, body, function, outcome). outcome is the raw result or exception, for the caller's form-post handling.

What's in this module

Server functions and the responses they return. Everything here is exported from wybthon, as are FormData and UploadedFile, what a form action receives.

Name Description
server_function @server_function or @server_function(method="GET"): make an async def run on the server.
ServerFunction What the decorator returns; id is module:qualname, and url the URL the browser calls.
redirect Navigate (or answer a page or a form post with a redirect).
reload Revalidate queries without navigating.
respond Return a value with a status, headers, or the query keys a call changed.

See also