<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged thingsdb</title>
<link>https://coderlegion.com/tag/thingsdb</link>
<description>Powered by Question2Answer</description>
<item>
<title>Introducing Native UUIDs in ThingsDB</title>
<link>https://coderlegion.com/30409/introducing-native-uuids-in-thingsdb</link>
<description>&lt;p&gt;In the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/thingsdb/ThingsDB/releases/latest&quot;&gt;latest release&lt;/a&gt;, ThingsDB introduces native support for UUIDs alongside a powerful new typed dictionary pattern (&lt;code&gt;dict&amp;lt;uuid:Type&amp;gt;&lt;/code&gt;) that simplifies high-performance key-value lookups.&lt;/p&gt;
&lt;p&gt;While ThingsDB relies on auto-incrementing integer IDs under the hood for maximum internal efficiency, integers aren't always ideal for public application interfaces. Exposing sequential integer IDs can expose business metrics or open your API to enumeration attacks. Native UUID support solves this without sacrificing speed or developer experience.&lt;/p&gt;
&lt;h3 id=&quot;how-thingsdb-handles-uuids&quot;&gt;How ThingsDB Handles UUIDs&lt;/h3&gt;
&lt;p&gt;ThingsDB uses &lt;strong&gt;UUID version 7&lt;/strong&gt; for its native UUID implementation. Because UUID v7 embeds a Unix timestamp in its leading bits, generated IDs remain time-ordered &lt;em&gt;(chronologically sortable)&lt;/em&gt; while ensuring global uniqueness across your cluster:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sequential Efficiency:&lt;/strong&gt; Since UUIDs sort naturally by creation time, database indexes stay performant without the index fragmentation typically caused by purely random UUIDs &lt;em&gt;(like UUID v4)&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Guaranteed Uniqueness:&lt;/strong&gt; Generation accounts for node identification, precise millisecond timestamps, and cryptographically sound randomness, so no two nodes will ever generate colliding UUIDs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Internally, ThingsDB processes and stores UUIDs as efficient 128-bit integers. However, when interacting with the API or serializing data to JSON, ThingsDB exposes them as standard, lower-case hyphenated strings:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;&quot;00f3c1b0-7d12-7000-8b1e-213456789abc&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;building-a-user-management-system-with-uuids&quot;&gt;Building a User Management System with UUIDs&lt;/h3&gt;
&lt;p&gt;Let's walk through a practical example: creating a custom &lt;code&gt;User&lt;/code&gt; type that uses UUIDs instead of default ThingsDB integer IDs.&lt;/p&gt;
&lt;h4 id=&quot;1-define-the-user-type&quot;&gt;1. Define the User Type&lt;/h4&gt;
&lt;p&gt;We'll start by defining the &lt;code&gt;User&lt;/code&gt; type with two properties: a &lt;code&gt;uuid&lt;/code&gt; field named &lt;code&gt;id&lt;/code&gt; and a string &lt;code&gt;name&lt;/code&gt;. We'll also pass the &lt;code&gt;HID&lt;/code&gt; flag to hide ThingsDB's default internal integer ID from API responses.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// Define the User type.
// We use the `HID` flag to suppress the default internal integer ID.
set_type('User', {
    id: 'uuid',
    name: 'str',
}, HID);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;2-set-up-collection-storage&quot;&gt;2. Set Up Collection Storage&lt;/h4&gt;
&lt;p&gt;Instead of using a generic dictionary, we can define a dedicated root &lt;code&gt;App&lt;/code&gt; type to anchor our collection data. We'll use the new &lt;code&gt;dict&amp;lt;uuid:User&amp;gt;&lt;/code&gt; type constraint to ensure the dictionary map strictly pairs uuid keys with &lt;code&gt;User&lt;/code&gt; instances.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// Define the App root structure with a typed user lookup dictionary
set_type('App', {
    users: 'dict&amp;lt;uuid:User&amp;gt;',
}, HID);

// Convert the collection to the App type (automatically initializes `users`)
.to_type('App');
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;3-implement-procedures-for-crud-operations&quot;&gt;3. Implement Procedures for CRUD Operations&lt;/h4&gt;
&lt;p&gt;To maintain clean architecture, we'll write reusable procedures.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;By convention, using CamelCase for custom procedures helps distinguish them from built-in ThingsDB functions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h5 id=&quot;adding-users-newuser-&quot;&gt;Adding Users (&lt;code&gt;NewUser&lt;/code&gt;)&lt;/h5&gt;
&lt;p&gt;Creating a user is straightforward. ThingsDB automatically generates a valid UUID if one isn't explicitly provided in the payload:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;new_procedure('NewUser', |object| {
    user = User(object);
    .users[user.id] = user;
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To run this procedure, use the &lt;code&gt;!&lt;/code&gt; operator &lt;em&gt;(or &lt;code&gt;wse()&lt;/code&gt;)&lt;/em&gt; to tell ThingsDB that the call produces side effects:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;NewUser({
    name: 'Alice',
})!;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Response:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
    &quot;id&quot;: &quot;c9bf9e57-1685-4c89-bafb-ff5af830be8a&quot;,
    &quot;name&quot;: &quot;Alice&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you already have a UUID generated on the client side, you can pass it directly in the payload (&lt;code&gt;{id: &quot;...&quot;, name: &quot;Alice&quot;}&lt;/code&gt;), and ThingsDB will parse and store it automatically.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h5 id=&quot;fetching-users-getuserbyid-&quot;&gt;Fetching Users (&lt;code&gt;GetUserById&lt;/code&gt;)&lt;/h5&gt;
