<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jeroen van der Heijden</title>
    <description>The latest articles on DEV Community by Jeroen van der Heijden (@joente).</description>
    <link>https://dev.to/joente</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F829081%2F4a77421f-dc64-4683-ab23-b333d1353e5f.jpg</url>
      <title>DEV Community: Jeroen van der Heijden</title>
      <link>https://dev.to/joente</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/joente"/>
    <language>en</language>
    <item>
      <title>Distributed Locking in Python</title>
      <dc:creator>Jeroen van der Heijden</dc:creator>
      <pubDate>Mon, 23 Feb 2026 07:39:08 +0000</pubDate>
      <link>https://dev.to/joente/distributed-locking-in-python-47jh</link>
      <guid>https://dev.to/joente/distributed-locking-in-python-47jh</guid>
      <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 href="https://thingsdb.io" rel="noopener noreferrer"&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;h2&gt;
  
  
  Why Distributed Locking is a "Hard" Problem
&lt;/h2&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 "held" 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;p&gt;Unlike traditional distributed locks that require "polling" the database, ThingsDB locks are event-driven. They leverage &lt;a href="https://docs.thingsdb.io/v1/data-types/room/emit/" rel="noopener noreferrer"&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;h2&gt;
  
  
  Part 1: The Infrastructure Setup
&lt;/h2&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;thingsdb.misc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize_infrastructure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Connect and authenticate
&lt;/span&gt;    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pass&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# This prepares the lock collection in ThingsDB.
&lt;/span&gt;    &lt;span class="c1"&gt;# It’s safe to call every time the service starts.
&lt;/span&gt;    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Part 2: The "Initialize Once" Pattern
&lt;/h2&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 "Lock Factory" that is pre-configured with the correct client, resource name, and timeout settings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;functools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;partial&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;thingsdb.misc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt;

&lt;span class="c1"&gt;# This will hold our pre-configured lock factory
&lt;/span&gt;&lt;span class="n"&gt;invoice_lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setup_application_locks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;invoice_lock&lt;/span&gt;

    &lt;span class="c1"&gt;# Perform the one-time backend setup
&lt;/span&gt;    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Initialize the lock object once.
&lt;/span&gt;    &lt;span class="c1"&gt;# If a worker crashes, the lock is freed after 30 seconds.
&lt;/span&gt;    &lt;span class="n"&gt;invoice_lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;partial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;process-invoices&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;h2&gt;
  
  
  Part 3: Using the Lock in Your Business Logic
&lt;/h2&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_invoices&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Use the pre-configured lock we created during startup
&lt;/span&gt;    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;invoice_lock&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Distributed lock acquired! &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;No other service can enter this block.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Simulate critical work like hitting a payment API
&lt;/span&gt;        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Work finished. Lock is automatically released.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Monitoring and Safety
&lt;/h2&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;is_busy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;process-invoices&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;is_busy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Another worker is currently processing invoices.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&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 "zombie" 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;h2&gt;
  
  
  Conclusion
&lt;/h2&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>python</category>
      <category>thingsdb</category>
      <category>programming</category>
    </item>
    <item>
      <title>Optimizing ThingsDB: Speeding up with Type Indexes</title>
      <dc:creator>Jeroen van der Heijden</dc:creator>
      <pubDate>Fri, 20 Feb 2026 10:34:59 +0000</pubDate>
      <link>https://dev.to/joente/optimizing-thingsdb-speeding-up-with-type-indexes-4g0e</link>
      <guid>https://dev.to/joente/optimizing-thingsdb-speeding-up-with-type-indexes-4g0e</guid>
      <description>&lt;p&gt;In the latest release of &lt;a href="https://github.com/thingsdb/ThingsDB" rel="noopener noreferrer"&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;h2&gt;
  
  
  The "Ideal" Way: Hierarchical Access
&lt;/h2&gt;

