Overview

UIX

Persistent Contexts

Eternal modules

UIX allows you to save and restore the context of a module across backend restarts or page reloads. Such persistent modules are called eternal modules. To create an eternal module, simply add a .eternal.ts or .eternal.tsx extension to the module filename.

Simple example:

backend/counter.eternal.ts
// this is an eternal module that is only initialized once export const counter = $(0);
backend/entrypoint.ts
// this module is loaded with a new context each time the app backend is restarted import {counter} from "./counter.eternal.ts"; counter.val++; // increments the counter value each time the backend is restarted console.log(counter); // logs 1,2,3,4... on each backend restart

You can read more about (eternal) contexts in the chapter Functions and Contexts.

Session data

Each browser client in a UIX app is automatically bound to a unique session. You can access shared data for this session from both the frontend and the backend, and private data from the backend only. Both shared and private data are persisted across backend restarts. They exist only as long as the frontend session has not been expired.

The shared and private data for the current session can be accessed through the Context object:

backend/entrypoint.tsx
export default { '/get-name': async (ctx) => { // load sharedData object for this context const sharedData = await ctx.getSharedData() // return 'name' entry return sharedData.name }, '/set-name/:name': async (ctx, {name}) => { // load sharedData object for this context const sharedData = await ctx.getSharedData() // set 'name' entry sharedData.name = name; return name; } } satisfies Entrypoint

In the same way, you can access the private data for a session on the backend with await ctx.getPrivateData(). You can store any DATEX compatible value in the shared/private data objects.

You can also access private data in normal backend functions that do not access a UIX context element by passing the caller information provided by datex.meta to Context.getPrivateData:

export async function updatePrivateId(id: number) {
    // get the private data for the caller endpoint
    const privateData = await Context.getPrivateData(datex.meta);
    // update the 'id' field in the private data 
    privateData.id = id;
}
Warning

Private data is stored directly on the backend and can contain any amount of data. In contrast, shared data is stored in the browser cookies, which are limited to a maximum storage size of 4096 bytes.

Declaring session data types

You can define the global interfaces SharedData and PrivateData for session data type safety across your entrypoints.

declare global {
    // shared session data properties, also acessible on the frontend
    interface SharedData {
        username: string
        icon?: string
    }

    // private session data properties, only acessible on the backend
    interface PrivateData {
        passwordHash: string
        uuid: string
    }
}

// ...
const sharedData = await ctx.getSharedData()
ctx.username // -> type string
ctx.nonExistingProperty // error, property does not exist!

Persistent values

As as an alternative to using eternal modules, persistent values can also be created with the eternal label

const counter = eternal ?? $(0); // counter value gets restored from the previous state or initialized if no previous state exists

counter.val ++; // counter gets incremented every time

For non-DATEX native types such as HTML elements, you must use lazyEternal to ensure that the type definitions are loaded:

export default await lazyEternal ?? $(<div>Content</div>)
const customValue = await lazyEternal ?? $(new MyCustomClass())

Did this doc help you?

Privacy Policy

Help us improving our docs

Our documentations are fully open source. Something is wrong or unclear?

Make a contribution