Controllers
Controllers are the heart of Mountaineer. They are the Python classes that define the logic for your views, layouts, and apps. They are responsible for rendering the view, handling side effects, and managing the state of your application.
View Controller
One Controller should be created for every frontend page in your webapp. The controller is where you place all logic that's necessary for this one page - the data that's pushed from the frontend and the data that's received from the page.
All data from render
is passed to the frontend and usable with automatically
generated typehints.
Class Constructor
- Name
slow_ssr_threshold
- Type
- float
- Description
Default: 0.1
Each python process has a single V8 runtime associated with it, so SSR rendering can become a bottleneck if it requires processing. We log a warning if we detect that an SSR render took longer than this threshold.
- Name
hard_ssr_timeout
- Type
- float | None
- Description
Default: 10.0
Each python process has a single V8 runtime associated with it, so SSR rendering can become a bottleneck if it requires processing. We log a warning if we detect that an SSR render took longer than this threshold.
Class Attributes
- Name
url
- Type
- str
- Description
The URL that this controller will be mounted at. This can contain dynamic path parameters, e.g.
/user/{user_id}
. Each parameter will be passed to your render function as a keyword argument, so this function would have a signature likeasync def render(self, user_id: str) -> RenderBase
.
- Name
view_path
- Type
- str | ManagedViewPath
- Description
Typically, view paths should be a relative path to the local project root. Paths are only used if you need to specify an absolute path to another file on disk.
- Name
slow_ssr_threshold
- Type
- float
- Description
If a server-side rendering operation takes longer than this threshold, we will log the time and path parameters as a warning to help debugging.
- Name
hard_ssr_timeout
- Type
- float | None
- Description
If a server-side rendering operation takes longer than this threshold, we will automatically kill the V8 runtime and return an error to the client. This helps avoid blocking other server render handlers if the React render logic hangs.
- Name
source_map
- Type
- SourceMapParser | None
- Description
During development, we will load server-side source maps alongside the raw javascript code. This parser controls converting stack traces from the minified code to the original source code.
- Name
script_name
- Type
- Description
The short-hand name of the controller, used to resolve the SSR script and other dependencies from disk.
Class Methods
- Name
render
- Return type
- RenderBase | None | Coroutine[Any, Any, RenderBase | None]
- Description
Render provides the raw data payload that will be sent to the frontend on initial render and during any sideeffect update. In most cases, you should return a RenderBase instance. If you have no data to display you can also return None.
This function must be explicitly typehinted with your response type, which allows the AppController to generate the correct TypeScript types for the frontend:
Code
class MyServerData(RenderBase): pass class MyController: def render(self) -> MyServerData: passIf you don't intend to sync any data from server->client you can typehint this function with an explicit None return annotation:
Code
class MyController: def render(self) -> None: passRender functions accept any number of arguments and keyword arguments, following the FastAPI route parameter style. This includes query parameters, path parameters, and request bodies.
Code
class MyController: url = "/my-url/{path_param}" def render( self, query_param: str, path_param: int, dependency: MyDependency = Depends(MyDependency), ) -> MyServerData: ...
- Name
resolve_paths
- Return type
- bool
- Description
Typically used internally by the Mountaineer build pipeline. Calling this function sets the active
view_base
of the frontend project, which allows us to resolve the built javascripts that are required for this controller.
Code
from mountaineer import ControllerBase, RenderBase class MyControllerRender(RenderBase): value: int class MyController(ControllerBase): url = "/my-page" view_path = "/app/my-page.tsx" async def render(self) -> MyControllerRender: return MyControllerRender(value=10)
Code
import { useServer } from "./_server"; const MyPage = () => { const serverState = useServer(); return <div>{serverState.value}</div>; } export default MyPage;
Layout Controller
Base class for layouts. Layout controllers are used to generate the HTML that wrap a regular view controller. They support all actions that a regular controller does (@sideeffect and @passthrough).
Their limitations:
- They are run in an isolated dependency injection context, they don't share dependency injected values with the given page
- The current page Request is not supported within render()
- Sideeffect updates to the layout don't affect the page state, and vice-versa
- Layout controllers can't be mounted as a URL route
App Controller
Main entrypoint of a project web application.
Class Constructor
- Name
name
- Type
- str
- Description
Default: 'Mountaineer Webapp'
- Name
version
- Type
- str
- Description
Default: '0.1.0'
- Name
view_root
- Type
- Path | None
- Description
Default: None
- Name
global_metadata
- Type
- Metadata | None
- Description
Default: None
Metadata that's injected into every page. This is useful for setting global stylesheets and scripts. It's also useful for setting fallback metadata if it isn't overloaded by individual pages like setting a page title.
- Name
custom_builders
- Type
- list[ClientBuilderBase] | None
- Description
Default: None
- Name
config
- Type
- ConfigBase | None
- Description
Default: None
The configuration object for the application. This is the main place to place runtime configuration values that might be changed across environments. One instance is registered as a global singleton and cached within the AppController class as well.
- Name
fastapi_args
- Type
- dict[str, Any] | None
- Description
Default: None
Override or add additional arguments to the FastAPI constructor. See the FastAPI documentation for the attributes of this constructor.
Class Attributes
- Name
builders
- Type
- list[ClientBuilderBase]
- Description
- Name
app
- Type
- FastAPI
- Description
Internal FastAPI application instance used by Mountaineer. Exposed to add API-only endpoints and middleware.
- Name
live_reload_port
- Type
- int
- Description
Port to use for live reloading of the webserver. This will be used to create a websocket connection to the browser to trigger a reload when the frontend has updates. By default we will bind to port 0 to guarantee an open system port.
- Name
controllers
- Type
- list[ControllerDefinition]
- Description
Mounted controllers that are used to render the web application. Add a new one through
.register()
.
- Name
internal_api_prefix
- Type
- str
- Description
URL prefix used for the action APIs that are auto-generated through @passthrough and @sideeffect decorators.
- Name
hierarchy_paths
- Type
- dict[Path, LayoutElement]
- Description
Mapping of disk paths to file-system metadata about the controller. This internally builds up a DAG of the layout hierarchy to be used for rendering nested pages and layouts.
Class Methods
- Name
register
- Return type
- Description
Register a new controller. This will:
- Mount the html of the controller to the main application service
- Mount all actions (ie. @sideeffect and @passthrough decorated functions) to their public API
- Name
invalidate_view
- Return type
- Description
After an on-disk change of a given path, we should clear its current script cache so we rebuild with the latest changes. We should also clear out any nested children - so in the case of a layout change, we refresh all of its subpages.
- Name
compile_html
- Return type
- Description
Compiles the HTML for a given page, with all the controller-returned values hydrated into the page.
- Name
update_hierarchy
- Return type
- Description
When we register a new element, we need to:
- Find if there are any on-disk layouts that have been defined. We add these greedily, before we know if they're backed by a layout controller
- Add the controller as a LayoutElement if it's new. Otherwise update the existing element
This function is built to be invariant to the order of the updates, so we can call it multiple times and call it progressively as more controllers are added. It will be valid at any given state for the current webapp mounting.
- Name
merge_render_signatures
- Return type
- Description
Collects the signature from the "reference_controller" and replaces the active target_controller's render endpoint with this new signature. We require all these new parameters to be kwargs so they can be injected into the render function by name alone.
- Name
generate_openapi
- Return type
- Description
Bundle custom user exceptions in the OpenAPI schema. By default endpoints just include the 422 Validation Error, but this allows for custom derived user methods.
Render
Base class for all renderable data models. Subclass this model when defining your own component data schema.
Class Attributes
- Name
metadata
- Type
- Metadata | None
- Description
- Name
model_config
- Type
- Description
Metadata
We provide support for all header tags that you'll find within an html <head>
. These
won't be rendered by your browser but are where you'll place style imports and page
metadata for SEO.
We have passthrough classes for all the common tags
so they align to what's outputted in HTML. These are defined and referenced below as ScriptAttribute
, LinkAttribute
, MetaAttribute
, etc.
We also provide some convenient shortcuts for more complicated, common meta tags.
Metadata lets the client specify the different metadata definitions that should appear on the current page. These are outside the scope of React management so are only handled once on the initial page render. But because they still receive the initial render request, you can add any dynamic logic that you want to customize the behavior of the page metadata. This includes templating the page title depending on a remote value, modifying the scripts injected depending on the user, etc.
Class Attributes
- Name
title
- Type
- str | None
- Description
- Name
metas
- Type
- list[MetaAttribute]
- Description
- Name
links
- Type
- list[LinkAttribute]
- Description
- Name
scripts
- Type
- list[ScriptAttribute]
- Description
- Name
explicit_response
- Type
- Response | None
- Description
- Name
ignore_global_metadata
- Type
- bool
- Description
- Name
model_config
- Type
- Description
Class Methods
- Name
merge
- Return type
- Metadata
- Description
- Name
build_header
- Return type
- list[str]
- Description
Builds the header for this controller. Returns the list of tags that will be injected into the
Code
class MyController(Controller): async def render( self, user_name: str, ): return MyControllerRender( metadata=Metadata( title=f"Welcome, {user_name}!", ) )
Code
Metadata( title="My Page", metas=[ MetaAttribute(...), ThemeColorMeta(...), ViewportMeta(...), ], links=[ LinkAttribute(...), ] )
Inject a generic tag into the of the current page.
Class Attributes
- Name
src
- Type
- str
- Description
- Name
asynchronous
- Type
- bool
- Description
- Name
defer
- Type
- bool
- Description
- Name
optional_attributes
- Type
- dict[str, str]
- Description
Code
ScriptAttribute( src="https://example.com/script.js", asynchronous=True, optional_attributes={"defer": "true"}, defer=False, )
Inject a generic tag into the of the current page.
Class Attributes
- Name
rel
- Type
- str
- Description
- Name
href
- Type
- str
- Description
- Name
optional_attributes
- Type
- dict[str, str]
- Description
Code
LinkAttribute( rel="stylesheet", href="https://example.com/styles.css", optional_attributes={"media": "screen"}, )
Represents a meta tag that can be included in the head of an HTML document.
Class Attributes
- Name
name
- Type
- str | None
- Description
- Name
content
- Type
- str | None
- Description
- Name
optional_attributes
- Type
- dict[str, str]
- Description
Code
MetaAttribute( name="description", content="This is a description of the page.", optional_attributes={"lang": "en"}, )
Defines the bounds on the current page and how much users are able to zoom.
Class Attributes
- Name
width
- Type
- str
- Description
- Name
initial_scale
- Type
- float
- Description
- Name
maximum_scale
- Type
- float
- Description
- Name
user_scalable
- Type
- bool
- Description
Class Methods
- Name
create_attribute
- Return type
- Description
Code
ViewportMeta( initial_scale=1.0, maximum_scale=2.0, user_scalable=True, )
Customizes the default color that is attached to the page.
Class Attributes
- Name
color
- Type
- str
- Description
- Name
media
- Type
- str | None
- Description
Class Methods
- Name
create_attribute
- Return type
- Description
Code
ThemeColorMeta( color="white", media="light", )
SSR
Every page on Mountaineer is server-side rendered by default. Most SSR logic is handled in our embedded Rust layer. In Rust we spin up a V8 isolate, handle logging, catch and process exceptions, etc. This page only covers the client functions that are exposed to Python.
render_ssr
Render the React component in the provided SSR javascript bundle. This file will be directly executed within the V8 runtime.
To speed up requests for the same exact content (ie. same react and same data) we cache the result of the render_ssr_rust call by default for a limited amount of previous calls. We limit the overall size of this cache to 5MB.
Parameters
- Name
script
- Type
- str
- Description
The raw code of the javascript bundle to execute. Should be pre-compiled into an SSR compatible package with a single entrypoint.
- Name
render_data
- Type
- dict[str, Any]
- Description
The raw code of the javascript bundle to execute. Should be pre-compiled into an SSR compatible package with a single entrypoint.
- Name
hard_timeout
- Type
- int | float | None
- Description
Default: None
The raw code of the javascript bundle to execute. Should be pre-compiled into an SSR compatible package with a single entrypoint.
An exception thrown by the V8 runtime in the case of a permanent failure that involves the content of the script.
Source Maps
Parse sourcemaps according to the official specification: https://sourcemaps.info/spec.html
Class Constructor
- Name
path
- Type
- str | Path
- Description
If specified, will output source paths relative to this path.
- Name
relative_to
- Type
- Description
If specified, will output source paths relative to this path.
Class Attributes
- Name
path
- Type
- Description
- Name
source_map
- Type
- SourceMapSchema | None
- Description
- Name
parsed_mappings
- Type
- dict[tuple[int, int], mountaineer_rs.MapMetadata] | None
- Description
Class Methods
- Name
parse
- Return type
- Description
Parse the source map file and build up the internal mappings. This is deterministic with respect to the initialized source map path, so this will be a no-op if it's already been run.
- Name
get_original_location
- Return type
- Description
For a compiled line and column, return the original line and column where they appeared in the pre-built file.
- Name
map_exception
- Return type
- str
- Description
Given a JS stack exception, try to map it to the original files and line numbers
- Name
convert_relative_path
- Return type
- Description
Absolute paths are convenient for internal use since they fully qualify a given file. However, for display they often get long and repetitive across multiple lines. This function will convert an absolute path to a relative path if it's within the same directory as the current working directory.