&lt;p&gt;In ThingsDB, the preferred pattern for accessing data is through your own code logic. At &lt;a href="https://www.infrasonar.com" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;new_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;set_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;str&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{Container}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;get_containers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Recursively collect all containers in a set&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_containers&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Return a set with all containers via the root:&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_containers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Problem: When &lt;code&gt;type_all()&lt;/code&gt; is slow
&lt;/h2&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Returns an object with `data` (the set) and `time` (execution time)&lt;/span&gt;
&lt;span class="nf"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;type_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without Index &lt;em&gt;(Full Scan)&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;..&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.008419966&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Solution: Enabling the Type Index
&lt;/h2&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 href="https://docs.thingsdb.io/v1/collection-api/mod_type/idx/" rel="noopener noreferrer"&gt;existing type&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Enable the 'idx' flag on the Container type&lt;/span&gt;
&lt;span class="nf"&gt;mod_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;idx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, define it &lt;a href="https://docs.thingsdb.io/v1/collection-api/new_type/" rel="noopener noreferrer"&gt;immediately upon creation&lt;/a&gt; using the &lt;code&gt;IDX&lt;/code&gt; flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;new_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;IDX&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Performance Impact
&lt;/h2&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;..&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.00014754&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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 "lazy" and typically starts building the index upon the first request after the flag is enabled.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&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 "ThingsDB-native" 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>thingsdb</category>
      <category>database</category>
      <category>datastructures</category>
      <category>programming</category>
    </item>
    <item>
      <title>Understanding the core mechanics of ThingsDB</title>
      <dc:creator>Jeroen van der Heijden</dc:creator>
      <pubDate>Mon, 22 Dec 2025 15:46:38 +0000</pubDate>
      <link>https://dev.to/joente/understanding-the-core-mechanics-of-thingsdb-1edg</link>
      <guid>https://dev.to/joente/understanding-the-core-mechanics-of-thingsdb-1edg</guid>
      <description>&lt;p&gt;In my &lt;a href="https://dev.to/joente/schema-first-approach-with-thingsdb-407p"&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;"under the hood"&lt;/em&gt; when you execute a query or run a procedure in &lt;a href="https://thingsdb.io" rel="noopener noreferrer"&gt;ThingsDB&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: The Gateway and "AWAY" Mode
&lt;/h2&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;"AWAY"&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 "blocked" connection.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fegzg5yl84pqlc3qucgrv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fegzg5yl84pqlc3qucgrv.png" alt="The Gateway and AWAY mode" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: To Compile or Not to Compile?
&lt;/h2&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 "change"&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc1qcwhht3xgtw6ltrns5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc1qcwhht3xgtw6ltrns5.png" alt="Compile or not compile" width="800" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: The Race for Consensus (The Change ID)
&lt;/h2&gt;

&lt;p&gt;If a query needs to modify data, ThingsDB initiates a "battle" 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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9nw6oyq4pr01asnj9teu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9nw6oyq4pr01asnj9teu.png" alt="Race for consensus" width="800" height="593"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Futures and Non-Blocking Changes
&lt;/h2&gt;

&lt;p&gt;ThingsDB handles complex logic using &lt;a href="https://docs.thingsdb.io/v1/data-types/future/" rel="noopener noreferrer"&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 "change" 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 "write" portion in a future. This prevents the entire procedure from generating a global change every time it's called (see &lt;a href="https://book.thingsdb.io/#chapter-13-1-2" rel="noopener noreferrer"&gt;this example&lt;/a&gt; from the ThingsDB book).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 5: Storage and The "Boot" Sequence
&lt;/h2&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 "Away" 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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwzshibf4lvkx7dp981ud.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwzshibf4lvkx7dp981ud.png" alt="Storage and The Boot Sequence" width="800" height="609"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: The Magic of "AWAY" Mode
&lt;/h2&gt;

&lt;p&gt;"Away" mode is ThingsDB’s secret weapon for maintenance without downtime. While a node is "Away," 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;h2&gt;
  
  
  Conclusion
&lt;/h2&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 "brain" of ThingsDB, we are finally ready to look at the Client Side in my next post!&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>database</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Schema-First Approach with ThingsDB</title>
      <dc:creator>Jeroen van der Heijden</dc:creator>
      <pubDate>Fri, 12 Dec 2025 06:45:53 +0000</pubDate>
      <link>https://dev.to/joente/schema-first-approach-with-thingsdb-407p</link>
      <guid>https://dev.to/joente/schema-first-approach-with-thingsdb-407p</guid>
      <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 href="https://docs.thingsdb.io/v1/overview/collections/#typed-collection" rel="noopener noreferrer"&gt;Typed Collections&lt;/a&gt;, &lt;a href="https://docs.thingsdb.io/v1/procedures-api/" rel="noopener noreferrer"&gt;Procedures&lt;/a&gt;, and &lt;a href="https://docs.thingsdb.io/v1/data-types/room/" rel="noopener noreferrer"&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;h2&gt;
  
  
  1. Setting the Stage: The Blank Slate
