Context
To make it easy to pass state between functions responding to a request,
Yggdrasil Server packages the original request, normalized parameters, options,
your app's context, utilities, and more in a single, shared YggdrasilServerContext object
(commonly named ygg).
Some Yggdrasil Server features (Yggdrasil Server Router and Conduit) have their own
state that they provide to you via an extended version of the YggdrasilServerContext.
This extended version uses prototype-based inheritance so that methods implemented in
the original object continue to run there.
Context-bound functions
For your convenience, built-in functions that live in the ygg context object
are bound to the ygg object so they can access the context with the this keyword
instead of requiring an explicit parameter. Since accessing the active ygg context
is a common need, Yggdrasil Server automatically binds all top-level functions in
your app's ctx and conduitCtx (for Yggdrasil Server Conduit) to ygg.
Here's a brief example showing how this can help simplify your app:
export function setDarkTheme(ygg: YggdrasilServerContext<AppTypes>) {
ygg.additionalHeaders.append.push(["Set-Cookie", "use-dark-theme=1"]);
}
// This only works when accessing the method through ygg.ctx.setDarkTheme()
const ctx = {
setDarkTheme(this: YggdrasilServerContext<AppTypes>) {
this.additionalHeaders.append.push(["Set-Cookie", "use-dark-theme=1"]);
}
}
Important side effects of binding
The important side effects of this convention are:
- Anything you access through the
yggcontext should not be stored or accessed through a separate variable. Always access members (including nested members) of theyggcontext fromygg(you might be able to do this for your ownctxorconduitCtxthrough, depending on your implementation). - Never call
yggmember functions with.bind(),.apply(), or anything else that would re-bind the member functions you're calling. - If you include a function bound to something else in your app's
ctxorconduitCtxcontexts, they will be re-bound toyggon access, so you must either re-bind them every time you access them, or include those functions inside an object in your app'sctxorconduitCtxto avoid re-binding.
Accessing context
The ygg context is only provided to you through functions you implement (e.g. the
callback of handleRequest). Only use the ygg context within the function it was
passed to or any other functions invoked as part of its execution. Do not store or
use the ygg context outside of that scope. Because the ygg context is
request-specific, you should never store or pass the ygg context through
global variables, or you will risk leaking data between requests.
In some cases, you may be able to access multiple ygg contexts simultaneously:
DemoServer.handleRequest(request, async (ygg) => {
const RequestSpecificConduitServer = new YggdrasilConduitServer<AppTypes, ApiTypes>(FlowServer, {
async greeter(ygg: YggdrasilConduitContext<AppTypes, ApiTypes>, name: string) {
return new Response("Hello " + name + "!", { status: 200 });
}
});
return RequestSpecificConduitServer.handleRequest(ygg);
} /* ... */ );
You should always use the ygg context nearest to your implementation. In this
example, it would be incorrect to use the ygg context from DemoServer.handleRequest
in the greeter function, which should use the ygg context passed directly to it.
Any functions invoked as part of the greeter functions's execution must also use
the ygg context passed to greeter. Otherwise, data loss may occur.