<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged full-stack</title>
<link>https://coderlegion.com/tag/full-stack</link>
<description>Powered by Question2Answer</description>
<item>
<title>Alusus WebPlatform: Best UI Practices and Browser Sync Process</title>
<link>https://coderlegion.com/14610/alusus-webplatform-best-ui-practices-and-browser-sync-process</link>
<description>&lt;p&gt;This article covers the approach used to write user interface programs using Alusus language and the WebPlatform library, in terms of how the code is written and how UI elements are handled during user interaction. It also explains how WebPlatform works behind the scenes to update the interface.&lt;/p&gt;
&lt;h3 id=&quot;accessing-ui-widgets&quot;&gt;Accessing UI Widgets&lt;/h3&gt;
&lt;p&gt;Those familiar with the Alusus language, the WebPlatform library, and our previous articles and videos on the subject already know that the approach to building UI interfaces relies on the &lt;code&gt;.{}&lt;/code&gt; operator to construct the element tree that forms the UI. For example, if we want to create a box containing a text label, an input field, and a button, the code would look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;Window.instance.setView(
    Box().{
        addChildren({
            Text(String(&quot;Enter data&quot;)),
            TextInput(),
            Button(String(&quot;Send&quot;)).{
                onClick.connect(closure (widget: ref[Widget], payload: ref[Int]) {
                });
            }
        });
    }
);

runEventLoop();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But how do we then programmatically interact with the input field (&lt;code&gt;TextInput&lt;/code&gt;) to retrieve the value the user entered? It's straightforward: we keep a reference to it so we can interact with it later, as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;def input: SrdRef[TextInput];

Window.instance.setView(
    Box().{
        addChildren({
            Text(String(&quot;Enter data&quot;)),
            TextInput().{
                input = this;
            },
            Button(String(&quot;Send&quot;)).{
                onClick.connect(closure (widget: ref[Widget], payload: ref[Int]) {
                    useUserData(input.text);
                });
            }
        });
    }
);

runEventLoop();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we keep a reference pointing to the element. Its value is set when the element is created, and then inside the button's &lt;code&gt;onClick&lt;/code&gt; handler, we use that variable to access the value entered by the user.&lt;/p&gt;
&lt;p&gt;This approach works, but it has a problem: the variable &lt;code&gt;input&lt;/code&gt; is only valid as long as execution hasn't left the current function. In the case of the main function this isn't an issue, since it's an infinite function — execution blocks at the &lt;code&gt;runEventLoop()&lt;/code&gt; call indefinitely, so we never exit the function and the &lt;code&gt;input&lt;/code&gt; variable remains valid for the entire lifetime of the program. However, if this was a different function — one that builds the tree and returns it to another function to display — we couldn't use local variables, because they are temporary and removed from memory when the function exits, even though the UI itself is still valid and displayed in the browser. In such cases, we have two options: use global variables (which we rarely recommend), or use the proper approach: relying on &lt;strong&gt;components&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;using-components&quot;&gt;Using Components&lt;/h3&gt;
&lt;p&gt;Components allow you to bundle a user interface together with its associated data and operations into a single reusable unit, much like components in frameworks such as React, Angular, and others. To create a component, you define a new class derived from the &lt;code&gt;Component&lt;/code&gt; class, then build the user interface inside that class's constructor. In this case, you can define variables that reference UI elements as class members rather than as temporary local variables, which ensures they remain in memory as long as the component is in memory. The following example shows how to convert the code above into a component and how to use it later in the main function:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;class MyComponent {
    @injection def component: Component;
    def input: SrdRef[TextInput];

    handler this~init() {
        def self: ref[this_type](this);
        this.view = Box().{
            addChildren({
                Text(String(&quot;Enter data&quot;)),
                TextInput().{
                    self.input = this;
                },
                Button(String(&quot;Send&quot;)).{
                    onClick.connect(closure (widget: ref[Widget], payload: ref[Int]) {
                        self.useUserData(self.input.text);
                    });
                }
            });
        };
    }

    handler this_type(): SrdRef[MyComponent] {
        return SrdRef[MyComponent].construct();
    }
}