&lt;/h2&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;new_collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OrderApp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Understanding Typed Collections (The Core Defense)
&lt;/h2&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 "&lt;a href="https://docs.thingsdb.io/v1/data-types/thing/" rel="noopener noreferrer"&gt;thing&lt;/a&gt;") 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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. Create a type for the root of my collection&lt;/span&gt;
&lt;span class="nf"&gt;new_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Convert the collection root to the 'App' type&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;h2&gt;
  
  
  3. Defining the Data Model
&lt;/h2&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create an enumerator for consistent order status values&lt;/span&gt;
&lt;span class="nf"&gt;set_enum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OrderStatus&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;OPEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;PENDING&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Pending&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;CLOSED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Closed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Define the Order structure&lt;/span&gt;
&lt;span class="nf"&gt;set_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Order&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;               &lt;span class="c1"&gt;// Return ID as `id` when wrapped&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;str&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;float&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OrderStatus&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Enforcing status consistency&lt;/span&gt;
    &lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;datetime&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Update the App type: Add a property 'orders' as an array of Order objects&lt;/span&gt;
&lt;span class="nf"&gt;mod_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orders&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[Order]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;h2&gt;
  
  
  4. Establishing an Event Channel
&lt;/h2&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 href="https://docs.thingsdb.io/v1/data-types/room/" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Add a room property to the App type for event handling&lt;/span&gt;
&lt;span class="nf"&gt;mod_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order_events&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;room&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// For easy identification, we give the room a fixed name&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;order_events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api_order_events&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;h2&gt;
  
  
  5. Encapsulating Logic with Procedures (The API Layer)
&lt;/h2&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 href="https://docs.thingsdb.io/v1/procedures-api/" rel="noopener noreferrer"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. Add a new order (with validation on input types)&lt;/span&gt;
&lt;span class="nf"&gt;new_procedure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api_order_add&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt;
        &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Emit the 'new-order' event for all listeners&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;order_events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;new-order&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nx"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Change order status&lt;/span&gt;
&lt;span class="nf"&gt;new_procedure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api_order_set_status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nx"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Input validation: ensures the new status is a valid enum value&lt;/span&gt;
    &lt;span class="nx"&gt;new_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()};&lt;/span&gt;

    &lt;span class="c1"&gt;// Access and update the specific Order object&lt;/span&gt;
    &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;new_status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Emit the 'change-order-status' event&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;order_events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change-order-status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;new_status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nx"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// 3. Retrieve orders by status&lt;/span&gt;
&lt;span class="nf"&gt;new_procedure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api_orders_by_status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;order_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()};&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;order_status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map_wrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Strict Security: The Locked-Down API User
&lt;/h2&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create a new user for microservice access&lt;/span&gt;
&lt;span class="nf"&gt;new_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Grant access to the collection:&lt;/span&gt;
&lt;span class="c1"&gt;// CHANGE (to modify data), JOIN (to listen to events), RUN (to execute procedures)&lt;/span&gt;
&lt;span class="nf"&gt;grant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;//OrderApi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CHANGE&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nx"&gt;JOIN&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nx"&gt;RUN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Whitelist: This is the security lock.&lt;/span&gt;
&lt;span class="c1"&gt;// The user can only execute procedures and join rooms starting with 'api_'&lt;/span&gt;
&lt;span class="nf"&gt;whitelist_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;procedures&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sr"&gt;/^api_.*/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;whitelist_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rooms&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sr"&gt;/^api_.*/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Generate and return the authentication token&lt;/span&gt;
&lt;span class="nf"&gt;new_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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 "listen" to the real-time event changes emitted through the &lt;code&gt;api_order_events&lt;/code&gt; room.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>thingsdb</category>
      <category>database</category>
      <category>eventdriven</category>
    </item>
    <item>
      <title>No Code in the Database... Right?</title>
      <dc:creator>Jeroen van der Heijden</dc:creator>
      <pubDate>Wed, 10 Dec 2025 14:11:39 +0000</pubDate>
      <link>https://dev.to/joente/no-code-in-the-database-right-51mg</link>
      <guid>https://dev.to/joente/no-code-in-the-database-right-51mg</guid>
      <description>&lt;p&gt;&lt;em&gt;"No code in the database"&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;h3&gt;
  
  
  The Traditional Problem: Framework Ownership