&lt;p&gt;While you can iterate through dictionary values, accessing users directly by key using the new &lt;code&gt;uuid()&lt;/code&gt; cast function offers O(1) fast lookup performance:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;new_procedure('GetUserById', |id| .users[uuid(id)]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, querying a user by string ID is clean and efficient:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;GetUserById('c9bf9e57-1685-4c89-bafb-ff5af830be8a');
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;calling-procedures-from-your-application&quot;&gt;Calling Procedures from Your Application&lt;/h3&gt;
&lt;p&gt;Once your procedures are defined, you can invoke them directly from your application using any native client library or standard HTTP requests.&lt;/p&gt;
&lt;h4 id=&quot;using-native-sdks-python-go-c-java-javascript-etc-&quot;&gt;Using Native SDKs (Python, Go, C#, Java, JavaScript etc.)&lt;/h4&gt;
&lt;p&gt;Native clients make procedure execution seamless. For example, using the &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.thingsdb.io/v1/connect/python/&quot;&gt;Python client&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;# Execute the GetUserById procedure using the native client
user = await client.run('GetUserById', 'c9bf9e57-1685-4c89-bafb-ff5af830be8a')
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;using-the-rest-api&quot;&gt;Using the REST API&lt;/h4&gt;
&lt;p&gt;ThingsDB also exposes procedure execution over HTTP POST requests, making it easy to integrate with any language or web service:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl --location --request POST 'http://&amp;lt;HOSTNAME:PORT&amp;gt;/collection/&amp;lt;COLLECTION_NAME&amp;gt;' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer &amp;lt;TOKEN&amp;gt;' \
  --data-raw '{
    &quot;type&quot;: &quot;run&quot;,
    &quot;name&quot;: &quot;GetUserById&quot;,
    &quot;args&quot;: [&quot;c9bf9e57-1685-4c89-bafb-ff5af830be8a&quot;]
  }'
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;With native 128-bit UUID support and typed key-value dictionaries, ThingsDB gives you the security and convenience of string UUIDs on the outside, paired with the storage performance of fast integer lookups on the inside.&lt;/p&gt;
&lt;p&gt;Give it a try in the latest release and let us know what you think!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30409/introducing-native-uuids-in-thingsdb</guid>
<pubDate>Fri, 09 Oct 2026 06:28:57 +0000</pubDate>
</item>
<item>
<title>Distributed Locking in Python</title>
<link>https://coderlegion.com/12077/distributed-locking-in-python</link>
<description>&lt;p&gt;When making use of Python's &lt;code&gt;asyncio&lt;/code&gt; library, synchronizing code within a single process is a solved problem. A simple &lt;code&gt;asyncio.Lock()&lt;/code&gt; ensures that coroutines play nice together. But as soon as your application scales to multiple containers, servers, or microservices, that local lock becomes invisible to the rest of your fleet.&lt;/p&gt;
&lt;p&gt;You suddenly face the challenge of &lt;strong&gt;Distributed Mutual Exclusion&lt;/strong&gt;: ensuring that a critical section of code is only executed by one worker at a time, regardless of which machine it is running on.&lt;/p&gt;
&lt;p&gt;In this post, we’ll explore a robust distributed lock implementation using &lt;a rel=&quot;nofollow&quot; href=&quot;https://thingsdb.io&quot;&gt;ThingsDB&lt;/a&gt;. This solution provides the familiar syntax of an asyncio context manager but leverages a globally synchronized backend to manage state across your entire infrastructure.&lt;/p&gt;
&lt;h3 id=&quot;why-distributed-locking-is-a-hard-problem&quot;&gt;Why Distributed Locking is a &quot;Hard&quot; Problem&lt;/h3&gt;
&lt;p&gt;On the surface, it sounds easy: just set a flag in a database. But in a distributed environment, the stakes are higher. If a worker acquires a lock and then crashes or loses network connectivity, that lock could remain &quot;held&quot; in the database forever, creating a permanent deadlock.&lt;/p&gt;
&lt;p&gt;ThingsDB solves this by introducing a &lt;strong&gt;server-side&lt;/strong&gt; timeout. If the client holding the lock fails to release it or disappears, ThingsDB automatically releases the lock for the next person in line.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Unlike traditional distributed locks that require &quot;polling&quot; the database, ThingsDB locks are event-driven. They leverage &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.thingsdb.io/v1/data-types/room/emit/&quot;&gt;ThingsDB’s emit event system&lt;/a&gt;, meaning there is zero wasted overhead. The moment a lock is released, an event triggers the next waiter in line for immediate execution. This ensures maximum throughput while strictly guaranteeing that only one worker is active at a time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;part-1-the-infrastructure-setup&quot;&gt;Part 1: The Infrastructure Setup&lt;/h3&gt;
&lt;p&gt;Before your application can start locking resources, ThingsDB needs to prepare the underlying collection logic. This is an &lt;strong&gt;idempotent&lt;/strong&gt; operation—it only does work the first time it is called.&lt;/p&gt;
&lt;p&gt;In a production environment, you should perform this setup during your application's bootstrap phase.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from thingsdb.misc import lock

async def initialize_infrastructure(client):
    # Connect and authenticate
    await client.connect('localhost')
    await client.authenticate('admin', 'pass')
    #
    # This prepares the lock collection in ThingsDB.
    # It’s safe to call every time the service starts.
    await lock.setup(client)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;part-2-the-initialize-once-pattern&quot;&gt;Part 2: The &quot;Initialize Once&quot; Pattern&lt;/h3&gt;
&lt;p&gt;In a real-world project, you want to avoid creating new lock objects inside your high-frequency loops. Instead, initialize your lock configuration once and reuse that object throughout the lifecycle of your application.&lt;/p&gt;
&lt;p&gt;By using &lt;code&gt;functools.partial&lt;/code&gt;, we can create a &quot;Lock Factory&quot; that is pre-configured with the correct client, resource name, and timeout settings.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from functools import partial
from thingsdb.misc import lock

# This will hold our pre-configured lock factory
invoice_lock = None

async def setup_application_locks(client):
    global invoice_lock
    #
    # Perform the one-time backend setup
    await lock.setup(client)
    #
    # Initialize the lock object once.
    # If a worker crashes, the lock is freed after 30 seconds.
    invoice_lock = partial(lock.lock,
                           client=client,
                           name='process-invoices',
                           timeout=30)
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;&lt;p&gt;Note: You can create as many different locks as your application needs. Just ensure that each unique resource or critical section is given its own &lt;strong&gt;unique name&lt;/strong&gt; to avoid unintended interference between different parts of your system.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;part-3-using-the-lock-in-your-business-logic&quot;&gt;Part 3: Using the Lock in Your Business Logic&lt;/h3&gt;
&lt;p&gt;Because the implementation uses the &lt;code&gt;async with&lt;/code&gt;syntax, it integrates seamlessly into any &lt;code&gt;asyncio&lt;/code&gt; application. It feels exactly like a local lock, but it’s protecting your data across the globe.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;async def process_invoices():
    # Use the pre-configured lock we created during startup
    async with invoice_lock():
        print('Distributed lock acquired! '
              'No other service can enter this block.')
        #
        # Simulate critical work like hitting a payment API
        await asyncio.sleep(5.0)
        #
        print('Work finished. Lock is automatically released.')
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;monitoring-and-safety&quot;&gt;Monitoring and Safety&lt;/h3&gt;
&lt;p&gt;Sometimes you need to know if a resource is busy without actually waiting in line. You can use the &lt;code&gt;locked()&lt;/code&gt; function for a non-blocking check:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;is_busy = await lock.locked(client, 'process-invoices')
if is_busy:
    print(&quot;Another worker is currently processing invoices.&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;key-takeaways&quot;&gt;Key Takeaways&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Queue Management:&lt;/strong&gt; The &lt;code&gt;queue_size&lt;/code&gt; and &lt;code&gt;timeout&lt;/code&gt; parameters allow you to control exactly how long a request should wait before giving up, preventing &quot;zombie&quot; requests from clogging your system.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resilience:&lt;/strong&gt; The server-side timeout ensures that your system is self-healing even in the face of hardware failures or process crashes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Developer Experience:&lt;/strong&gt; By mimicking the &lt;code&gt;asyncio.Lock()&lt;/code&gt; interface, the learning curve for your team is practically zero.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;conclusion-1&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Distributing your Python application shouldn't mean sacrificing the safety of mutual exclusion. By using ThingsDB to back your locks, you get a high-performance, self-healing synchronization layer that scales with your infrastructure.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/12077/distributed-locking-in-python</guid>
<pubDate>Mon, 23 Feb 2026 07:37:04 +0000</pubDate>
</item>
<item>
<title>Optimizing ThingsDB: Speeding up with Type Indexes</title>
<link>https://coderlegion.com/11965/optimizing-thingsdb-speeding-up-with-type-indexes</link>
<description>&lt;p&gt;In the latest release of &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/thingsdb/ThingsDB&quot;&gt;ThingsDB&lt;/a&gt;, we’ve introduced a way to significantly accelerate the &lt;code&gt;type_all()&lt;/code&gt; function. By enabling type indexing, you can now loop over all instances of a specific type with near-instant results.&lt;/p&gt;
&lt;h3 id=&quot;the-ideal-way-hierarchical-access&quot;&gt;The &quot;Ideal&quot; Way: Hierarchical Access&lt;/h3&gt;
&lt;p&gt;In ThingsDB, the preferred pattern for accessing data is through your own code logic. At &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.infrasonar.com&quot;&gt;InfraSonar&lt;/a&gt;, for example, we use a hierarchical structure for our &lt;code&gt;Container&lt;/code&gt; type. Every container can have children, and we retrieve the entire tree starting from a &lt;code&gt;.root&lt;/code&gt; container.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;new_type('Container');
set_type('Container', {
    name: 'str',
    children: '{Container}',
    get_containers: |this| {
        // Recursively collect all containers in a set
        this.children.reduce(|a, c| a |= c.get_containers(), set(this));
    }
});

// Return a set with all containers via the root:
.root.get_containers();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;the-problem-when-type-all-is-slow&quot;&gt;The Problem: When &lt;code&gt;type_all()&lt;/code&gt; is slow&lt;/h3&gt;
&lt;p&gt;There are scenarios where data isn't easily reachable through a single root, or where you simply need a flat list of every instance of a type. For this, ThingsDB provides &lt;code&gt;type_all()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Historically, &lt;code&gt;type_all()&lt;/code&gt; was designed for debugging and one-off migrations, not for high-frequency production code. This is because, internally, ThingsDB had to perform a full scan—looping through every single object in the collection to filter those that matched the requested type.&lt;/p&gt;
&lt;p&gt;If you measure the performance, the difference is noticeable:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// Returns an object with `data` (the set) and `time` (execution duration)
timeit(type_all('Container'));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without Index &lt;em&gt;(Full Scan)&lt;/em&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
    &quot;data&quot;: [..],
    &quot;time&quot;: 0.008419966
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;the-solution-enabling-the-type-index&quot;&gt;The Solution: Enabling the Type Index&lt;/h3&gt;
&lt;p&gt;You can now tell ThingsDB to maintain a dedicated index for a specific type. This moves the effort from the &lt;em&gt;query time&lt;/em&gt; to the &lt;em&gt;storage time&lt;/em&gt;, making retrievals incredibly fast.&lt;/p&gt;
&lt;p&gt;You can enable this on an &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.thingsdb.io/v1/collection-api/mod_type/idx/&quot;&gt;existing type&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// Enable the 'idx' flag on the Container type
mod_type('Container', 'idx', true);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or, define it &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.thingsdb.io/v1/collection-api/new_type/&quot;&gt;immediately upon creation&lt;/a&gt; using the &lt;code&gt;IDX&lt;/code&gt; flag:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;new_type('Container', IDX);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;performance-impact&quot;&gt;Performance Impact&lt;/h3&gt;
&lt;p&gt;Once indexed, running &lt;code&gt;type_all('Container')&lt;/code&gt; no longer requires a full scan. ThingsDB simply returns the pre-populated set of instances.&lt;/p&gt;
&lt;p&gt;With Type Index enabled:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
    &quot;data&quot;: [..],
    &quot;time&quot;: 0.00014754
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By enabling the index, the execution time dropped from &lt;strong&gt;8.4 milliseconds&lt;/strong&gt; to just &lt;strong&gt;147 microseconds&lt;/strong&gt;. This is a &lt;strong&gt;57x&lt;/strong&gt; speed improvement.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Note: You might need to run the query twice to see the full effect. ThingsDB is &quot;lazy&quot; and typically starts building the index upon the first request after the flag is enabled.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;While traversing your own data structures &lt;em&gt;(like our &lt;code&gt;.root&lt;/code&gt; example)&lt;/em&gt; remains the most &quot;ThingsDB-native&quot; way to work, the new &lt;code&gt;IDX&lt;/code&gt; flag makes &lt;code&gt;type_all()&lt;/code&gt; a viable and high-performance tool for your production toolset.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/11965/optimizing-thingsdb-speeding-up-with-type-indexes</guid>
<pubDate>Fri, 20 Feb 2026 10:25:33 +0000</pubDate>
</item>
<item>
<title>Understanding the core mechanics of ThingsDB</title>
<link>https://coderlegion.com/8731/understanding-the-core-mechanics-of-thingsdb</link>
<description>&lt;p&gt;In my &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/8421/schema-first-approach-with-thingsdb&quot;&gt;previous post&lt;/a&gt;, I promised to dive into the client side. However, to truly appreciate how the client interacts with the system, we first need to pull back the curtain on what happens &lt;em&gt;&quot;under the hood&quot;&lt;/em&gt; when you execute a query or run a procedure in &lt;a rel=&quot;nofollow&quot; href=&quot;https://thingsdb.io&quot;&gt;ThingsDB&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;step-1-the-gateway-and-away-mode&quot;&gt;Step 1: The Gateway and &quot;AWAY&quot; Mode&lt;/h3&gt;
&lt;p&gt;When you send a query to a ThingsDB node, the first thing it does is check its own status. If the node is currently in &lt;strong&gt;&quot;AWAY&quot;&lt;/strong&gt; mode &lt;em&gt;(a specialized state we’ll explore in a moment)&lt;/em&gt;, it won't process your request locally. Instead, it acts as a traffic controller, forwarding the query to another active node in the cluster. This ensures that the client never experiences a &quot;blocked&quot; connection.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=10427946820811360751&quot; alt=&quot;The Gateway and AWAY Mode&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;step-2-to-compile-or-not-to-compile-&quot;&gt;Step 2: To Compile or Not to Compile?&lt;/h3&gt;
&lt;p&gt;Once a node accepts the query, it checks its internal cache. If ThingsDB recognizes the exact same query from earlier, it can skip the compilation phase and use a pre-compiled version from the cache.&lt;/p&gt;
&lt;p&gt;If the query is new, ThingsDB begins the &lt;strong&gt;compilation process&lt;/strong&gt;. During this phase, the engine determines a critical factor: &lt;em&gt;Does this query require a &quot;change&quot;&lt;/em&gt;?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Read-only queries:&lt;/strong&gt; If no data changes are required, the query executes immediately. No other nodes are involved, making read operations incredibly fast.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Procedures:&lt;/strong&gt; This is where procedures shine. Unlike raw queries, procedures are always pre-compiled. ThingsDB knows instantly whether they require a change or not, shaving off valuable microseconds.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=17433814778877694978&quot; alt=&quot;To Compile or Not to Compile&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;step-3-the-race-for-consensus-the-change-id-&quot;&gt;Step 3: The Race for Consensus (The Change ID)&lt;/h3&gt;
&lt;p&gt;If a query needs to modify data, ThingsDB initiates a &quot;battle&quot; for the next &lt;strong&gt;Change ID&lt;/strong&gt;. It looks at the current global Change ID and proposes the next increment &lt;em&gt;(Current + 1)&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Through a quorum-based consensus, if the majority of nodes agree on this ID, the change is processed. All modifications to the collection are tied to this specific ID and synchronized across the cluster. This mechanism guarantees that every node handles changes in the exact same order, maintaining perfect data consistency.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=397844495296791436&quot; alt=&quot;The Race for Consensus&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;step-4-futures-and-non-blocking-changes&quot;&gt;Step 4: Futures and Non-Blocking Changes&lt;/h3&gt;
&lt;p&gt;ThingsDB handles complex logic using &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.thingsdb.io/v1/data-types/future/&quot;&gt;Futures&lt;/a&gt;. If a query contains one or multiple futures that require changes, each future can receive its own Change ID. This is a powerful optimization! It prevents the system from locking up during external module calls and allows you to isolate heavy &quot;change&quot; logic from the rest of your procedure or query.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Strategy Tip:&lt;/strong&gt; If you have a procedure that is mostly read-only but occasionally needs to write data, wrap the &quot;write&quot; portion in a future. This prevents the entire procedure from generating a global change every time it's called (see &lt;a rel=&quot;nofollow&quot; href=&quot;https://book.thingsdb.io/#chapter-13-1-2&quot;&gt;this example&lt;/a&gt; from the ThingsDB book).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;step-5-storage-and-the-boot-sequence&quot;&gt;Step 5: Storage and The &quot;Boot&quot; Sequence&lt;/h3&gt;
&lt;p&gt;ThingsDB uses a dual-mechanism for data persistence:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Full State Dumps:&lt;/strong&gt; A snapshot of the entire data state at a specific point in time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Archive Files:&lt;/strong&gt; A continuous log of every individual change.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When a node boots up, it follows a strict recovery path:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It loads the last Full State Dump.&lt;/li&gt;
&lt;li&gt;It identifies the last Change ID from that dump and then replays all subsequent changes from the Archive Files.&lt;/li&gt;
&lt;li&gt;It then waits for a peer node to enter &lt;strong&gt;Away Mode&lt;/strong&gt;. The booted node reports its last processed Change ID, and the &quot;Away&quot; node syncs the missing delta.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If a node is too far out of sync &lt;em&gt;(or is a brand-new node being added to the cluster)&lt;/em&gt;, a Full Synchronization occurs, where the entire state dump is transferred from scratch.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=15115807473647644070&quot; alt=&quot;Storage and The Boot Sequence&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;step-6-the-magic-of-away-mode&quot;&gt;Step 6: The Magic of &quot;AWAY&quot; Mode&lt;/h3&gt;
&lt;p&gt;&quot;Away&quot; mode is ThingsDB’s secret weapon for maintenance without downtime. While a node is &quot;Away,&quot; it performs heavy lifting such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Full State Dumps &lt;em&gt;(Snapshots)&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Garbage Collection &lt;em&gt;(Memory management)&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Scheduled Backups.&lt;/li&gt;
&lt;li&gt;Synchronizing peer nodes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;During this time, the node still talks to clients, but it forwards their queries to keep the system responsive. Changes are collected in the background and applied just before the node leaves Away mode and rejoins the cluster. ThingsDB ensures that &lt;strong&gt;only one node at a time&lt;/strong&gt; is in this mode, keeping the rest of the cluster available for primary tasks.&lt;/p&gt;
&lt;h3 id=&quot;conclusion-2&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;By decoupling blocking maintenance tasks from client requests and using a sophisticated consensus for changes, ThingsDB provides a database experience that is both high-performance and incredibly resilient.&lt;/p&gt;
&lt;p&gt;Now that you understand the &quot;brain&quot; of ThingsDB, we are finally ready to look at the Client Side in my next post!&lt;/p&gt;
</description>
<category>Database</category>
<guid isPermaLink="true">https://coderlegion.com/8731/understanding-the-core-mechanics-of-thingsdb</guid>
<pubDate>Mon, 22 Dec 2025 15:52:17 +0000</pubDate>
</item>
<item>
<title>Schema-First Approach with ThingsDB</title>
<link>https://coderlegion.com/8421/schema-first-approach-with-thingsdb</link>
<description>&lt;p&gt;In many architectures, the separation between database and application code introduces a dangerous gap where data integrity can fail. The application code is responsible for validation, but what if multiple services access the same data?&lt;/p&gt;
&lt;p&gt;This post explores a different approach. We'll show you how to leverage ThingsDB's core features—&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.thingsdb.io/v1/overview/collections/#typed-collection&quot;&gt;Typed Collections&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.thingsdb.io/v1/procedures-api/&quot;&gt;Procedures&lt;/a&gt;, and &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.thingsdb.io/v1/data-types/room/&quot;&gt;Rooms&lt;/a&gt;—to make the database itself the enforcing authority.&lt;/p&gt;
&lt;p&gt;Follow along as we set up a secure, event-driven API for an &lt;code&gt;Order&lt;/code&gt; application, demonstrating how to lock down access and ensure every data change is both valid and consistent.&lt;/p&gt;
&lt;h3 id=&quot;1-setting-the-stage-the-blank-slate&quot;&gt;1. Setting the Stage: The Blank Slate&lt;/h3&gt;
&lt;p&gt;We begin by creating a fresh collection for our application.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(execute the code snippet in the &lt;code&gt;/thingsdb&lt;/code&gt; scope)&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;new_collection('OrderApp');
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;2-understanding-typed-collections-the-core-defense-&quot;&gt;2. Understanding Typed Collections (The Core Defense)&lt;/h3&gt;
&lt;p&gt;The first step toward protection is establishing a &lt;strong&gt;schema&lt;/strong&gt;. By default, a new ThingsDB collection starts with an empty, unrestricted object (a basic &quot;&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.thingsdb.io/v1/data-types/thing/&quot;&gt;thing&lt;/a&gt;&quot;) at its root. Any user with write access can add &lt;em&gt;anything&lt;/em&gt; to it.&lt;/p&gt;
&lt;p&gt;We want to lock this down. We create a type, &lt;code&gt;App&lt;/code&gt;, to serve as the structured blueprint for the collection's entire existence.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(execute the code snippet in the &lt;code&gt;//OrderApp&lt;/code&gt; scope)&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// 1. Create a type for the root of my collection
new_type('App');

// 2. Convert the collection root to the 'App' type
.to_type('App');
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; The collection is now restricted by the &lt;code&gt;App&lt;/code&gt; type. No data can be added or modified unless the modification conforms to changes made to the &lt;code&gt;App&lt;/code&gt; type definition. This is your primary schema defense.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;3-defining-the-data-model&quot;&gt;3. Defining the Data Model&lt;/h3&gt;
&lt;p&gt;Next, we define the structure of the data we want to store—the &lt;code&gt;Order&lt;/code&gt; object—and where to keep it within the &lt;code&gt;App&lt;/code&gt; root.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(execute the code snippet in the &lt;code&gt;//OrderApp&lt;/code&gt; scope)&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// Create an enumerator for consistent order status values
set_enum('OrderStatus', {
    OPEN: 'Open',
    PENDING: 'Pending',
    CLOSED: 'Closed',
});

// Define the Order structure
set_type('Order', {
    id: '#',               // Return ID as `id` when wrapped
    name: 'str',
    price: 'float',
    status: 'OrderStatus', // Enforcing status consistency
    created: 'datetime',
});

// Update the App type: Add a property 'orders' as an array of Order objects
mod_type('App', 'add', 'orders', '[Order]');
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;mod_type(..)&lt;/code&gt; call automatically creates the &lt;code&gt;orders&lt;/code&gt; property as an empty list on the collection root, ready to hold our data.&lt;/p&gt;
&lt;h3 id=&quot;4-establishing-an-event-channel&quot;&gt;4. Establishing an Event Channel&lt;/h3&gt;
&lt;p&gt;For a modern microservice or component-based application, polling for data changes is inefficient. We set up an event channel using a &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.thingsdb.io/v1/data-types/room/&quot;&gt;Room&lt;/a&gt; to enable real-time communication.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(execute the code snippet in the &lt;code&gt;//OrderApp&lt;/code&gt; scope)&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// Add a room property to the App type for event handling
mod_type('App', 'add', 'order_events', 'room');

// For easy identification, we give the room a fixed name
.order_events.set_name('api_order_events');
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Benefit:&lt;/strong&gt; Any component can now join this room and instantly react to events like &lt;code&gt;new-order&lt;/code&gt; or &lt;code&gt;change-order-status&lt;/code&gt; without needing to constantly query the collection.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;5-encapsulating-logic-with-procedures-the-api-layer-&quot;&gt;5. Encapsulating Logic with Procedures (The API Layer)&lt;/h3&gt;
&lt;p&gt;To guarantee that only valid changes are made to the collection, we expose all actions &lt;em&gt;only&lt;/em&gt; through &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.thingsdb.io/v1/procedures-api/&quot;&gt;Procedures&lt;/a&gt;. We prefix these procedures with &lt;code&gt;api_&lt;/code&gt; for easy security whitelisting later.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(execute the code snippet in the &lt;code&gt;//OrderApp&lt;/code&gt; scope)&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// 1. Add a new order (with validation on input types)
new_procedure('api_order_add', |name, price| {
    order = Order{
        name:,
        price:,
    };
    .orders.push(order);

    // Emit the 'new-order' event for all listeners
    .order_events.emit('new-order', order.wrap());
    nil;
});

// 2. Change order status
new_procedure('api_order_set_status', |order_id, status| {
    // Input validation: ensures the new status is a valid enum value
    new_status = OrderStatus{||status.upper()};

    // Access and update the specific Order object
    Order(order_id).status = new_status;

    // Emit the 'change-order-status' event
    .order_events.emit('change-order-status', order_id, new_status.value());
    nil;
});

// 3. Retrieve orders by status
new_procedure('api_orders_by_status', |status| {
    order_status = OrderStatus{||status.upper()};
    .orders.filter(|order| order.status == order_status).map_wrap();
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;6-strict-security-the-locked-down-api-user&quot;&gt;6. Strict Security: The Locked-Down API User&lt;/h3&gt;
&lt;p&gt;The final and most crucial step is to create a user that can &lt;strong&gt;only execute these specific procedures&lt;/strong&gt;. This prevents any external service from bypassing your defined business logic.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(execute the code snippet in the &lt;code&gt;/thingsdb&lt;/code&gt; scope)&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// Create a new user for microservice access
new_user('api');

// Grant access to the collection:
// CHANGE (to modify data), JOIN (to listen to events), RUN (to execute procedures)
grant('//OrderApi', 'api', CHANGE|JOIN|RUN);

// Whitelist: This is the security lock.
// The user can only execute procedures and join rooms starting with 'api_'
whitelist_add('api', 'procedures', /^api_.*/);
whitelist_add('api', 'rooms', /^api_.*/);

// Generate and return the authentication token
new_token('api');
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this setup, the &lt;code&gt;api&lt;/code&gt; user is completely restricted. It cannot arbitrarily delete the collection, change its structure, or modify data without going through the validated input and logic defined in your &lt;code&gt;api_&lt;/code&gt; procedures. Your collection is now truly protected by code!&lt;/p&gt;
&lt;p&gt;In the next post, we will switch gears to the client side. We will demonstrate how a microservice uses the generated token to connect to the &lt;code&gt;OrderApp&lt;/code&gt; collection, how to perform the authenticated &lt;code&gt;api_&lt;/code&gt; queries, and, crucially, how to &quot;listen&quot; to the real-time event changes emitted through the &lt;code&gt;api_order_events&lt;/code&gt; room.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/8421/schema-first-approach-with-thingsdb</guid>
<pubDate>Fri, 12 Dec 2025 06:48:09 +0000</pubDate>
</item>
<item>
<title>No Code in the Database... Right?</title>
<link>https://coderlegion.com/8352/no-code-in-the-database-right</link>
<description>&lt;p&gt;&lt;em&gt;&quot;No code in the database&quot;&lt;/em&gt; is a mantra instilled in developers from computer science school onward. It's a fundamental principle of traditional database design aimed at separation of concerns and maintainability.&lt;/p&gt;
&lt;p&gt;But what if your &lt;em&gt;database IS your code&lt;/em&gt;? What if the data store not only saves your state but actively manages your business logic, acts as your API, and functions as your message bus?&lt;/p&gt;
&lt;h4 id=&quot;the-traditional-problem-framework-ownership&quot;&gt;The Traditional Problem: Framework Ownership&lt;/h4&gt;
&lt;p&gt;When you start a project using a traditional SQL database, frameworks like Django, Laravel, or Rails become the central authority.&lt;/p&gt;
&lt;p&gt;These frameworks effectively &lt;em&gt;&quot;own&quot; the database&lt;/em&gt;. They manage the schema through migrations and use an Object-Relational Mapping (ORM) layer to perform queries. This is why mixing major frameworks that touch the same core data is heavily discouraged—each framework wants to be the single source of truth for the database schema and application logic.&lt;/p&gt;
&lt;h4 id=&quot;the-thingsdb-shift-the-database-as-the-hub&quot;&gt;The ThingsDB Shift: The Database as the Hub&lt;/h4&gt;
&lt;p&gt;With a database like &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.thingsdb.io/v1/getting-started/&quot;&gt;ThingsDB&lt;/a&gt;, the model fundamentally changes. Here, the database is your runtime environment, blurring the line between data storage and application logic.&lt;/p&gt;
&lt;p&gt;This architecture is especially powerful if your project involves:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Polyglot Microservices:&lt;/strong&gt; Using many different components or services, possibly written in different programming languages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decentralized Access:&lt;/strong&gt; Needing all components to access the same, unified data source and logic hub.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.thingsdb.io/v1/getting-started/&quot;&gt;ThingsDB&lt;/a&gt; steps into the role of the Data API and Message Bus, centralizing data management and coordination.&lt;/p&gt;
&lt;h4 id=&quot;guaranteeing-data-state-and-event-driven-architecture&quot;&gt;Guaranteeing Data State and Event-Driven Architecture&lt;/h4&gt;
&lt;p&gt;This integrated approach offers powerful mechanisms to guarantee data integrity and build truly reactive applications:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Strict Data Integrity with Typed Collections and Procedures:&lt;/strong&gt; By defining &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.thingsdb.io/v1/procedures-api/&quot;&gt;procedures&lt;/a&gt; with strict input restrictions and leveraging fully &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.thingsdb.io/v1/overview/collections/#typed-collection&quot;&gt;typed collections&lt;/a&gt;, you guarantee the validity and consistent state of your data. The data cannot be modified in an inconsistent way because the logic lives right alongside it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No More Polling with Rooms and Events:&lt;/strong&gt; ThingsDB removes the need for wasteful polling across your services. Instead, you build truly event-driven applications using Rooms and Events. When data changes, all subscribing components are immediately notified, allowing your application to react instantly.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In short, while frameworks like Django and Laravel are perfect for applications where they serve as the central brain, &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.thingsdb.io/v1/getting-started/&quot;&gt;ThingsDB&lt;/a&gt; enables a modern, event-driven architecture where the database is a powerful, active participant in your business logic, simplifying communication and guaranteeing data consistency across diverse application components. Choose the tool that best aligns with your project's long-term complexity and inter-service communication needs.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/8352/no-code-in-the-database-right</guid>
<pubDate>Wed, 10 Dec 2025 21:20:15 +0000</pubDate>
</item>
<item>
<title>Event-Driven Updates with ThingsDB</title>
<link>https://coderlegion.com/7517/event-driven-updates-with-thingsdb</link>
<description>&lt;p&gt;In the world of real-time applications, delivering instantaneous updates to users is foundational. From collaborative documents to live chat applications, the ability to instantly reflect changes across all connected clients can make or break the user experience. &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.thingsdb.io/v1&quot;&gt;ThingsDB&lt;/a&gt;, with its robust event-driven architecture, offers a powerful solution for achieving this.&lt;/p&gt;
&lt;p&gt;Traditionally, implementing event-driven updates in a real-time application, like a chat room, often followed a pattern that could lead to unnecessary complexity. Let's break down the classic approach and then explore how ThingsDB's new &lt;em&gt;&quot;emit to peers only&quot;&lt;/em&gt; feature simplifies this process.&lt;/p&gt;
&lt;h3 id=&quot;traditional-flow&quot;&gt;Traditional Flow&lt;/h3&gt;
&lt;p&gt;Imagine a simple chat application. When a user sends a message, the desired flow is that the message appears instantly on the users screen, and via an event for everyone in the chat room.&lt;/p&gt;
&lt;p&gt;Here's how that flow typically worked:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A chat client sends a new message to the ThingsDB server.&lt;/li&gt;
&lt;li&gt;ThingsDB stores the message and then emits an event to a designated &quot;room&quot; (or topic) that all connected clients are listening to.&lt;/li&gt;
&lt;li&gt;All clients subscribed to that &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.thingsdb.io/v1/data-types/room/&quot;&gt;room&lt;/a&gt; receive the event and display the new message in their respective chat interfaces.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This approach works, but it introduces a subtle challenge: the client that sent the message also receives the same event back.&lt;/p&gt;
&lt;p&gt;Here's a visual representation of the traditional flow:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y92i3mpofjf0tjjcdjlk.png&quot; alt=&quot;visual-representation-traditional-flow&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;self-inflicted-updates&quot;&gt;Self-Inflicted Updates&lt;/h3&gt;
&lt;p&gt;While seemingly harmless, receiving one's own message back as an event can create issues, especially if the client is designed to immediately display the message locally upon sending. This leads to a double-update scenario: the message appears instantly when sent, and then again when the event is received from the server.&lt;/p&gt;
&lt;p&gt;To avoid this redundancy, developers often resorted to complex workarounds like implementing intricate code to detect if an incoming event originated from the current client, perhaps by including a unique client ID with each emitted event.&lt;/p&gt;
&lt;h3 id=&quot;the-thingsdb-solution-emitting-to-peers-only&quot;&gt;The ThingsDB Solution: Emitting to Peers Only&lt;/h3&gt;
&lt;p&gt;ThingsDB addresses this challenge elegantly with its new &lt;em&gt;&quot;emit to peers only&quot;&lt;/em&gt; option. This powerful feature allows you to broadcast events to all listeners within a room except the one that initiated the event.&lt;/p&gt;
&lt;p&gt;Here's how the simplified flow works:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A chat client sends a new message to ThingsDB, explicitly indicating that the event should only be emitted to other peers and displays the message in the chat screen.&lt;/li&gt;
&lt;li&gt;ThingsDB stores the message and then emits the event to the designated room. Crucially, it filters out the originating client from the list of recipients.&lt;/li&gt;
&lt;li&gt;All other clients subscribed to the &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.thingsdb.io/v1/data-types/room/&quot;&gt;room&lt;/a&gt; receive the event and display the new message.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Sender Updates Locally: The sending client, having already displayed the message locally, does not receive a redundant event from the server.&lt;/p&gt;
&lt;p&gt;Let's visualize this improved flow:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b3ja72tghqwgljyc7mlh.png&quot; alt=&quot;visual-representation-emit-peers-only-flow&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;beyond-chat-rooms&quot;&gt;Beyond Chat Rooms&lt;/h3&gt;
&lt;p&gt;The &lt;em&gt;&quot;emit to peers only&quot;&lt;/em&gt; concept extends far beyond chat applications. Consider any scenario where a client initiates an action and immediately reflects that change locally, while also needing to inform other connected clients:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Collaborative Document Editing:&lt;/strong&gt; When a user types a character, it appears instantly on their screen. Other collaborators see the update via an event, but the typing user doesn't need to process their own keystroke as an incoming event.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real-time Dashboards:&lt;/strong&gt; A user performs an action that updates a metric locally. Other users viewing the dashboard receive an event to update their views, but the initiating user's dashboard is already updated.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gaming:&lt;/strong&gt; A player moves their character. The local client updates the character's position instantly. Other players receive an event to update the character's position on their screens.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;conclusion-3&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;ThingsDB's &lt;em&gt;&quot;emit to peers only&quot;&lt;/em&gt; feature is implemented by all &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.thingsdb.io/v1/connect/&quot;&gt;supported clients&lt;/a&gt; and is addressing a common pain point in real-time application development. For any application built on the promise of instant updates, utilizing this feature means spending less time debugging logic and more time writing clean, purposeful code.&lt;/p&gt;
</description>
<category>Other</category>
<guid isPermaLink="true">https://coderlegion.com/7517/event-driven-updates-with-thingsdb</guid>
<pubDate>Wed, 19 Nov 2025 08:55:19 +0000</pubDate>
</item>
</channel>
</rss>