@uiEndpoint[&quot;/&quot;]
@title[&quot;Example&quot;]
func main {
    Window.instance.setView(MyComponent());
    runEventLoop();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that the class begins by declaring a variable of type &lt;code&gt;Component&lt;/code&gt; annotated with &lt;code&gt;@injection&lt;/code&gt;, and this is how inheritance is done in Alusus language. The &lt;code&gt;@injection&lt;/code&gt; modifier tells the compiler to inject the members of the contained object into the scope of the containing object. This is essentially what inheritance is at its core — containment plus exposure of the inner class's members in the outer class, and the &lt;code&gt;extends&lt;/code&gt; keyword that some languages provide is just a syntactic sugar for this.&lt;/p&gt;
&lt;p&gt;Also notice that the initializer (constructor) declares a variable named &lt;code&gt;self&lt;/code&gt; of the exact same type as &lt;code&gt;this&lt;/code&gt;. We copy the value of &lt;code&gt;this&lt;/code&gt; here so we can use it later inside the UI tree. The reason for this copy is to distinguish between the &lt;code&gt;this&lt;/code&gt; referring to the component and the &lt;code&gt;this&lt;/code&gt; referring to a UI element. This need arises because the &lt;code&gt;.{}&lt;/code&gt; operator automatically defines a variable named &lt;code&gt;this&lt;/code&gt; pointing to the element that precedes the operator. In other words, the keyword &lt;code&gt;this&lt;/code&gt; inside the curly braces of &lt;code&gt;TextInput().{ this }&lt;/code&gt; refers to the &lt;code&gt;TextInput&lt;/code&gt; being created there, shadowing the outer &lt;code&gt;this&lt;/code&gt; that refers to the component. So inside those curly braces, &lt;code&gt;self&lt;/code&gt; refers to the component while &lt;code&gt;this&lt;/code&gt; refers to the &lt;code&gt;TextInput&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;At the end of the class, we define a &lt;code&gt;this_type()&lt;/code&gt; handler. This handler redefines what happens when the compiler sees parentheses used with the class name — as in &lt;code&gt;Window.instance.setView(MyComponent())&lt;/code&gt; inside the main function. By default, using parentheses with a class name creates a temporary (stack-allocated) instance of that class. For example, &lt;code&gt;Int()&lt;/code&gt; creates a temporary &lt;code&gt;Int&lt;/code&gt; value. But what we want for UI components is heap allocation using a &lt;code&gt;SrdRef&lt;/code&gt; (shared reference), which keeps the object alive even after the function exits, as long as something holds a copy of that reference.&lt;/p&gt;
&lt;p&gt;If the component accepts arguments in its initializer, the &lt;code&gt;this_type()&lt;/code&gt; handler would look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;    handler this_type(arg: String): SrdRef[MyComponent] {
        return SrdRef[MyComponent]().{ alloc()~init(arg) };
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We could then instantiate the component like this: &lt;code&gt;MyComponent(String(&quot;...&quot;))&lt;/code&gt;. In this definition, we declare a variable of type &lt;code&gt;SrdRef[MyComponent]&lt;/code&gt; without allocating the object itself, then call &lt;code&gt;alloc()&lt;/code&gt; to allocate memory, and &lt;code&gt;~init()&lt;/code&gt; to initialize it with the required arguments. When no arguments need to be passed, we can simply call the &lt;code&gt;construct()&lt;/code&gt; function on &lt;code&gt;SrdRef&lt;/code&gt;, which is a shortcut for calling &lt;code&gt;alloc()&lt;/code&gt; followed by &lt;code&gt;~init()&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;properties-methods-and-callbacks&quot;&gt;Properties, Methods, and Callbacks&lt;/h3&gt;
&lt;p&gt;You can enable access to different widgets of your components from outside the component using regular Alusus class properties. For example, to make the the text of the input field accessible from outside the component you can add a text property to your component, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;    handler this.text: String {
        return this.input.text;
    }
    
    handler this.text = String {
        this.input.text = value;
        return value;
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Likewise, you can define methods and callbacks like any regular class. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;class MyComponent {
    @injection def component: Component;
    def input: SrdRef[TextInput];
    def onButtonClicked: closure();

    handler this.text: String {
        return this.input.text;
    }
    
    handler this.text = String {
        this.input.text = value;
        return value;
    }

    handler this~init() {
        def self: ref[this_type](this);
        this.view = Box().{
            addChildren({
                Text(String(&quot;Enter data&quot;)),
                TextInput().{
                    self.input = this;
                },
                Button(String(&quot;Send&quot;)).{
                    onClick.connect(closure (widget: ref[Widget], payload: ref[Int]) {
                        self.useUserData(self.input.text);
                        if not self.onButtonClicked.isNull() {
                            self.onButtonClicked();
                        }
                    });
                }
            });
        };
    }

    handler this_type(): SrdRef[MyComponent] {
        return SrdRef[MyComponent].construct();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the upper version of the component, you can now interact with these elements from outside the component, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;MyComponent().{
    text = String(&quot;some default value&quot;);
    onButtonClick = closure() {
        // Do something when the button is clicked
        doSomething(this.text);
    };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;cleanup-and-resource-release&quot;&gt;Cleanup and Resource Release&lt;/h3&gt;
&lt;p&gt;Unlike languages that rely on garbage collection — such as JavaScript, TypeScript, and others — Alusus uses &lt;strong&gt;reference counting&lt;/strong&gt; for memory management. What distinguishes this approach is that the timing of object construction and destruction is well-defined, deterministic, and under full control, making it reliable for releasing resources created within a component. All we need to do is define a &lt;code&gt;~terminate&lt;/code&gt; handler (destructor) in the &lt;code&gt;MyComponent&lt;/code&gt; class and perform any necessary cleanup inside it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;    handler this~terminate() {
        // Free resources allocated by this component
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Alusus, we are guaranteed that &lt;code&gt;~terminate&lt;/code&gt; will be called immediately when the component is no longer needed — it won't linger in memory waiting for a garbage collector. This makes it reliable for resource cleanup. For example, if the &lt;code&gt;useUserData&lt;/code&gt; function in our component sends a request to the server, we can cancel that request in the terminate handler, guaranteeing that the request is cancelled immediately when the component is removed from the UI (assuming no other references to the component are held elsewhere).&lt;/p&gt;
&lt;h3 id=&quot;behind-the-scenes-how-ui-updates-work&quot;&gt;Behind the Scenes: How UI Updates Work&lt;/h3&gt;
&lt;p&gt;A question that may come to mind at this point is: when exactly are UI elements created in the browser, when are they removed, and how are they updated? Unlike React and many other libraries, the mechanism for creating and updating the UI in Alusus and WebPlatform is very simple and direct. There is no lifecycle (like that in React or Angular); instead, UI elements are created immediately when they are added to the window, or when they are connected to a tree that is currently attached to the window — and this happens within the same call that performs that attachment.&lt;/p&gt;
&lt;p&gt;For example, when calling:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;Window.instance.setView(MyComponent());
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the UI elements (the text, input field, and button) will be created in the browser immediately, before &lt;code&gt;setView&lt;/code&gt; returns. And if &lt;code&gt;setView&lt;/code&gt; is called again with a different value, those elements will be removed and replaced with the new ones immediately, before &lt;code&gt;setView&lt;/code&gt; returns. The same applies to calling &lt;code&gt;addChildren&lt;/code&gt; on a &lt;code&gt;Box&lt;/code&gt; widget or similar functions in other widget types.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;setView&lt;/code&gt; is called with a new value, the current view is removed and any terminate handlers within that view are called immediately, before &lt;code&gt;setView&lt;/code&gt; returns. The same applies to &lt;code&gt;Box.removeChildren&lt;/code&gt;, which will destroy the child and release all its resources before returning.&lt;/p&gt;
&lt;p&gt;The same principle applies to UI updates: executing an operation like &lt;code&gt;text.text = String(&quot;...&quot;)&lt;/code&gt; updates the interface immediately, before that operation completes.&lt;/p&gt;
&lt;p&gt;As for data entered by the user, it is read directly from the browser when the corresponding property is accessed. For example, reading the &lt;code&gt;input.text&lt;/code&gt; property reads directly from the input field in the browser — there is no need to synchronize data between the browser and Alusus objects.&lt;/p&gt;
&lt;p&gt;This simplicity of design makes applications written with Alusus and WebPlatform more stable, better performing, and predictable. There is no complex process running behind the scenes — as there is in other libraries — that causes confusion and surprises for the developer.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/14610/alusus-webplatform-best-ui-practices-and-browser-sync-process</guid>
<pubDate>Sun, 12 Apr 2026 20:00:43 +0000</pubDate>
</item>
<item>
<title>Deeper Look Into Alusus WASM Based Chat Example</title>
<link>https://coderlegion.com/11609/deeper-look-into-alusus-wasm-based-chat-example</link>
<description>&lt;p&gt;In our previous article &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/10923/building-a-wasm-chat-app-using-alusus-step-1-the-ui&quot;&gt;Building a WASM Chat App Using Alusus&lt;/a&gt; we built a simple chat application using Alusus programming language and WebPlatform framework. In this article I'm going to step back and explain the code in more details.&lt;/p&gt;
&lt;p&gt;The entire app was written in a single source file. This single source file contains everything in the project including requesting dependencies, defining and accessing the data model, back-end, front-end, as well as build and publish script. You might think that it's not a good idea to put everything in a single file anyway, and you would be right, but the ability to put everything in a single file is a very useful feature of Alusus language, because it means the language does not dictate how your project should be structured. This has two main benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You can organize your project based on functionality, rather than based on where the code gets executed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can package related back-end and front-end code together. For example, the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Alusus/Identity&quot;&gt;Identity&lt;/a&gt; library includes front-end components as well as related back-end endpoints, making it much simpler to maintain and use the library.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;requesting-dependencies&quot;&gt;Requesting Dependencies&lt;/h3&gt;
&lt;p&gt;The project starts with few import statement, which is pretty standard, but one of the packages it imports is Alusus Package Manager (Apm), which it then use to import external libraries.&lt;/p&gt;
&lt;p&gt;Apm takes care of fetching then importing dependencies from an external source. At this point the only external source it supports is GitHub. When a package is imported it looks locally for that package, and if it doesn't find it it automatically fetches it from GitHub, then it imports the package into the project. So, the user can manage his project's dependencies from within the source code, without needing to define those dependencies in external yaml, json, or xml files like most other package managers.&lt;/p&gt;
&lt;h3 id=&quot;the-data-model&quot;&gt;The Data Model&lt;/h3&gt;
&lt;p&gt;You can see in the code that all it takes to define the data model is to define classes and mark them with some modifiers. The ORM depends on those modifiers for generating the DB schema, while changes in the data model can applied using migrations, which are user defined functions marked with a modifier as well. Take a look at the message model for example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;@model[&quot;messages&quot;, 2]
class MessageModel {
    Rows.define_model_essentials[];

    @foreignKey[&quot;rooms&quot;, &quot;name&quot;]
    @VarChar[&quot;256&quot;]
    @notNull
    @column[&quot;room_name&quot;]
    def roomName: String;

    @foreignKey[&quot;users&quot;, &quot;username&quot;]
    @VarChar[&quot;256&quot;]
    @notNull
    @column
    def username: String;

    @VarChar[&quot;1024&quot;]
    @notNull
    @column
    def message: String;

    @BigInteger
    @notNull
    @column[&quot;creation_date&quot;]
    def creationDate: Int[64];
    
    @migration[1, 2, { RoomModel: 1; UserModel: 1 }]
    function migrateToV2(db: ref[Rows.Db]): SrdRef[Error] {
        db.exec(&quot;alter table messages add column room_name varchar(256) not null references rooms(name)&quot;);
        db.exec(&quot;alter table messages add column username varchar(256) not null references users(username)&quot;);
        return SrdRef[Error]();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Most of these attributes modifiers are self-explanatory. The are some details related to migrations, but migrations is a subject that's worth its own article, so will leave it for another time.&lt;/p&gt;
&lt;p&gt;Using the model to fetch data from the DB is done through a syntax like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;    def messages: Array[SrdRef[MessageModel]] = db
        .from[MessageModel]
        .where[roomName == body(&quot;roomName&quot;)~cast[String].trim()]
        .order[-creationDate]
        .select()
        .slice(0, MAX_MESSAGES);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the upper statement it's using this model to fetch messages belonging to a specific room. There are few things to note here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The use of square brackets. In Alusus square brackets aren't used for arrays like many other languages, instead, square brackets indicate parameters that are processed during compile time, compared to parenthesis which are used for passing parameters at run time. In this statement we are passing few things at compile time. This is because converting this call to an SQL statement happens at compile time, not run time, which is good for both performance as well as security.&lt;/li&gt;
&lt;li&gt;Within the parameters of &lt;code&gt;.where&lt;/code&gt; we are passing an expression. At compile time, this expression gets converted into an SQL where clause. This is a pretty clean and easily readable way for writing DB queries.&lt;/li&gt;
&lt;li&gt;A similar thing applies to the &lt;code&gt;.order&lt;/code&gt; call; what we are passing to &lt;code&gt;order&lt;/code&gt; is an expression that tells the ORM how to order the result. In this case we are order by &lt;code&gt;creationDate&lt;/code&gt; descending. Notice that we aren't passing a value here; we are passing the name of the model class' member variable.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;back-end-and-front-end&quot;&gt;Back-end and Front-end&lt;/h3&gt;
&lt;p&gt;You can see two different modifier types used for annotating endpoints: @beEndpoint (back-end endpoint) and @feEndpoint (front-end endpoint). Both of these are applied to functions to make them the entry point of that endpoint. Back-end endpoints are simply functions that receive a connection object that they can use to read request data and send back responses. These functions are part of the main app, so they, unlike front-end endpoints, get compiled to the same instruction set as the main app.&lt;/p&gt;
&lt;p&gt;Front-end endpoints on the other hand are more complicated in terms of compilation. When you annotate a function with &lt;code&gt;@feEndpoint&lt;/code&gt; that tells WebPlatform to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Compile the function into WebAssembly.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Generate a corresponding HTML file (and potentially other related files).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This complexity, however, is handled completely by WebPlatform and the user doesn't need to worry about any of that. The user just uses some APIs to build a UI in a way similar to desktop applications. The user doesn't need to worry about writing HTML, CSS, Javascript, or build scripts; things just work. I don't know if web development can be made simpler and more intuitive than this.&lt;/p&gt;
&lt;p&gt;Within the front-end function you'll see a call to &lt;code&gt;Window.instance.setView&lt;/code&gt; which sets the browser's view. What gets passed to &lt;code&gt;setView&lt;/code&gt; is a UI tree composed of widgets and components. This is similar to many other UI frameworks, but, unlike other frameworks, the syntax used to build the tree is standard Alusus syntax. No html, xml, or jsx syntax; just standard Alusus syntax that can be used to build UI trees and can also be used for other things.&lt;/p&gt;
&lt;p&gt;The example also includes two components: TextEntry and Header. Those are simply classes that inherit from the Component class and set the view property of it. They set the &lt;code&gt;view&lt;/code&gt; property inside the constructor, which in Alusus is defined using the &lt;code&gt;handler this~init&lt;/code&gt; definition. The following syntax is what is used to define the inheritance:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;@injection def component: Component;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is a reason for this unusual syntax but that is worth its own article, so we'll not get into that for now. Finally, you'll often see components include such a definition:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;    handler this_type(): SrdRef[Header] {
        return SrdRef[Header].construct();
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This handler overrides the parenthesis operator on the class itself, not the class instances. This is used to override the default behavior, which instantiates a temporary object, and replace it with a function that allocates an object on the heap and return a shared reference to it. This makes it easier to later on instantiate objects of this type when creating a UI tree.&lt;/p&gt;
&lt;p&gt;The front-end function usually ends with a call to &lt;code&gt;runEventLoop()&lt;/code&gt;. This is a blocking function that runs the event loop, similar to native app development.&lt;/p&gt;
&lt;h3 id=&quot;build-amp-publish-script&quot;&gt;Build &amp;amp; Publish Script&lt;/h3&gt;
&lt;p&gt;In the Deployment section of the article we replaced the entry point of the app with this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;Nashir.setup[Nashir.Config().{
    projectName = &quot;chat&quot;;
    projectVersion = &quot;v1&quot;;
    serverPort = 8000;
    initializer~no_deref = initializeServer~ast;
    dependencies.add(Rows.PostgresqlDriver.getBuildDependencies())؛
    publishDriver = Nashir.AlususNetPublishDriver();
}];
ProgArg.parse(2);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What these 8 lines of code give us is the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An entry point for running the server locally.&lt;/li&gt;
&lt;li&gt;A build script that generates an executable.&lt;/li&gt;
&lt;li&gt;A publish script that publishes the project to Alusus Net.&lt;/li&gt;
&lt;li&gt;A set of command line arguments that the user can use to decide whether to run, build, or publish.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of that was done with these few lines of code. Behind the scenes, these lines of code searches the code base for front end endpoints as well as static resource routes in order to determine what gets packaged with the app during the build. Basically, what takes a script to accomplish with other languages is done here with these few lines of code.&lt;/p&gt;
&lt;p&gt;At the end of the call to &lt;code&gt;Nashir.setup&lt;/code&gt; we just call &lt;code&gt;ProgArg.parse&lt;/code&gt; to tell it to parse the command line arguments and perform the requested user action. The 2 argument just tells ProgArg the index at which command line arguments start. In this case it's 2 because the first arg will contain &lt;code&gt;alusus&lt;/code&gt; and the second is the name of the main file passed to the compiler. If you use ProgArg from within pre-compiled code you'll need to pass 1 instead of 2 since you'll just call the executable itself rather than calling &lt;code&gt;alusus&lt;/code&gt; and passing the main source to it.&lt;/p&gt;
&lt;h3 id=&quot;low-level-language&quot;&gt;Low Level Language&lt;/h3&gt;
&lt;p&gt;What we've seen so far may give you the impression that we are dealing with a pretty high-level language, but don't let this neatness deceive you. Alusus is a low level language; think C language kind of low-level. In fact, it's actually compatible with C so you can directly import C libraries and use them; all you need to do is to declare those functions then you can call them, just like in C you only need to declare the function or import a header that does it for you. What makes Alusus look like a high level language is the flexibility of the language that allow libraries to define new syntax or make calls to the compiler itself.&lt;/p&gt;
&lt;p&gt;Going back to the example at hand, the compiler of Alusus doesn't know anything about packages, GitHub, back-end and front end points, static resource routes, deciding when to build to WebAssembly, data models, compiling DB queries to SQL, etc. All of those were features added by libraries. In fact, you may be surprised to know that Alusus' compiler doesn't even know what closures are; closures are added by the &quot;closures&quot; library which is built entirely in Alusus. This flexibility makes Alusus really unique and opens the door for many great features to be added in the future. Basically, you are getting even more flexibility than what dynamic high level languages provide, and you are combining that with the high performance of low level languages; the best of both worlds.&lt;/p&gt;
&lt;p&gt;Stay tuned for future articles in which I'll be covering more areas of Alusus and its libraries.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/11609/deeper-look-into-alusus-wasm-based-chat-example</guid>
<pubDate>Fri, 13 Feb 2026 23:59:46 +0000</pubDate>
</item>
<item>
<title>Building a WASM Chat App using Alusus - Step 5: Deployment</title>
<link>https://coderlegion.com/11268/building-a-wasm-chat-app-using-alusus-step-5-deployment</link>
<description>&lt;p&gt;In the &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/11116/building-a-wasm-chat-app-using-alusus-step-4-adding-users-and-rooms&quot;&gt;previous article&lt;/a&gt; we finished building a basic web app with support for rooms, users, and password based authentication. It's now time to deploy our app to some server. The recommended method of deployment is to generate an executable for this app, then deploy the executable, rather than deploy the source code and have Alusus itself installed on the production machine. The easiest way to do this is to use &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Alusus/Nashir&quot;&gt;Nashir&lt;/a&gt; to build the app and deploy it to Alusus Net, a low cost hosting service geared towards hosting Alusus based applications. To do this, first we need to import Nashir into our app:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;Apm.importFile(&quot;Alusus/Nashir&quot;, { &quot;Nashir.alusus&quot;, &quot;PublishDrivers/AlususNet.alusus&quot; });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nashir is a general building and publishing tool that can support multiple publishing targets, so we need to tell it which hosting service we are publishing on, and that's why we are importing the &lt;code&gt;AlususNet&lt;/code&gt; publish driver. With this imported, we can now use it in the program's entry point. All we need to do is replace the few lines in the entry point with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;func initializeServer {
    initializeDatabase();
}

Nashir.setup[Nashir.Config().{
    projectName = &quot;chat&quot;;
    projectVersion = &quot;v1&quot;;
    serverPort = 8000;
    initializer~no_deref = initializeServer~ast;
    dependencies.add(Rows.PostgresqlDriver.getBuildDependencies())؛
    publishDriver = Nashir.AlususNetPublishDriver();
}];
ProgArg.parse(2);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We do not need to create an entry point nor to manually start the server; Nashir will do all that for us, and it will give us the option of running the app directly, or generating an executable for it. The &lt;code&gt;initializeDatabase&lt;/code&gt; call is now moved to a function, and that function is passed to Nashir through its config. We need to do this because we no longer want to initialize the DB directly; instead, we want the DB to be initialized by the new program entry point that will be generated automatically for us by Nashir. This will be clearer once you understand that the code at the root scope only gets executed during the JIT phase; generated executables on the other hand start from a specific function, then includes all its dependencies. This is one of the unique features of Alusus, and it allows projects to generate multiple binary builds from a single unified project, allowing Nashir in this case to generate separate builds for the front end and the back end.&lt;/p&gt;
&lt;p&gt;The rest of the arguments in the call to &lt;code&gt;Nashir.setup&lt;/code&gt; is pretty self explanatory, you give it the basic info of the project, the list of binary dependencies, and an instance of the publish driver to use for the actual publishing after the build is complete.&lt;/p&gt;
&lt;p&gt;Nashir depends on a library called ProgArg, which is a library for managing program arguments. The final step is to call &lt;code&gt;ProgArg.parse&lt;/code&gt; giving it the index from which to start parsing arguments. We start from 2 because arg 0 is &lt;code&gt;alusus&lt;/code&gt; binary and arg 1 is the name of the source file. The call to &lt;code&gt;ProgArg.parse&lt;/code&gt; will trigger the right action based on the user's command line arguments.&lt;/p&gt;
&lt;p&gt;At this poine, when you run &lt;code&gt;alusus chat.alusus&lt;/code&gt; you will be presented with the list of commands available to you. Running &lt;code&gt;alusus chat.alusus start&lt;/code&gt; will start the application on your machine. The &lt;code&gt;build&lt;/code&gt; command will generate an executable, and the &lt;code&gt;publish&lt;/code&gt; command will generate the executable then publish it on Alusus Net. The remaining publishing steps will happen interactively in the terminal, where you will be asked to enter credentials for the account that you can create on &lt;code&gt;https://alusus.net&lt;/code&gt;, select payment options and accept terms of service, then the publishing will start and your app will be available online within minutes under a subdomain of &lt;code&gt;alusus.net&lt;/code&gt;, with HTTPS automatically enabled. In order to let the app connect to the DB within the container you'll need to connect using the system user rather than a Postgres user, which means setting your DB connection params as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DB name: &quot;root&quot;&lt;/li&gt;
&lt;li&gt;DB user: &quot;root&quot;&lt;/li&gt;
&lt;li&gt;DB password: &quot;&quot;&lt;/li&gt;
&lt;li&gt;DB host: &quot;&quot;&lt;/li&gt;
&lt;li&gt;DB port: 0&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can either update the code so that it defaults to these values, or keep it as is and set these values as env vars from the Alusus Net dashboard, which is the recommended approach. Notice that Alusus Net won't let you add an env var with a value of &quot;&quot;, so instead use a space string, i.e. &quot; &quot; and this will be trimmed in the &lt;code&gt;getDbDriver&lt;/code&gt; function. Once you set these env vars in the dashboard make sure to restart the container to let the app pick those new values. The container will take 15 seconds or so to start up after which the app will be ready for use and you can now create rooms and chat with your friends across the world.&lt;/p&gt;
&lt;p&gt;You can visit this page to view the final source code of our app: &lt;a rel=&quot;nofollow&quot; href=&quot;https://encommunity.alusus.org/t/building-a-chat-web-app-step-5-deployment/21&quot;&gt;https://encommunity.alusus.org/t/building-a-chat-web-app-step-5-deployment/21&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Stay tuned for upcoming articles where I will be explaining in more details what happens behind the scenes in this app, and what makes Alusus programming language a truely unique and promising programming language.&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/11268/building-a-wasm-chat-app-using-alusus-step-5-deployment</guid>
<pubDate>Sun, 08 Feb 2026 19:17:10 +0000</pubDate>
</item>
<item>
<title>Building a WASM Chat App using Alusus - Step 4: Adding Users and Rooms</title>
<link>https://coderlegion.com/11116/building-a-wasm-chat-app-using-alusus-step-4-adding-users-and-rooms</link>
<description>&lt;p&gt;In the &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/11049/building-a-wasm-chat-app-using-alusus-step-3-persisting-the-data&quot;&gt;previous article&lt;/a&gt; we built a fully functioning chat application, but with no user authentication and with only one global room. It's now time to authenticate users and allow creating separate rooms. This will require updating the data model. We will add two more models, &lt;code&gt;RoomModel&lt;/code&gt; and &lt;code&gt;UserModel&lt;/code&gt;. Each of these models has a name (room name or username) and a password field. To avoid complexity we won't do any form of extra security measures needed in production apps and will stick with just password based authentication and will also just store the password in plain text, which is not a valid security method for production apps but is enough for this tutorial. Our MessageModel will also have two foriegn keys, username and roomName. The data model will now look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;@model[&quot;rooms&quot;, 1]
class RoomModel {
    Rows.define_model_essentials[];

    @VarChar[&quot;256&quot;]
    @primaryKey
    @column
    def name: String;

    @VarChar[&quot;1024&quot;]
    @notNull
    @column
    def password: String;
}

@model[&quot;users&quot;, 1]
class UserModel {
    Rows.define_model_essentials[];

    @VarChar[&quot;256&quot;]
    @primaryKey
    @column
    def username: String;

    @VarChar[&quot;1024&quot;]
    @notNull
    @column
    def password: String;
}

@model[&quot;messages&quot;, 2]
class MessageModel {
    Rows.define_model_essentials[];

    @foreignKey[&quot;rooms&quot;, &quot;name&quot;]
    @VarChar[&quot;256&quot;]
    @notNull
    @column[&quot;room_name&quot;]
    def roomName: String;

    @foreignKey[&quot;users&quot;, &quot;username&quot;]
    @VarChar[&quot;256&quot;]
    @notNull
    @column
    def username: String;

    @VarChar[&quot;1024&quot;]
    @notNull
    @column
    def message: String;

    @BigInteger
    @notNull
    @column[&quot;creation_date&quot;]
    def creationDate: Int[64];
    
    @migration[1, 2, { RoomModel: 1; UserModel: 1 }]
    function migrateToV2(db: ref[Rows.Db]): SrdRef[Error] {
        db.exec(&quot;alter table messages add column room_name varchar(256) ...&quot;);
        db.exec(&quot;alter table messages add column username varchar(256) ...&quot;);
        return SrdRef[Error]();
    }
}

def schema: {
    RoomModel,
    UserModel,
    MessageModel
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that MessageModel now has a version of 2 and includes the &lt;code&gt;migrateToV2&lt;/code&gt; function. This is only needed if you want to upgrade the previous version to the new version. In this case it's just a tutorial so you can remove this function and just start with a fresh DB.&lt;/p&gt;
&lt;p&gt;Our endpoints now will need to deal with multiple values in each request (username, room name, etc) so we'll need to use JSON request bodies rather than plain text as we did so far. Let's import the JSON library at the beginning of our app:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;Apm.importFile(&quot;Alusus/Json&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also need to have an endpoint to validate the credentials. To keep the app simple we will just have a single login endponit where you enter your username, password, room name, and room password. If the user does not exist, it will be created with the given password. If it does exist, then we'll just the password against the existing user and reject if the password doesn't match. We will do the same for room name. Our &lt;code&gt;POST /validate-creds&lt;/code&gt; endpoint looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;@beEndpoint[&quot;POST&quot;, &quot;/validate-creds&quot;]
func validateCreds (conn: ptr[Http.Connection]) {
    def postData: array[Char, 1024];
    def postDataSize: Int = Http.read(conn, postData~ptr, postData~size);
    def body: Json = String(postData~ptr, postDataSize);
    def roomName: String = body(&quot;roomName&quot;)~cast[String].trim();
    def roomPassword: String = body(&quot;roomPassword&quot;);
    def userName: String = body(&quot;userName&quot;)~cast[String].trim();
    def userPassword: String = body(&quot;userPassword&quot;);
    // Verify or create user.
    def users: Array[SrdRef[UserModel]] = db.from[UserModel].where[username == userName].select();
    if users.getLength() == 0 {
        db.save[UserModel](UserModel().{
            username = userName;
            password = userPassword;
        });
    } else if users(0).password != userPassword {
        Http.print(conn, &quot;HTTP/1.1 401 Unauthorized\r\n\r\n&quot;);
        return;
    }
    // Verify or create room.
    def rooms: Array[SrdRef[RoomModel]] = db.from[RoomModel].where[name == roomName].select();
    if rooms.getLength() == 0 {
        db.save[RoomModel](RoomModel().{
            name = roomName;
            password = roomPassword;
        });
    } else if rooms(0).password != roomPassword {
        Http.print(conn, &quot;HTTP/1.1 401 Unauthorized\r\n\r\n&quot;);
        return;
    }
    // Successful
    Http.print(conn, &quot;HTTP/1.1 200 Ok\r\n\r\n&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;/validate-creds&lt;/code&gt; endpoint will be used by a login page that will validate the entered creds before taking the user to the chat page. The endoints that receive and return messages will also need to be updated to include the credentials in the body since we aren't creating any auth tokens. These will be updated in a similar way to the &lt;code&gt;/validate-creds&lt;/code&gt; endpoint, so we won't list the code here to keep the article a bit shorter. The full final code for the entire app will be listed below.&lt;/p&gt;
&lt;p&gt;In the front end we will define the following global variables:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;def userName: String;
def userPassword: String;
def roomName: String;
def roomPassword: String;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is so that we keep the creds entered by user in the login screen to be used later on during the chat. We will have a new function called &lt;code&gt;login&lt;/code&gt; that will be responsible for creating the login form and waiting until the user has entered the creds and the creds have been validated by the backend. The function looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;func login {
    def success: SrdRef[Promise[Int]] = Promise[Int].new();
    def notification: SrdRef[Text];
    def roomName: SrdRef[Input];
    def roomPassword: SrdRef[Input];
    def userName: SrdRef[Input];
    def userPassword: SrdRef[Input];

    Window.instance.setView(Box().{
        style.{
            height = Length.percent(100);
            height = Length.percent(100);
            display = Display.FLEX;
            layout = Layout.COLUMN;
            justify = Justify.CENTER;
            align = Align.CENTER;
        };
        addChildren({
            Text().{
                notification = this;
                style.{
                    margin = Length4.px(10);
                    fontColor = Color(&quot;f00&quot;);
                };
            },
            Input().{
                userName = this;
                style.margin = Length4.px(10);
                placeholder = String(&quot;Username&quot;);
            },
            Input().{
                userPassword = this;
                style.margin = Length4.px(10);
                placeholder = String(&quot;Password&quot;);
            },
            Input().{
                roomName = this;
                style.margin = Length4.px(10);
                placeholder = String(&quot;Room Name&quot;);
            },
            Input().{
                roomPassword = this;
                style.margin = Length4.px(10);
                placeholder = String(&quot;Room Secret&quot;);
            },
            Button(String(&quot;Submit&quot;)).{
                style.margin = Length4.px(10);
                onClick.connect(closure (widget: ref[Widget], payload: ref[Int]) {
                    notification.setText(String());
                    def json: StringBuilder[JsonStringBuilderMixin](1024, 1025);
                    json.format(
                        &quot;{ \&quot;userName\&quot;: %js, \&quot;userPassword\&quot;: %js, \&quot;roomName\&quot;: %js, \&quot;roomPassword\&quot;: %js }&quot;,
                        userName.getText().trim(),
                        userPassword.getText(),
                        roomName.getText().trim(),
                        roomPassword.getText()
                    );
                    sendRequest(
                        &quot;POST&quot;, &quot;/validate-creds&quot;, &quot;Content-Type: application/text&quot;, json, 10000,
                        closure (json: Json) {
                            def status: Int = json(&quot;eventData&quot;)(&quot;status&quot;);
                            if status == 200 {
                                Root.userName = userName.getText().trim();
                                Root.userPassword = userPassword.getText();
                                Root.roomName = roomName.getText().trim();
                                Root.roomPassword = roomPassword.getText();
                                success.resolve(0);
                            } else {
                                notification.setText(String(&quot;Login failed!&quot;));
                            }
                        }
                    );
                });
            }
        });
    });

    await(success);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Overall the code is quite similar to the code of the chat screen, but the thing to notice here is the &lt;code&gt;success&lt;/code&gt; promise instantiated at the beginning. At the end this promise is awaited by the &lt;code&gt;await&lt;/code&gt; function, which transfers control to the message loop and waits there until the promise is resolved before returning.&lt;/p&gt;
&lt;p&gt;The code of the UI's entry point is similar to how it was, except that it now calls &lt;code&gt;login&lt;/code&gt; upfront, and that the body of the HTTP requests are now in JSON format and includes the credentials. JSON bodies are constructed like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;def getMessagesJson: String = StringBuilder[JsonStringBuilderMixin](1024, 1025).{
    format(
        &quot;{ \&quot;userName\&quot;: %js, \&quot;userPassword\&quot;: %js, \&quot;roomName\&quot;: %js, \&quot;roomPassword\&quot;: %js }&quot;,
        userName, userPassword, roomName, roomPassword
    );
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's it; we now have a chat app with users and rooms secured by passwords. CoderLegion has a small limit on the size of the article, so I can't list the entire source code here now that the app has grown a bit, but you can view it here: &lt;a rel=&quot;nofollow&quot; href=&quot;https://encommunity.alusus.org/t/building-a-chat-web-app-step-4-adding-users-and-rooms/20&quot;&gt;https://encommunity.alusus.org/t/building-a-chat-web-app-step-4-adding-users-and-rooms/20&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the next article we'll explain how to deploy this simple app and have it available to everyone over the internet.&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/11116/building-a-wasm-chat-app-using-alusus-step-4-adding-users-and-rooms</guid>
<pubDate>Thu, 05 Feb 2026 08:11:44 +0000</pubDate>
</item>
<item>
<title>Building a WASM Chat App using Alusus - Step 3: Persisting the Data</title>
<link>https://coderlegion.com/11049/building-a-wasm-chat-app-using-alusus-step-3-persisting-the-data</link>
<description>&lt;p&gt;In the &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/10972/building-a-wasm-chat-app-using-alusus-step-2-adding-a-backend&quot;&gt;previous article&lt;/a&gt; we added a simple back end that kept the data in memory only. In this article we will be persisting the data to a PostgreSQL database.&lt;/p&gt;
&lt;p&gt;We are still keeping all messages in a single pool, so we only need a single data model to store those messages. We will store the messages in a PostgreSQL database. After setting up Postgres you need to import the &lt;code&gt;Rows&lt;/code&gt; ORM library, and this can be done by adding this line nexto the where we are importing WebPlatform:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;Apm.importFile(&quot;Alusus/Rows&quot;, { &quot;Rows.alusus&quot;, &quot;Drivers/Postgresql.alusus&quot; });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that we are telling Alusus Package Manager to import the Postgresql driver in addition to the Rows library itself. This driver is part of the Rows library but is not imported by default in case the user wants to use a different database (MySQL for example) instead of Postgres.&lt;/p&gt;
&lt;p&gt;Now that we have our ORM ready, let's build our persistence layer. Again, we can keep everything in the same file, &lt;code&gt;chat.alusus&lt;/code&gt;. Our persistence layer looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;//==============================================================================
// Database

macro envVarOrDefault[varName, defaultVal] CharsPtr().{
    this = System.getEnv(varName);
    if this == 0 this = defaultVal;
}

@model[&quot;messages&quot;, 1]
class MessageModel {
    Rows.define_model_essentials[];

    @VarChar[&quot;1024&quot;]
    @notNull
    @column
    def message: String;

    @BigInteger
    @notNull
    @column[&quot;creation_date&quot;]
    def creationDate: Int[64];
}

def db: Rows.Db;
def schema: {
    MessageModel
};

function initializeDatabase {
    db.init(closure(d: ref[SrdRef[Rows.Driver]]) {
        d = getDbDriver();
    });
    db.schemaBuilder[schema].migrate();
}

function getDbDriver(): SrdRef[Rows.Driver] {
    def driver: SrdRef[Rows.Driver] = Rows.PostgresqlDriver(Rows.ConnectionParams().{
        dbName = String(envVarOrDefault[&quot;CHAT_DB_NAME&quot;, &quot;alusus&quot;]).trim();
        userName = String(envVarOrDefault[&quot;CHAT_DB_USERNAME&quot;, &quot;alusus&quot;]).trim();
        password = String(envVarOrDefault[&quot;CHAT_DB_PASSWORD&quot;, &quot;alusus&quot;]).trim();
        host = String(envVarOrDefault[&quot;CHAT_DB_HOST&quot;, &quot;0.0.0.0&quot;]).trim();
        port = String.parseInt(envVarOrDefault[&quot;CHAT_DB_PORT&quot;, &quot;5432&quot;]);
    });
    if !driver.isConnected() {
        System.fail(1, String(&quot;Error connecting to DB: &quot;) + driver.getLastError());
    }
    return driver;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our model class is called &lt;code&gt;MessageModel&lt;/code&gt; and it has two fields: &lt;code&gt;message&lt;/code&gt; and &lt;code&gt;creationDate&lt;/code&gt;. The &lt;code&gt;getDbDriver&lt;/code&gt; function instantiates the Postgres driver with the right connection params. The &lt;code&gt;initializeDatabase&lt;/code&gt; initializes the driver and also calls the &lt;code&gt;migrate&lt;/code&gt; function of the schema builder to make sure the table is created.&lt;/p&gt;
&lt;p&gt;With our data model ready, it's time to use it in the backend endpoints. We will no longer need the global array. Instead, we'll write the data to the database. We also won't need the mutex since the database engine internally handles synchronization. The backend will now look like the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;//==============================================================================
// Backend

def MAX_MESSAGES: 12;

@beEndpoint[&quot;POST&quot;, &quot;/messages&quot;]
func postMessages (conn: ptr[Http.Connection]) {
    def postData: array[Char, 1024];
    def postDataSize: Int = Http.read(conn, postData~ptr, postData~size);
    def msg: MessageModel;
    msg.message = String(postData~ptr, postDataSize);
    msg.creationDate = Time.getTimestamp(0);
    db.save[MessageModel](msg);
    Http.print(conn, &quot;HTTP/1.1 200 Ok\r\n\r\n&quot;);
}

@beEndpoint[&quot;GET&quot;, &quot;/messages&quot;]
func getMessages (conn: ptr[Http.Connection]) {
    // Fetch messages from DB.
    def messages: Array[SrdRef[MessageModel]] = db
        .from[MessageModel]
        .order[-creationDate]
        .select()
        .slice(0, MAX_MESSAGES);
    def response: StringBuilder(1024 * 10, 1024 * 5);
    def i: Int;
    for i = messages.getLength() - 1, i &amp;gt;= 0, --i {
        if i != messages.getLength() - 1 response += &quot;\n&quot;;
        response += &quot;- &quot;;
        response += messages(i).message;
    }
    // Delete older messages.
    if messages.getLength() &amp;gt; 0 {
        db.from[MessageModel].where[creationDate &amp;lt; messages(messages.getLength() - 1).creationDate].delete();
    }
    // Send response.
    Http.print(conn, &quot;HTTP/1.1 200 Ok\r\n&quot;);
    Http.print(conn, &quot;Content-Type: text/plain\r\n&quot;);
    Http.print(conn, &quot;Cache-Control: no-cache\r\n&quot;);
    Http.print(conn, &quot;Content-Length: %d\r\n\r\n&quot;, response.getLength());
    Http.print(conn, response);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The main difference from the prev version is in how we save and load the data. We now save the data by instantiating a MessageModel object, set its member vars with the right values, then call &lt;code&gt;db.save[MessageModel](msg)&lt;/code&gt;. Loading the data is done using the &lt;code&gt;db.from[MessageModel].order[-creationDate].select()&lt;/code&gt; statement, which loads it ordered descendingly by the creation date. For this example we continue to limit the messages to 12, but this limit can be changed or removed altogether. Deleting the old records is done by the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;db.from[MessageModel].where[creationDate &amp;lt; messages(messages.getLength() - 1).creationDate].delete();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which deletes all the messages that have a creation date older than the 12th message in the loaded data. Now the full source code looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import &quot;Srl/String&quot;;
import &quot;Srl/Time&quot;;
import &quot;Apm&quot;;
Apm.importFile(&quot;Alusus/Rows&quot;, { &quot;Rows.alusus&quot;, &quot;Drivers/Postgresql.alusus&quot; });
Apm.importFile(&quot;Alusus/WebPlatform&quot;);
use Srl;
use WebPlatform;

//==============================================================================
// Database

macro envVarOrDefault[varName, defaultVal] CharsPtr().{
    this = System.getEnv(varName);
    if this == 0 this = defaultVal;
}

@model[&quot;messages&quot;, 1]
class MessageModel {
    Rows.define_model_essentials[];

    @VarChar[&quot;1024&quot;]
    @notNull
    @column
    def message: String;

    @BigInteger
    @notNull
    @column[&quot;creation_date&quot;]
    def creationDate: Int[64];
}

def db: Rows.Db;
def schema: {
    MessageModel
};

function initializeDatabase {
    db.init(closure(d: ref[SrdRef[Rows.Driver]]) {
        d = getDbDriver();
    });
    db.schemaBuilder[schema].migrate();
}

function getDbDriver(): SrdRef[Rows.Driver] {
    def driver: SrdRef[Rows.Driver] = Rows.PostgresqlDriver(Rows.ConnectionParams().{
        dbName = envVarOrDefault[&quot;CHAT_DB_NAME&quot;, &quot;alusus&quot;];
        userName = envVarOrDefault[&quot;CHAT_DB_USERNAME&quot;, &quot;alusus&quot;];
        password = envVarOrDefault[&quot;CHAT_DB_PASSWORD&quot;, &quot;alusus&quot;];;
        host = envVarOrDefault[&quot;CHAT_DB_HOST&quot;, &quot;0.0.0.0&quot;];
        port = String.parseInt(envVarOrDefault[&quot;CHAT_DB_PORT&quot;, &quot;5432&quot;]);
    });
    if !driver.isConnected() {
        System.fail(1, String(&quot;Error connecting to DB: &quot;) + driver.getLastError());
    }
    return driver;
}

//==============================================================================
// Backend

def MAX_MESSAGES: 12;

@beEndpoint[&quot;POST&quot;, &quot;/messages&quot;]
func postMessages (conn: ptr[Http.Connection]) {
    def postData: array[Char, 1024];
    def postDataSize: Int = Http.read(conn, postData~ptr, postData~size);
    def msg: MessageModel;
    msg.message = String(postData~ptr, postDataSize);
    msg.creationDate = Time.getTimestamp(0);
    db.save[MessageModel](msg);
    Http.print(conn, &quot;HTTP/1.1 200 Ok\r\n\r\n&quot;);
}

@beEndpoint[&quot;GET&quot;, &quot;/messages&quot;]
func getMessages (conn: ptr[Http.Connection]) {
    // Fetch messages from DB.
    def messages: Array[SrdRef[MessageModel]] = db
        .from[MessageModel]
        .order[-creationDate]
        .select()
        .slice(0, MAX_MESSAGES);
    def response: StringBuilder(1024 * 10, 1024 * 5);
    def i: Int;
    for i = messages.getLength() - 1, i &amp;gt;= 0, --i {
        if i != messages.getLength() - 1 response += &quot;\n&quot;;
        response += &quot;- &quot;;
        response += messages(i).message;
    }
    // Delete older messages.
    if messages.getLength() &amp;gt; 0 {
        db.from[MessageModel].where[creationDate &amp;lt; messages(messages.getLength() - 1).creationDate].delete();
    }
    // Send response.
    Http.print(conn, &quot;HTTP/1.1 200 Ok\r\n&quot;);
    Http.print(conn, &quot;Content-Type: text/plain\r\n&quot;);
    Http.print(conn, &quot;Cache-Control: no-cache\r\n&quot;);
    Http.print(conn, &quot;Content-Length: %d\r\n\r\n&quot;, response.getLength());
    Http.print(conn, response);
}

//==============================================================================
// Frontend Components

class Header {
    @injection def component: Component;

    handler this~init() {
        this.view = Box({}).{
            style.{
                padding = Length4.pt(4);
                borderWidth = Length4.pt(0, 0, 1.5, 0);
                borderStyle = BorderStyle.SOLID;
                borderColor = Color(&quot;000&quot;);
                justify = Justify.START;
                display = Display.FLEX;
                layout = Layout.COLUMN;
                background = Background(Color(&quot;fff&quot;));
            };
            addChildren({
                Text(String(&quot;Wasm Chat&quot;)).{
                    style.fontSize = Length.pt(18.0);
                }
            });
        };
    }

    handler this_type(): SrdRef[Header] {
        return SrdRef[Header].construct();
    }
}

class TextEntry {
    @injection def component: Component;

    def onNewEntry: closure (String);
    def textInput: SrdRef[TextInput];

    handler this~init() {
        def self: ref[this_type](this);

        this.view = Box({}).{
            style.{
                display = Display.FLEX;
                layout = Layout.ROW;
                justify = Justify.SPACE_BETWEEN;
            };
            addChildren({
                TextInput().{
                    self.textInput = this;
                    style.{
                        flex = Flex(1);
                        margin = Length4.px(10);
                        height = Length.px(50);
                        fontSize = Length.pt(12.0);
                        borderStyle = BorderStyle.SOLID;
                        borderColor = Color(&quot;000&quot;);
                        borderWidth = Length4.px(1.5);
                    };
                    onKeyUp.connect(closure (widget: ref[TextInput], payload: ref[String]) {
                        if payload == &quot;Shift+Enter&quot; {
                            def newData: String = widget.getText().trim();
                            widget.setText(String());
                            if not self.onNewEntry.isNull() self.onNewEntry(newData);
                        }
                    });
                },
                Button(String(&quot;Send&quot;)).{
                    style.{
                        height = Length.px(50);
                        width = Length.px(50);
                        fontSize = Length.px(16.0);
                        justify = Justify.CENTER;
                        margin = Length4.px(10, 10, 10, 0);
                    };
                    onClick.connect(closure (widget: ref[Widget], payload: ref[Int]) {
                        def newData: String = self.textInput.getText().trim();
                        self.textInput.setText(String());
                        if not self.onNewEntry.isNull() self.onNewEntry(newData);
                    });
                }
            });
        };
    }

    handler this_type(): SrdRef[TextEntry] {
        return SrdRef[TextEntry].construct();
    }
}

//==============================================================================
// Frontend Pages

@uiEndpoint[&quot;/&quot;]
@title[&quot;Wasm Chat&quot;]
func main {
    def onFetch: closure (json: Json);

    Window.instance.style.{
        padding = Length4.pt(0);
        margin = Length4.pt(0);
    };
    Window.instance.setView(Box({}).{
        style.{
            height = Length.percent(100);
            justify = Justify.SPACE_BETWEEN;
            display = Display.FLEX;
            layout = Layout.COLUMN;
            background = Background(Color(&quot;aaa&quot;));
        };
        addChildren({
            Header(),
            Text(String()).{
                style.{
                    flex = Flex(1);
                    width = Length.percent(100) - Length.px(20);
                    margin = Length4.px(10, 10, 0, 10);
                    fontSize = Length.pt(20.0);
                    borderStyle = BorderStyle.SOLID;
                    borderColor = Color(&quot;000&quot;);
                    borderWidth = Length4.px(1.5);
                    background = Background(Color(&quot;fff&quot;));
                };
                onFetch = closure (json: Json) {
                    def status: Int[64] = json(&quot;eventData&quot;)(&quot;status&quot;);
                    if status &amp;gt;= 200 and status &amp;lt; 300 {
                        def data: String = json(&quot;eventData&quot;)(&quot;body&quot;);
                        if this.getText() != data {
                            this.setText(data);
                        }
                    }
                };
            },
            TextEntry().{
                onNewEntry = closure (newData: String) {
                    sendRequest(
                        &quot;POST&quot;, &quot;/messages&quot;, &quot;Content-Type: application/text&quot;, newData, 10000,
                        closure (Json) {
                            sendRequest(&quot;GET&quot;, &quot;/messages&quot;, null, null, 500, onFetch);
                        }
                    );
                };
            };
        })
    });

    startTimer(500000, closure (json: Json) {
        sendRequest(&quot;GET&quot;, &quot;/messages&quot;, null, null, 500, onFetch);
    });
    sendRequest(&quot;GET&quot;, &quot;/messages&quot;, null, null, 500, onFetch);

    runEventLoop();
}

//==============================================================================
// Entry Point

initializeDatabase();
Console.print(&quot;Starting server on port 8000...\nURL: http://localhost:8000/\n&quot;);
buildAndRunServer(Array[CharsPtr]({ &quot;listening_ports&quot;, &quot;8000&quot;, &quot;static_file_max_age&quot;, &quot;0&quot; }));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's it, the app is ready to save messages. You just need to make sure a Postgres server is running and the right connection params set in the code, then you can run the code. In the &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/11116/building-a-wasm-chat-app-using-alusus-step-4-adding-users-and-rooms&quot;&gt;next article&lt;/a&gt; we will be adding support for users and rooms.&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/11049/building-a-wasm-chat-app-using-alusus-step-3-persisting-the-data</guid>
<pubDate>Wed, 04 Feb 2026 07:58:52 +0000</pubDate>
</item>
<item>
<title>Building a WASM Chat App using Alusus - Step 2: Adding a Backend</title>
<link>https://coderlegion.com/10972/building-a-wasm-chat-app-using-alusus-step-2-adding-a-backend</link>
<description>&lt;p&gt;Continuing the app we built in the &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/10923/building-a-wasm-chat-app-using-alusus-step-1-the-ui&quot;&gt;previous article&lt;/a&gt;, we will now add a back end to enable chatting between multiple front ends.&lt;/p&gt;
&lt;p&gt;Let's keep it simple for the first step and have the backend store the chat data in memory. We'll build two endpoints, one for posting a new message, and one for fetching the message history. We will skip the rooms for now and keep all messages in one global publicly available pool of messages, so everyone will see all messages from everyone. The messages for now will simply be stored in a global array of strings.&lt;/p&gt;
&lt;p&gt;It's important to keep in mind that Alusus and WebPlatform are multi-threaded, so global variables will need to be thread safe. So, we will use a mutex to guard the access to this global array. Our backed will look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;//==============================================================================
// Backend

def MAX_MESSAGES: 12;
def messages: Array[String];
def mutex: Threading.Mutex;
Threading.initMutex(mutex~ptr, 0);

@beEndpoint[&quot;POST&quot;, &quot;/messages&quot;]
func postMessages (conn: ptr[Http.Connection]) {
    def postData: array[Char, 1024];
    def postDataSize: Int = Http.read(conn, postData~ptr, postData~size);
    Threading.lockMutex(mutex~ptr);
    if messages.getLength() &amp;gt;= MAX_MESSAGES messages.remove(0);
    messages.add(String(postData~ptr, postDataSize));
    Threading.unlockMutex(mutex~ptr);
    Http.print(conn, &quot;HTTP/1.1 200 Ok\r\n\r\n&quot;);
}

@beEndpoint[&quot;GET&quot;, &quot;/messages&quot;]
func getMessages (conn: ptr[Http.Connection]) {
    Threading.lockMutex(mutex~ptr);
    def response: String = String.merge(messages, &quot;\n&quot;);
    Threading.unlockMutex(mutex~ptr);
    Http.print(conn, &quot;HTTP/1.1 200 Ok\r\n&quot;);
    Http.print(conn, &quot;Content-Type: text/plain\r\n&quot;);
    Http.print(conn, &quot;Cache-Control: no-cache\r\n&quot;);
    Http.print(conn, &quot;Content-Length: %d\r\n\r\n&quot;, response.getLength());
    Http.print(conn, response.buf);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each REST endpoint is a function marked with the &lt;code&gt;@beEndpoint&lt;/code&gt; modifier, giving it the method and the route for that endpoint. You'll see that in each of these two endpoints we lock the mutex before accessing the global &lt;code&gt;messages&lt;/code&gt; variable, then we unlock it afterwards.&lt;/p&gt;
&lt;p&gt;You can put this backend code in a separate file and then import it from &lt;code&gt;chat.alusus&lt;/code&gt;, and you can also keep inside &lt;code&gt;chat.alusus&lt;/code&gt;. In Alusus, you do not have to separate the backend from the front end, nor do you need to run them separately; WebPlatform takes care of all of that for you.&lt;/p&gt;
&lt;p&gt;Now that we have the backend, we need to hook the frontend to it. We have a text entry component that isn't listening to any events, so let's listen to the button click and key-up events and notify the owner:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;class TextEntry {
    @injection def component: Component;

    def onNewEntry: closure (String);
    def textInput: SrdRef[TextInput];

    handler this~init() {
        def self: ref[this_type](this);

        this.view = Box({}).{
            style.{
                display = Display.FLEX;
                layout = Layout.ROW;
                justify = Justify.SPACE_BETWEEN;
            };
            addChildren({
                TextInput().{
                    self.textInput = this;
                    style.{
                        flex = Flex(1);
                        margin = Length4.px(10);
                        height = Length.px(50);
                        fontSize = Length.pt(12.0);
                        borderStyle = BorderStyle.SOLID;
                        borderColor = Color(&quot;000&quot;);
                        borderWidth = Length4.px(1.5);
                    };
                    onKeyUp.connect(closure (widget: ref[TextInput], payload: ref[String]) {
                        if payload == &quot;Shift+Enter&quot; {
                            def newData: String = String(&quot;- &quot;) + widget.getText().trim();
                            widget.setText(String());
                            if not self.onNewEntry.isNull() self.onNewEntry(newData);
                        }
                    });
                },
                Button(String(&quot;Send&quot;)).{
                    style.{
                        height = Length.px(50);
                        width = Length.px(50);
                        fontSize = Length.px(16.0);
                        justify = Justify.CENTER;
                        margin = Length4.px(10, 10, 10, 0);
                    };
                    onClick.connect(closure (widget: ref[Widget], payload: ref[Int]) {
                        def newData: String = String(&quot;- &quot;) + self.textInput.getText().trim();
                        self.textInput.setText(String());
                        if not self.onNewEntry.isNull() self.onNewEntry(newData);
                    });
                }
            });
        };
    }

    handler this_type(): SrdRef[TextEntry] {
        return SrdRef[TextEntry].construct();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the owner of the component just needs to provide the onNewEntry callback. In the UI endpoint we will do two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Listen to new entries and send them over to the backend.&lt;/li&gt;
&lt;li&gt;Periodically fetch the chat history from the backend and display it.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;@uiEndpoint[&quot;/&quot;]
@title[&quot;Wasm Chat&quot;]
func main {
    def onFetch: closure (json: Json);

    Window.instance.style.{
        padding = Length4.pt(0);
        margin = Length4.pt(0);
    };
    Window.instance.setView(Box({}).{
        style.{
            height = Length.percent(100);
            justify = Justify.SPACE_BETWEEN;
            display = Display.FLEX;
            layout = Layout.COLUMN;
            background = Background(Color(&quot;aaa&quot;));
        };
        addChildren({
            Header(),
            Text(String()).{
                style.{
                    flex = Flex(1);
                    width = Length.percent(100) - Length.px(20);
                    margin = Length4.px(10, 10, 0, 10);
                    fontSize = Length.pt(20.0);
                    borderStyle = BorderStyle.SOLID;
                    borderColor = Color(&quot;000&quot;);
                    borderWidth = Length4.px(1.5);
                    background = Background(Color(&quot;fff&quot;));
                };
                onFetch = closure (json: Json) {
                    def status: Int[64] = json(&quot;eventData&quot;)(&quot;status&quot;);
                    if status &amp;gt;= 200 and status &amp;lt; 300 {
                        def data: String = json(&quot;eventData&quot;)(&quot;body&quot;);
                        if this.getText() != data {
                            this.setText(data);
                        }
                    }
                };
            },
            TextEntry().{
                onNewEntry = closure (newData: String) {
                    sendRequest(
                        &quot;POST&quot;, &quot;/messages&quot;, &quot;Content-Type: application/text&quot;, newData, 10000,
                        closure (Json) {}
                    );
                    sendRequest(&quot;GET&quot;, &quot;/messages&quot;, null, null, 500, onFetch);
                };
            };
        })
    });

    startTimer(500000, closure (json: Json) {
        sendRequest(&quot;GET&quot;, &quot;/messages&quot;, null, null, 500, onFetch);
    });
    sendRequest(&quot;GET&quot;, &quot;/messages&quot;, null, null, 500, onFetch);

    runEventLoop();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The rest of the app looks the same, and running the app is done the same way as shown in the previous section. The full source code now looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import &quot;Srl/String&quot;;
import &quot;Apm&quot;;
Apm.importFile(&quot;Alusus/Threading&quot;);
Apm.importFile(&quot;Alusus/WebPlatform&quot;);
use Srl;
use WebPlatform;

//==============================================================================
// Backend

def MAX_MESSAGES: 12;
def messages: Array[String];
def mutex: Threading.Mutex;
Threading.initMutex(mutex~ptr, 0);

@beEndpoint[&quot;POST&quot;, &quot;/messages&quot;]
func postMessages (conn: ptr[Http.Connection]) {
    def postData: array[Char, 1024];
    def postDataSize: Int = Http.read(conn, postData~ptr, postData~size);
    Threading.lockMutex(mutex~ptr);
    if messages.getLength() &amp;gt;= MAX_MESSAGES messages.remove(0);
    messages.add(String(postData~ptr, postDataSize));
    Threading.unlockMutex(mutex~ptr);
    Http.print(conn, &quot;HTTP/1.1 200 Ok\r\n\r\n&quot;);
}

@beEndpoint[&quot;GET&quot;, &quot;/messages&quot;]
func getMessages (conn: ptr[Http.Connection]) {
    Threading.lockMutex(mutex~ptr);
    def response: String = String.merge(messages, &quot;\n&quot;);
    Threading.unlockMutex(mutex~ptr);
    Http.print(conn, &quot;HTTP/1.1 200 Ok\r\n&quot;);
    Http.print(conn, &quot;Content-Type: text/plain\r\n&quot;);
    Http.print(conn, &quot;Cache-Control: no-cache\r\n&quot;);
    Http.print(conn, &quot;Content-Length: %d\r\n\r\n&quot;, response.getLength());
    Http.print(conn, response.buf);
}


//==============================================================================
// Frontend Components

class Header {
    @injection def component: Component;

    handler this~init() {
        this.view = Box({}).{
            style.{
                padding = Length4.pt(4);
                borderWidth = Length4.pt(0, 0, 1.5, 0);
                borderStyle = BorderStyle.SOLID;
                borderColor = Color(&quot;000&quot;);
                justify = Justify.START;
                display = Display.FLEX;
                layout = Layout.COLUMN;
                background = Background(Color(&quot;fff&quot;));
            };
            addChildren({
                Text(String(&quot;Wasm Chat&quot;)).{
                    style.fontSize = Length.pt(18.0);
                }
            });
        };
    }

    handler this_type(): SrdRef[Header] {
        return SrdRef[Header].construct();
    }
}

class TextEntry {
    @injection def component: Component;

    def onNewEntry: closure (String);
    def textInput: SrdRef[TextInput];

    handler this~init() {
        def self: ref[this_type](this);

        this.view = Box({}).{
            style.{
                display = Display.FLEX;
                layout = Layout.ROW;
                justify = Justify.SPACE_BETWEEN;
            };
            addChildren({
                TextInput().{
                    self.textInput = this;
                    style.{
                        flex = Flex(1);
                        margin = Length4.px(10);
                        height = Length.px(50);
                        fontSize = Length.pt(12.0);
                        borderStyle = BorderStyle.SOLID;
                        borderColor = Color(&quot;000&quot;);
                        borderWidth = Length4.px(1.5);
                    };
                    onKeyUp.connect(closure (widget: ref[TextInput], payload: ref[String]) {
                        if payload == &quot;Shift+Enter&quot; {
                            def newData: String = String(&quot;- &quot;) + widget.getText().trim();
                            widget.setText(String());
                            if not self.onNewEntry.isNull() self.onNewEntry(newData);
                        }
                    });
                },
                Button(String(&quot;Send&quot;)).{
                    style.{
                        height = Length.px(50);
                        width = Length.px(50);
                        fontSize = Length.px(16.0);
                        justify = Justify.CENTER;
                        margin = Length4.px(10, 10, 10, 0);
                    };
                    onClick.connect(closure (widget: ref[Widget], payload: ref[Int]) {
                        def newData: String = String(&quot;- &quot;) + self.textInput.getText().trim();
                        self.textInput.setText(String());
                        if not self.onNewEntry.isNull() self.onNewEntry(newData);
                    });
                }
            });
        };
    }

    handler this_type(): SrdRef[TextEntry] {
        return SrdRef[TextEntry].construct();
    }
}

//==============================================================================
// Frontend Pages

@uiEndpoint[&quot;/&quot;]
@title[&quot;Wasm Chat&quot;]
func main {
    def onFetch: closure (json: Json);

    Window.instance.style.{
        padding = Length4.pt(0);
        margin = Length4.pt(0);
    };
    Window.instance.setView(Box({}).{
        style.{
            height = Length.percent(100);
            justify = Justify.SPACE_BETWEEN;
            display = Display.FLEX;
            layout = Layout.COLUMN;
            background = Background(Color(&quot;aaa&quot;));
        };
        addChildren({
            Header(),
            Text(String()).{
                style.{
                    flex = Flex(1);
                    width = Length.percent(100) - Length.px(20);
                    margin = Length4.px(10, 10, 0, 10);
                    fontSize = Length.pt(20.0);
                    borderStyle = BorderStyle.SOLID;
                    borderColor = Color(&quot;000&quot;);
                    borderWidth = Length4.px(1.5);
                    background = Background(Color(&quot;fff&quot;));
                };
                onFetch = closure (json: Json) {
                    def status: Int[64] = json(&quot;eventData&quot;)(&quot;status&quot;);
                    if status &amp;gt;= 200 and status &amp;lt; 300 {
                        def data: String = json(&quot;eventData&quot;)(&quot;body&quot;);
                        if this.getText() != data {
                            this.setText(data);
                        }
                    }
                };
            },
            TextEntry().{
                onNewEntry = closure (newData: String) {
                    sendRequest(
                        &quot;POST&quot;, &quot;/messages&quot;, &quot;Content-Type: application/text&quot;, newData, 10000,
                        closure (Json) {}
                    );
                    sendRequest(&quot;GET&quot;, &quot;/messages&quot;, null, null, 500, onFetch);
                };
            };
        })
    });

    startTimer(500000, closure (json: Json) {
        sendRequest(&quot;GET&quot;, &quot;/messages&quot;, null, null, 500, onFetch);
    });
    sendRequest(&quot;GET&quot;, &quot;/messages&quot;, null, null, 500, onFetch);

    runEventLoop();
}

//==============================================================================
// Entry Point

Console.print(&quot;Starting server on port 8000...\nURL: http://localhost:8000/\n&quot;);
buildAndRunServer(Array[CharsPtr]({ &quot;listening_ports&quot;, &quot;8000&quot;, &quot;static_file_max_age&quot;, &quot;0&quot; }));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can have multiple people chat over this simple chat application, but the data is not persisted yet, so if we restart the server all data will be gone. We will fix that in the &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/11049/building-a-wasm-chat-app-using-alusus-step-3-persisting-the-data&quot;&gt;next article&lt;/a&gt;.&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/10972/building-a-wasm-chat-app-using-alusus-step-2-adding-a-backend</guid>
<pubDate>Tue, 03 Feb 2026 07:14:04 +0000</pubDate>
</item>
<item>
<title>Building a WASM Chat App using Alusus - Step 1: The UI</title>
<link>https://coderlegion.com/10923/building-a-wasm-chat-app-using-alusus-step-1-the-ui</link>
<description>&lt;p&gt;WebAssembly is a relatively new technology that allows building native apps for the web, which can provide extra performance, lower memory footprint, and improved security over traditional JS based web applications. In this tutorial we'll walk through building a simple WebAssembly based application. The app is a simple text based chat application. For this tutorial we'll use &lt;a rel=&quot;nofollow&quot; href=&quot;https://alusus.org&quot;&gt;Alusus programming language&lt;/a&gt;, which provides good support for WebAssembly.&lt;/p&gt;
&lt;h3 id=&quot;requirements&quot;&gt;Requirements&lt;/h3&gt;
&lt;p&gt;We'll keep the requirement simple for the sake of this tutorial. When the user launches the app&lt;br&gt;
he/she should be presented with a dialog where you enter a username, a user password, a room name, and a room password. Once the user enters those info, we'll log the user into that room. Like any other chat app, the room has an entry box where the user enters a new message, and an area for showing chat messages from all members of that room.&lt;/p&gt;
&lt;p&gt;All you need in terms of development environment is to &lt;a rel=&quot;nofollow&quot; href=&quot;https://alusus.org/en/download.html&quot;&gt;install Alusus language&lt;/a&gt; in any modern linux distro, and have any text editor.&lt;/p&gt;
&lt;h3 id=&quot;basic-chat-frontend&quot;&gt;Basic Chat Frontend&lt;/h3&gt;
&lt;p&gt;We will start off by building a basic UI. With Alusus and &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Alusus/WebPlatform&quot;&gt;WebPlatform&lt;/a&gt; you build everything in Alusus itself without the need to embed any HTML or CSS; those will be generated for you by the compiler, so you build the app as if you are building a desktop application using some UI toolkit.&lt;/p&gt;
&lt;p&gt;We will create two separate components, one for the header and one for the text entry, and we will have one main endpoint that displays the UI. Here is how the full code looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import &quot;Srl/String&quot;;
import &quot;Apm&quot;;
Apm.importFile(&quot;Alusus/WebPlatform&quot;);
use Srl;
use WebPlatform;

//==============================================================================
// Frontend Components

class Header {
    @injection def component: Component;

    handler this~init() {
        this.view = Box({}).{
            style.{
                padding = Length4.pt(4);
                borderWidth = Length4.pt(0, 0, 1.5, 0);
                borderStyle = BorderStyle.SOLID;
                borderColor = Color(&quot;000&quot;);
                justify = Justify.START;
                display = Display.FLEX;
                layout = Layout.COLUMN;
                background = Background(Color(&quot;fff&quot;));
            };
            addChildren({
                Text(String(&quot;Wasm Chat&quot;)).{
                    style.fontSize = Length.pt(18.0);
                }
            });
        };
    }

    handler this_type(): SrdRef[Header] {
        return SrdRef[Header].construct();
    }
}

class TextEntry {
    @injection def component: Component;

    handler this~init() {
        def self: ref[this_type](this);

        this.view = Box({}).{
            style.{
                display = Display.FLEX;
                layout = Layout.ROW;
                justify = Justify.SPACE_BETWEEN;
            };
            addChildren({
                TextInput().{
                    style.{
                        flex = Flex(1);
                        margin = Length4.px(10);
                        height = Length.px(50);
                        fontSize = Length.pt(12.0);
                        borderStyle = BorderStyle.SOLID;
                        borderColor = Color(&quot;000&quot;);
                        borderWidth = Length4.px(1.5);
                    };
                },
                Button(String(&quot;Send&quot;)).{
                    style.{
                        height = Length.px(50);
                        width = Length.px(50);
                        fontSize = Length.px(16.0);
                        justify = Justify.CENTER;
                        margin = Length4.px(10, 10, 10, 0);
                    };
                }
            });
        };
    }

    handler this_type(): SrdRef[TextEntry] {
        return SrdRef[TextEntry].construct();
    }
}

//==============================================================================
// Frontend Pages

@uiEndpoint[&quot;/&quot;]
@title[&quot;Wasm Chat&quot;]
func main {
    Window.instance.style.{
        padding = Length4.pt(0);
        margin = Length4.pt(0);
    };
    Window.instance.setView(Box({}).{
        style.{
            height = Length.percent(100);
            justify = Justify.SPACE_BETWEEN;
            display = Display.FLEX;
            layout = Layout.COLUMN;
            background = Background(Color(&quot;aaa&quot;));
        };
        addChildren({
            Header(),
            Text(String()).{
                style.{
                    flex = Flex(1);
                    width = Length.percent(100) - Length.px(20);
                    margin = Length4.px(10, 10, 0, 10);
                    fontSize = Length.pt(20.0);
                    borderStyle = BorderStyle.SOLID;
                    borderColor = Color(&quot;000&quot;);
                    borderWidth = Length4.px(1.5);
                    background = Background(Color(&quot;fff&quot;));
                };
            },
            TextEntry()
        })
    });

    runEventLoop();
}

//==============================================================================
// Entry Point

Console.print(&quot;Starting server on port 8000...\nURL: http://localhost:8000/\n&quot;);
buildAndRunServer(Array[CharsPtr]({ &quot;listening_ports&quot;, &quot;8000&quot;, &quot;static_file_max_age&quot;, &quot;0&quot; }));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code itself is pretty self explanatory; the UI endpoint is a function marked with the &lt;code&gt;@uiEndpoint&lt;/code&gt; modifier. In this function we are doing two things: setting the main view then entering the event loop. The view is a &lt;code&gt;Box&lt;/code&gt;, which is equivalent to a &lt;code&gt;div&lt;/code&gt; in HTML, and in that box there is the header, a text view for viewing chat history, and the text entry component. The &lt;code&gt;Text&lt;/code&gt; and &lt;code&gt;Header&lt;/code&gt; components are simply classes that inherit from WebPlatform's &lt;code&gt;Component&lt;/code&gt; class and create the component's view in the constructor.&lt;br&gt;
If you are familiar with CSS you will recognize the styles we are assigning to each of the widgets. Each of these styles is equivalent to its HTML counterpart, but are defined in a strongly typed manner, and hence you'll see the use of enumerations and classes instead of plain strings.&lt;/p&gt;
&lt;p&gt;Put all that code in a single file called &lt;code&gt;chat.alusus&lt;/code&gt;, then run it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;alusus chat.alusus
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is how the page looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=16788105427697238339&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The UI currently does nothing as it's not linked to any backend yet. This is what we will be doing in the &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/10972/building-a-wasm-chat-app-using-alusus-step-2-adding-a-backend&quot;&gt;next article&lt;/a&gt;.&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/10923/building-a-wasm-chat-app-using-alusus-step-1-the-ui</guid>
<pubDate>Mon, 02 Feb 2026 08:49:09 +0000</pubDate>
</item>
<item>
<title>My journey from beginner to backend developer, sharing lessons, projects, and tips.</title>
<link>https://coderlegion.com/6452/my-journey-from-beginner-to-backend-developer-sharing-lessons-projects-and-tips</link>
<description>&lt;p&gt;&lt;strong&gt;Title:&lt;/strong&gt; &lt;em&gt;From Beginner to Backend Developer: My One-Year Journey&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;introduction&quot;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Hi, I’m &lt;strong&gt;The Duchess of Hackers&lt;/strong&gt;, a Full-Stack Developer and Digital Marketer. Almost a year ago, I started my journey into backend development alongside my studies in Marketing &amp;amp; IT. It’s been full of challenges, discoveries, and growth  here’s what I’ve learned.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;why-i-started-learning-backend&quot;&gt;&lt;strong&gt;Why I Started Learning Backend&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;I wanted to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Understand how applications work behind the scenes&lt;/li&gt;
&lt;li&gt;Build real-world projects&lt;/li&gt;
&lt;li&gt;Combine marketing and tech knowledge to create solutions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I started with Python, moved to Flask, and gradually explored databases, APIs, and backend logic.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;key-projects-and-lessons-learned&quot;&gt;&lt;strong&gt;Key Projects and Lessons Learned&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;One of my first projects was a &lt;strong&gt;simple ATM simulator in Python&lt;/strong&gt;. Through this, I learned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Basic programming logic and control flow&lt;/li&gt;
&lt;li&gt;How to structure a small project&lt;/li&gt;
&lt;li&gt;The importance of testing and debugging&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each small project helped me gain confidence and prepared me for bigger challenges.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;tools-and-resources-that-helped-me&quot;&gt;&lt;strong&gt;Tools and Resources That Helped Me&lt;/strong&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Languages &amp;amp; Frameworks:&lt;/strong&gt; Python, Flask&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Version Control:&lt;/strong&gt; Git &amp;amp; GitHub&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Development Environment:&lt;/strong&gt; VS Code, WSL/Ubuntu&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Learning Platforms:&lt;/strong&gt; Online tutorials, coding communities, and practice challenges&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Tip for beginners:&lt;/strong&gt; Break problems into small, manageable steps, practice consistently, and don’t be afraid to ask for help.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;biggest-takeaways&quot;&gt;&lt;strong&gt;Biggest Takeaways&lt;/strong&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Learning to code is a &lt;strong&gt;journey, not a race&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Community support and feedback are invaluable&lt;/li&gt;
&lt;li&gt;Document your projects and share your learning it helps both you and others&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;call-to-action&quot;&gt;&lt;strong&gt;Call to Action&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;I hope my journey inspires other beginners to start coding, build projects, and keep growing.&lt;/p&gt;
&lt;p&gt;If you’re starting your coding journey, I’d love to hear about your first project or challenge  share it in the comments or connect with me on &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/the_duchess_of_hackers&quot;&gt;dev.to&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/gracequaye1&quot;&gt;GitHub&lt;/a&gt;, or &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/grace-okailey-quaye&quot;&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Signature&lt;/strong&gt;&lt;br&gt;
Full-Stack Developer | Digital Marketer&lt;br&gt;
&lt;em&gt;The Duchess of Hackers&lt;/em&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/grace-okailey-quaye&quot;&gt;LinkedIn&lt;/a&gt; | &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/gracequaye1&quot;&gt;GitHub&lt;/a&gt; | &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/the_duchess_of_hackers&quot;&gt;dev.to&lt;/a&gt;&lt;/p&gt;
</description>
<category>Python Basics</category>
<guid isPermaLink="true">https://coderlegion.com/6452/my-journey-from-beginner-to-backend-developer-sharing-lessons-projects-and-tips</guid>
<pubDate>Tue, 14 Oct 2025 23:23:12 +0000</pubDate>
</item>
</channel>
</rss>