&lt;/h3&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;"own" 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;h3&gt;
  
  
  The ThingsDB Shift: The Database as the Hub
&lt;/h3&gt;

&lt;p&gt;With a database like &lt;a href="https://docs.thingsdb.io/v1/getting-started/" rel="noopener noreferrer"&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 href="https://docs.thingsdb.io/v1/getting-started/" rel="noopener noreferrer"&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;h3&gt;
  
  
  Guaranteeing Data State and Event-Driven Architecture
&lt;/h3&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 href="https://docs.thingsdb.io/v1/procedures-api/" rel="noopener noreferrer"&gt;procedures&lt;/a&gt; with strict input restrictions and leveraging fully &lt;a href="https://docs.thingsdb.io/v1/overview/collections/#typed-collection" rel="noopener noreferrer"&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 href="https://docs.thingsdb.io/v1/getting-started/" rel="noopener noreferrer"&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>architecture</category>
      <category>database</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Event-Driven Updates with ThingsDB</title>
      <dc:creator>Jeroen van der Heijden</dc:creator>
      <pubDate>Mon, 17 Nov 2025 18:00:34 +0000</pubDate>
      <link>https://dev.to/joente/event-driven-updates-with-thingsdb-3in5</link>
      <guid>https://dev.to/joente/event-driven-updates-with-thingsdb-3in5</guid>
      <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 href="https://docs.thingsdb.io/v1" rel="noopener noreferrer"&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;"emit to peers only"&lt;/em&gt; feature simplifies this process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Traditional Flow
&lt;/h2&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 "room" (or topic) that all connected clients are listening to.&lt;/li&gt;
&lt;li&gt;All clients subscribed to that &lt;a href="https://docs.thingsdb.io/v1/data-types/room/" rel="noopener noreferrer"&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy92i3mpofjf0tjjcdjlk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy92i3mpofjf0tjjcdjlk.png" alt="visual-representation-traditional-flow" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Self-Inflicted Updates
&lt;/h2&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;h2&gt;
  
  
  The ThingsDB Solution: Emitting to Peers Only
&lt;/h2&gt;

&lt;p&gt;ThingsDB addresses this challenge elegantly with its new &lt;em&gt;"emit to peers only"&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 href="https://docs.thingsdb.io/v1/data-types/room/" rel="noopener noreferrer"&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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3ja72tghqwgljyc7mlh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3ja72tghqwgljyc7mlh.png" alt="visual-representation-emit-peers-only-flow" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond Chat Rooms
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;"emit to peers only"&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;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;ThingsDB's &lt;em&gt;"emit to peers only"&lt;/em&gt; feature is implemented by all &lt;a href="https://docs.thingsdb.io/v1/connect/" rel="noopener noreferrer"&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>database</category>
      <category>eventdriven</category>
      <category>code</category>
    </item>
    <item>
      <title>🚀 ThingsDB v1.8.2: Introducing Anonymous Types</title>
      <dc:creator>Jeroen van der Heijden</dc:creator>
      <pubDate>Mon, 10 Nov 2025 16:05:51 +0000</pubDate>
      <link>https://dev.to/joente/thingsdb-v182-introducing-anonymous-types-5ae8</link>
      <guid>https://dev.to/joente/thingsdb-v182-introducing-anonymous-types-5ae8</guid>
      <description>&lt;p&gt;Developers using &lt;a href="https://www.thingsdb.io" rel="noopener noreferrer"&gt;ThingsDB&lt;/a&gt; &lt;em&gt;(or other data solutions)&lt;/em&gt; often run into a common pattern: you have a defined, complex type, but when you query it, you only need a subset of its fields.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; If you have a &lt;code&gt;User&lt;/code&gt; type with fields like &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, and &lt;code&gt;password_hash&lt;/code&gt;, you can't just return the entire &lt;em&gt;thing&lt;/em&gt; to a client. Before v1.8.2, your options were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define a second, wrap-only type (e.g., &lt;code&gt;_User&lt;/code&gt;) to exclude the sensitive fields.&lt;/li&gt;
