Skip to main content

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:

Setting headers with a global function
export function setDarkTheme(ygg: YggdrasilServerContext<AppTypes>) {
ygg.additionalHeaders.append.push(["Set-Cookie", "use-dark-theme=1"]);
}
Setting headers with a function defined in ctx
// 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 ygg context should not be stored or accessed through a separate variable. Always access members (including nested members) of the ygg context from ygg (you might be able to do this for your own ctx or conduitCtx through, depending on your implementation).
  • Never call ygg member 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 ctx or conduitCtx contexts, they will be re-bound to ygg on access, so you must either re-bind them every time you access them, or include those functions inside an object in your app's ctx or conduitCtx to 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:

Dealing with multiple ygg objects
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.