&lt;li&gt;Manually construct a map of the desired fields.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ThingsDB v1.8.2 introduces an new solution with &lt;a href="https://docs.thingsdb.io/v1/data-types/anomyous/" rel="noopener noreferrer"&gt;Anonymous Types&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚡ The Solution: Anonymous Type
&lt;/h2&gt;

&lt;p&gt;Anonymous types allow you to define a temporary, ad-hoc type structure precisely where you need it, most often for defining the output of a query or a method call like &lt;code&gt;wrap()&lt;/code&gt; or &lt;code&gt;map_wrap()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The syntax is simple and powerful: the ampersand (&lt;code&gt;&amp;amp;&lt;/code&gt;) prefix followed by a standard type definition.&lt;/p&gt;

&lt;p&gt;Let's look at the classic use case—safely projecting data from a registered type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. Define the full type&lt;/span&gt;
&lt;span class="nf"&gt;set_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;str&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;str&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;password_hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;str&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// SENSITIVE FIELD&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// 2. For simplicity, we create a list with a single User instance.&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Jeroen van der Heijden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jeroen@cesbit.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;password_hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SGVsbG8gVGhpbmdzREIhCg==&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}];&lt;/span&gt;

&lt;span class="c1"&gt;// 3. Use an ANONYMOUS TYPE to define the public output&lt;/span&gt;
&lt;span class="c1"&gt;// We only expose 'id', 'name', and 'email', securely omitting the hash.&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map_wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// '#' maps to the internal ID of the Thing&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;str&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;str&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result (JSON):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123456&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jeroen van der Heijden"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jeroen@cesbit.com"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;No extra type definition needed!&lt;/strong&gt; The anonymous type ensures type safety and validation on the projected fields without cluttering your global type registry.&lt;/p&gt;

&lt;h2&gt;
  
  
  📈 Beyond Simplicity: The Optimization Advantage
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;&amp;amp;{...}&lt;/code&gt; literal syntax isn't just a convenience—it's a performance booster.&lt;/p&gt;

&lt;p&gt;Unlike the alternative, &lt;a href="https://docs.thingsdb.io/v1/collection-api/ano/" rel="noopener noreferrer"&gt;ano(...)&lt;/a&gt;, which constructs the type definition at runtime, the literal &lt;code&gt;&amp;amp;{...}&lt;/code&gt; syntax allows the ThingsDB compiler to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate optimal internal code for the data structure.&lt;/li&gt;
&lt;li&gt;Bypass runtime overhead for type creation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaway:&lt;/strong&gt; Always prefer &lt;code&gt;&amp;amp;{...}&lt;/code&gt; for static definitions. Only use the &lt;code&gt;ano(...)&lt;/code&gt; function when the type structure must be built dynamically based on runtime variables or input.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔗 Advanced Use Cases: Complex Anonymous Structures
&lt;/h2&gt;

&lt;p&gt;Anonymous types support the full richness of ThingsDB's type system, including arrays and nested structures. &lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Nested output structure&lt;/span&gt;
&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;request_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;audit_trail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;   &lt;span class="c1"&gt;// An array of audit check objects&lt;/span&gt;
        &lt;span class="na"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;int&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*enum&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Match an existing Enum definition&lt;/span&gt;
    &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="na"&gt;total_records&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;audit_trail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;  &lt;span class="c1"&gt;// Computed&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single block creates a temporary, highly-structured schema for an output object.&lt;/p&gt;

&lt;p&gt;Upgrade to &lt;a href="https://github.com/thingsdb/ThingsDB/releases/tag/v1.8.2" rel="noopener noreferrer"&gt;ThingsDB v1.8.2&lt;/a&gt; today and start enjoying the freedom and speed of on-the-fly type definition!&lt;/p&gt;

</description>
      <category>backend</category>
      <category>database</category>
      <category>news</category>
    </item>
    <item>
      <title>🚀 What if your Data Solution Contains Migration History?</title>
      <dc:creator>Jeroen van der Heijden</dc:creator>
      <pubDate>Thu, 06 Nov 2025 19:27:06 +0000</pubDate>
      <link>https://dev.to/joente/what-if-your-data-solution-contains-migration-history-335</link>
      <guid>https://dev.to/joente/what-if-your-data-solution-contains-migration-history-335</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/thingsdb/ThingsDB/releases/tag/v1.8.0" rel="noopener noreferrer"&gt;ThingsDB 1.8.0&lt;/a&gt; introduces significant features designed to help teams manage collection structure changes with confidence and clarity. The headline feature is a robust &lt;strong&gt;Commit History&lt;/strong&gt; system, complemented by improvements to structure-only exports.&lt;/p&gt;




&lt;h2&gt;
  
  
  💾 Feature 1: Version Control for Your Collection Structure
&lt;/h2&gt;

&lt;p&gt;When you work with &lt;a href="https://www.thingsdb.io" rel="noopener noreferrer"&gt;ThingsDB&lt;/a&gt;, you typically have a clear separation between two types of collection operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Routine Operations:&lt;/strong&gt; Queries used by your application at runtime (e.g., adding an item, updating a record). These are the "normal" changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structural Operations:&lt;/strong&gt; Queries used for setup or migrations (e.g., adding or changing types, updating procedures). These represent major changes to your application's data schema.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The new Commit History feature is specifically designed to version-control these &lt;strong&gt;Structural Operations&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enabling and Using Commit History
&lt;/h3&gt;

&lt;p&gt;When you choose to enable the &lt;em&gt;commit history&lt;/em&gt; on a collection using the &lt;code&gt;set_history()&lt;/code&gt; function, ThingsDB enforces a strict rule: &lt;strong&gt;any structural change must be explicitly committed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is why functions that modify a collection structure (like &lt;code&gt;new_type()&lt;/code&gt; or &lt;code&gt;set_type()&lt;/code&gt;) now require an explicit call to &lt;code&gt;commit()&lt;/code&gt; before they can be executed. This ensures every critical change is logged with an explanatory message.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example: Making a Committed Migration
&lt;/h4&gt;

&lt;p&gt;This example shows the required structure for a migration script when commit history is enabled. The &lt;code&gt;commit()&lt;/code&gt; call stages the message first, and the commit is finalized upon successful execution of the entire query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. Stage the commit message and enable structural changes&lt;/span&gt;
&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Adds an initial user type for authentication&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Perform the actual structural change&lt;/span&gt;
&lt;span class="nf"&gt;new_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;set_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;str&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;password_hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;str&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting commit object permanently stores the commit message, the user who ran the query, and the entire block of code that was executed.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔍 Feature 2: Searching and Auditing Commit History
&lt;/h2&gt;

&lt;p&gt;The new &lt;code&gt;history()&lt;/code&gt; function is your primary tool for exploring this permanent archive of structural changes. You can search, filter, and audit every critical modification made to your collections.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Filtering Options
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;history()&lt;/code&gt; function accepts several powerful filters:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;id&lt;/td&gt;
&lt;td&gt;Retrieves a specific, unique commit object instead of a list.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;first/last&lt;/td&gt;
&lt;td&gt;Limits the search to the &lt;strong&gt;X oldest&lt;/strong&gt; (&lt;code&gt;first&lt;/code&gt;) or &lt;strong&gt;X newest&lt;/strong&gt; (&lt;code&gt;last&lt;/code&gt;) commits.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;before/after&lt;/td&gt;
&lt;td&gt;Filters commits that occurred &lt;strong&gt;prior to&lt;/strong&gt; or &lt;strong&gt;after&lt;/strong&gt; a specified datetime object.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;match&lt;/td&gt;
&lt;td&gt;Uses a &lt;a href="https://docs.thingsdb.io/v1/data-types/regex/" rel="noopener noreferrer"&gt;regular expression&lt;/a&gt; to search across the commit's &lt;strong&gt;user name&lt;/strong&gt;, &lt;strong&gt;message&lt;/strong&gt;, and &lt;strong&gt;code&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;has_err&lt;/td&gt;
&lt;td&gt;Filters based on execution status: &lt;code&gt;true&lt;/code&gt; for failed commits, &lt;code&gt;false&lt;/code&gt; for successful ones.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Example: Finding Failed Migrations
&lt;/h4&gt;

&lt;p&gt;This code snippet demonstrates how to search for all commits that failed (i.e., resulted in an error) within the last year, allowing you to quickly debug structural issues:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;history&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;//app_data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;has_err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;after&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;years&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🧹 Feature 3: Pruning Old History
&lt;/h2&gt;

&lt;p&gt;History is valuable, but it shouldn't be infinite. The &lt;code&gt;del_history()&lt;/code&gt; function allows you to prune old, unnecessary records using the same powerful filters:&lt;/p&gt;

&lt;h4&gt;
  
  
  Example: Removing Old History
&lt;/h4&gt;

&lt;p&gt;This code removes all commits that were created more than six months ago in the &lt;code&gt;//stuff&lt;/code&gt; collection, helping to keep your history archive clean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;del_history&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;//stuff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;before&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;months&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ Warning: Disabling commit history via &lt;code&gt;set_history(&amp;lt;scope&amp;gt;, false)&lt;/code&gt; will &lt;strong&gt;permanently delete&lt;/strong&gt; all existing historical records for that scope. This action cannot be undone.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🎁 Bonus Feature: Improved Structure Exports
&lt;/h2&gt;

&lt;p&gt;Finally, the export function has been improved. A structure-only export (&lt;code&gt;export({dump: false})&lt;/code&gt;) now includes complex enumerators as well, ensuring you get the complete "framework" or schema of a collection without any data, making it easier to manage and deploy database templates.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt; ThingsDB 1.8.0 brings professional-grade schema versioning to your data solution. Start using Commit History today to ensure every structural change is recorded, audit-able, and traceable!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Documentation links:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.thingsdb.io/v1/collection-api/commit/" rel="noopener noreferrer"&gt;https://docs.thingsdb.io/v1/collection-api/commit/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.thingsdb.io/v1/collection-api/export/" rel="noopener noreferrer"&gt;https://docs.thingsdb.io/v1/collection-api/export/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.thingsdb.io/v1/thingsdb-api/history/" rel="noopener noreferrer"&gt;https://docs.thingsdb.io/v1/thingsdb-api/history/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.thingsdb.io/v1/thingsdb-api/del_history/" rel="noopener noreferrer"&gt;https://docs.thingsdb.io/v1/thingsdb-api/del_history/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.thingsdb.io/v1/thingsdb-api/set_history/" rel="noopener noreferrer"&gt;https://docs.thingsdb.io/v1/thingsdb-api/set_history/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>data</category>
      <category>thingsdb</category>
      <category>software</category>
      <category>versioncontrol</category>
    </item>
    <item>
      <title>🚀 Optimizing Meta Data Retrieval with ThingsDB 1.7.6</title>
      <dc:creator>Jeroen van der Heijden</dc:creator>
      <pubDate>Sat, 25 Oct 2025 10:49:26 +0000</pubDate>
      <link>https://dev.to/joente/optimizing-meta-data-retrieval-with-thingsdb-176-205k</link>
      <guid>https://dev.to/joente/optimizing-meta-data-retrieval-with-thingsdb-176-205k</guid>
      <description>&lt;p&gt;We at &lt;a href="https://www.infrasonar.com" rel="noopener noreferrer"&gt;InfraSonar&lt;/a&gt; query large volumes of meta data, resulting in many properties that are often empty lists, &lt;code&gt;false&lt;/code&gt; booleans, or empty strings.&lt;br&gt;
This redundant data increases payload size and network traffic unnecessarily.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/thingsdb/ThingsDB" rel="noopener noreferrer"&gt;ThingsDB version 1.7.6&lt;/a&gt; introduces new &lt;a href="https://docs.thingsdb.io/v1/overview/type/#wrap-prefix-flags" rel="noopener noreferrer"&gt;wrap-prefix flags&lt;/a&gt; that solve this by allowing us to exclude properties from the wrapped output if they evaluate to a "false" state.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Problem: Bloated JSON Payloads
&lt;/h2&gt;

&lt;p&gt;Consider our &lt;code&gt;Metric&lt;/code&gt; type definition &lt;em&gt;(partial, in reality it looks slightly different)&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;set_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Metric&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                &lt;span class="c1"&gt;// the metric Id as "id"&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;str&amp;lt;1:&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;Ref?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// nil for most&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;str&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// optional description&lt;/span&gt;
    &lt;span class="na"&gt;dataType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DataType&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;isRequired&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bool&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;isFileId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bool&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// false for most&lt;/span&gt;
    &lt;span class="na"&gt;displayFunction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;int&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// mapping to a display function&lt;/span&gt;
    &lt;span class="na"&gt;derived&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;[Derived]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// list of derived metrics, empty for most&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A standard &lt;code&gt;Metric(123456).wrap()&lt;/code&gt; call often returns a large object dominated by default or empty values:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(JSON)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123456&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dataType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"STRING"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"isRequired"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"isFileId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"displayFunction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"derived"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Solution: The ! and ? Wrap-Prefix Flags
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://docs.thingsdb.io/v1/overview/type/#wrap-prefix-flags" rel="noopener noreferrer"&gt;wrap-prefix flags&lt;/a&gt; enable a concise way to control data serialization based on truthiness:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;!&lt;/strong&gt; - Excludes the property if its value evaluates to &lt;code&gt;false&lt;/code&gt; (e.g., &lt;code&gt;false&lt;/code&gt;, &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;""&lt;/code&gt;, &lt;code&gt;[]&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;?&lt;/strong&gt; - Excludes the property only if its value is explicitly &lt;code&gt;nil&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Applying the Flags
&lt;/h2&gt;

&lt;p&gt;We update the &lt;code&gt;Metric&lt;/code&gt; definition by prefixing properties we want to exclude when empty or false:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;set_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Metric&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;str&amp;lt;1:&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;?&amp;amp;Ref?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// Exclude when explicitly `nil`.&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;!str&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// Exclude when false (empty string).&lt;/span&gt;
    &lt;span class="na"&gt;dataType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DataType&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;isRequired&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;!bool&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// Exclude when false.&lt;/span&gt;
    &lt;span class="na"&gt;isFileId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;!bool&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// Exclude when false.&lt;/span&gt;
    &lt;span class="na"&gt;displayFunction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;!int&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Exclude when false (value is 0).&lt;/span&gt;
    &lt;span class="na"&gt;derived&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;!&amp;amp;[Derived]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Exclude when false (empty list).&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Compact Result
&lt;/h2&gt;

&lt;p&gt;Running the same query, &lt;code&gt;Metric(123456).wrap()&lt;/code&gt;, now yields a significantly more compact result:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(JSON)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123456&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dataType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"STRING"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"isRequired"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By leveraging the &lt;code&gt;!&lt;/code&gt; and &lt;code&gt;?&lt;/code&gt; flags, we eliminate unnecessary data transfer, resulting in significantly reduced network traffic and faster query response times when dealing with meta data objects.&lt;/p&gt;

</description>
      <category>thingsdb</category>
      <category>infrasonar</category>
      <category>programming</category>
      <category>networktraffic</category>
    </item>
    <item>
      <title>Great article about using ThingsDB as a complete back-end solution, Thanks Michal!</title>
      <dc:creator>Jeroen van der Heijden</dc:creator>
      <pubDate>Sat, 07 Dec 2024 13:05:19 +0000</pubDate>
      <link>https://dev.to/joente/great-article-about-using-thingsdb-as-a-complete-back-end-solution-thanks-michal-20o8</link>
      <guid>https://dev.to/joente/great-article-about-using-thingsdb-as-a-complete-back-end-solution-thanks-michal-20o8</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/stefanak-michal" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1271912%2Fe6e52634-25f9-42ca-aee3-e5eddc86f408.jpg" alt="stefanak-michal"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/stefanak-michal/let-me-explain-a-thingsdb-todo-app-demo-2n9g" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Let me explain a ThingsDB Todo app demo&lt;/h2&gt;
      &lt;h3&gt;Michal Štefaňák ・ Aug 5 '24&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#database&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#showdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
    </item>
  </channel>
</rss>
