<?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>Forem: Romans Malinovskis</title>
    <description>The latest articles on Forem by Romans Malinovskis (@romaninsh).</description>
    <link>https://forem.com/romaninsh</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%2F3331008%2F7d4f28af-7ca3-47e3-b493-ed1d37a95925.png</url>
      <title>Forem: Romans Malinovskis</title>
      <link>https://forem.com/romaninsh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/romaninsh"/>
    <language>en</language>
    <item>
      <title>SurrealDB test case for SaaS</title>
      <dc:creator>Romans Malinovskis</dc:creator>
      <pubDate>Wed, 23 Jul 2025 14:16:44 +0000</pubDate>
      <link>https://forem.com/romaninsh/surrealdb-test-case-for-saas-38ja</link>
      <guid>https://forem.com/romaninsh/surrealdb-test-case-for-saas-38ja</guid>
      <description>&lt;p&gt;I have added a test database for SurrealDB, that looks at the scenario of a SaaS product. It's important to apply the core limitation where an authenticated user can only see records, belonging to that user. An developer-error (or even vibe-coder error) in Graph database can leak data and one of the requirements for &lt;code&gt;vantage&lt;/code&gt; is the ability to enforce fundamental entity conditions. &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%2F1rby73ml7pgm4e6akcfo.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%2F1rby73ml7pgm4e6akcfo.png" alt=" " width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If I want to query "client-&amp;gt;placed-&amp;gt;order" relation, I need both order and client to belong to my bakery:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT
    name AS product_name,
    inventory.stock AS current_inventory,
    array::first((SELECT count() FROM order WHERE bakery = bakery:hill_valley GROUP ALL)).count AS total_orders
FROM product
WHERE bakery = bakery:hill_valley AND is_deleted = false;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my latest PR to I have established a use-case that is designed specifically with "production" scenario like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/romaninsh/vantage/pull/63" rel="noopener noreferrer"&gt;https://github.com/romaninsh/vantage/pull/63&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The model already existed for PostgreSQL, but next I'm going to extend support into SurrealDB query builder.&lt;/p&gt;

</description>
      <category>surrealdb</category>
      <category>sql</category>
      <category>saas</category>
    </item>
    <item>
      <title>Arc and Mutex for SQL Expression parameters</title>
      <dc:creator>Romans Malinovskis</dc:creator>
      <pubDate>Sun, 20 Jul 2025 18:18:29 +0000</pubDate>
      <link>https://forem.com/romaninsh/arc-and-mutex-for-sql-expression-parameters-cjj</link>
      <guid>https://forem.com/romaninsh/arc-and-mutex-for-sql-expression-parameters-cjj</guid>
      <description>&lt;p&gt;I have now converted OwnedExpression to use &lt;a href="https://dev.to/romaninsh/the-expressive-protocol-3eb7"&gt;Expressive protocol&lt;/a&gt; and started to think about "LazyExpression".&lt;/p&gt;

&lt;p&gt;It's not really an expression that's lazy - it is a parameter to the expression that is lazy. &lt;/p&gt;

&lt;p&gt;So I ditched LazyExpression entirely, and instead implemented support for Arc and Arc&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/romaninsh/vantage/pull/61" rel="noopener noreferrer"&gt;https://github.com/romaninsh/vantage/pull/61&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;, a parameter to Expression engine could be either a value or a nested expression. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now&lt;/strong&gt;, a parameter to Expression can also be Arc or Mutex holding a value or a nested expression.&lt;/p&gt;

&lt;p&gt;In other words a query builder (which is mutable) can now be used inside another query. Of course we can still defer things too, which does give us a lot of flexibility:&lt;/p&gt;

&lt;p&gt;Suppose you have a custom struct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;GreetingQuery&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&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;a struct would generate a custom query with current name if it is part of a template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nb"&gt;From&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;GreetingQuery&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;OwnedExpression&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;GreetingQuery&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OwnedExpression&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;expr!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt;&lt;span class="nf"&gt;.clone&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="k"&gt;impl&lt;/span&gt; &lt;span class="nb"&gt;From&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GreetingQuery&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;IntoExpressive&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OwnedExpression&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;GreetingQuery&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;IntoExpressive&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;nested&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;OwnedExpression&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;greeting&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets construct our struct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Mutex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GreetingQuery&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"world"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&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;this can now be cloned and shared all throughout the app. Some expressions may rely on it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;expr!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"select {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;now every time expression is executed it will acquire a value from a mutex:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="nf"&gt;.execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// select hello "world"&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="nf"&gt;.lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"vantage"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="nf"&gt;.execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// select helo "vantage"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>rust</category>
      <category>sql</category>
      <category>vantage</category>
    </item>
    <item>
      <title>The "Expressive" protocol</title>
      <dc:creator>Romans Malinovskis</dc:creator>
      <pubDate>Sat, 19 Jul 2025 22:56:32 +0000</pubDate>
      <link>https://forem.com/romaninsh/the-expressive-protocol-3eb7</link>
      <guid>https://forem.com/romaninsh/the-expressive-protocol-3eb7</guid>
      <description>&lt;p&gt;Followed the &lt;a href="https://dev.to/romaninsh/rust-query-builder-for-sql-an-surrealdb-32pf"&gt;introduction of 3 query builders&lt;/a&gt; (SQL, MongoDB and SurrealDB query dialects), I still felt that my architecture is still uncertain. While implementing builders, I wasn't sure if I should re-use existing expression engines or build a new one or if I should go for Owned or Lazy expressions. Also I wasn't sure how expression engines would interact with multiple databases.&lt;/p&gt;

&lt;p&gt;My latest PR is a step to resolve all those issues, introducing a clean way to implement Expression Engines with the &lt;code&gt;Expressive protocol&lt;/code&gt; (&lt;a href="https://github.com/romaninsh/vantage/pull/58" rel="noopener noreferrer"&gt;https://github.com/romaninsh/vantage/pull/58&lt;/a&gt;)&lt;/p&gt;

&lt;h1&gt;
  
  
  Implementing custom Expression Engines
&lt;/h1&gt;

&lt;p&gt;My PR contains a "mock" expression engine, but I intend to re-shape &lt;code&gt;OwnedExpression&lt;/code&gt; into a proper implementation of &lt;code&gt;expressive protocol&lt;/code&gt;. It is possible though to create new expression engines relatively easily.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Implement necessary traits:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Expressive&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyExpr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;MyExpr&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;into_expressive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;IntoExpressive&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyExpr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MyExpr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;DataSource&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyExpr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;MyDB&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nb"&gt;Fn&lt;/span&gt;&lt;span class="k"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Value&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;ol&gt;
&lt;li&gt;Implement or derive Debug trait&lt;/li&gt;
&lt;li&gt;Implement From for IntoExpressive&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The standard way for Rust to cast types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyExpr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;From&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Identifier&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;IntoExpressive&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyExpr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;.. allows Builders to use a wide range of types to to shift weight of implementation into expression engine&lt;/p&gt;

&lt;p&gt;Once your expression engine is ready - you can use your &lt;code&gt;Expressive&lt;/code&gt; type. Define &lt;code&gt;my_expr!&lt;/code&gt; macro to simplify your code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;my_expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;my_expr!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"select {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Identifier&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Nesting Expressions
&lt;/h1&gt;

&lt;p&gt;Expressive protocol requires implementations to allow nested templates and also support closures for deferred values. If a particular database is incapable of using expressions - nested templates can also be converted into deferred values. &lt;/p&gt;

&lt;p&gt;Creating and manipulating expressions must always be sync(), but it may carry some &lt;code&gt;closures&lt;/code&gt; to be evaluated later. Obviously if the database can do logic server-side we prefer that.&lt;/p&gt;

&lt;p&gt;Here are the examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// nesting expressions:&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;my_expr!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NOT({})"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;my_expr!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"true OR false"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// deferred closures:&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;my_expr!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{} + 5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nd"&gt;json!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Executing Queries
&lt;/h1&gt;

&lt;p&gt;To execute a query, you need a database driver that implements &lt;code&gt;DataSource&amp;lt;MyExpr&amp;gt;&lt;/code&gt;. Yeah! you can support multiple expression engines inside the same datasource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;DataBase&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="nf"&gt;.execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Calling &lt;code&gt;execute()&lt;/code&gt; will evaluate deferred closures first, then will run expression and return result.&lt;/p&gt;

&lt;p&gt;There is however, one more feature - ability to defer expressions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;db1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;DataBase&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;db2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;GeoDB&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;subquery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;my_expr!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s"&gt;"select country_id from countries where ip={}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="n"&gt;ip&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;my_expr!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s"&gt;"select * from data where country = {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;db2&lt;/span&gt;&lt;span class="nf"&gt;.defer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subquery&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db1&lt;/span&gt;&lt;span class="nf"&gt;.execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this scenario, we are still working with a single await, however query would be performed across 2 databases. Result of subquery will be automatically inserted into expression which will be executed too.&lt;/p&gt;

&lt;p&gt;As you might have noticed - we can even use different implementation of expressive protocol here.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lazy expressions and snapshots
&lt;/h1&gt;

&lt;p&gt;Previously I implemented OwnedExpression and LazyExpression, but now I can make them interact since they both will be implementing the same protocol. &lt;/p&gt;

&lt;p&gt;Builder implementations may decide which expressions engine to use and would still be compatible with everything around them. &lt;/p&gt;

&lt;p&gt;Into&amp;lt;&amp;gt; can even allow you to cast between different expression engines if they are compatible or automatically defer if they are not. &lt;/p&gt;

&lt;p&gt;Now I can now go back to the SelectQuery builders and polish them next.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>sql</category>
      <category>surrealdb</category>
      <category>vantage</category>
    </item>
    <item>
      <title>Rust Query Builder for SQL an SurrealDB</title>
      <dc:creator>Romans Malinovskis</dc:creator>
      <pubDate>Thu, 10 Jul 2025 20:37:32 +0000</pubDate>
      <link>https://forem.com/romaninsh/rust-query-builder-for-sql-an-surrealdb-32pf</link>
      <guid>https://forem.com/romaninsh/rust-query-builder-for-sql-an-surrealdb-32pf</guid>
      <description>&lt;p&gt;SurrealDB is a new multi-modal database, which is in active development. And while I am really impressed by the features - users report performance issues.&lt;/p&gt;

&lt;p&gt;I also want to use SurrealDB in my project, but if we do run into performance issues - we better have a way to switch to a different database. A custom query language SurrealQL makes it pretty difficult to perform such a change. I've observed many users trying SurrealDB a year ago and having exactly the same issue.&lt;/p&gt;

&lt;p&gt;I continue to refactor my project - Vantage, and today I have a very basic support for Query Building, but now it's done in a vendor-independent way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query builders
&lt;/h2&gt;

&lt;p&gt;Query builders that I've mentioned &lt;a href="https://dev.to/romaninsh/sql-expressions-in-rust-4p5n"&gt;in my previous post&lt;/a&gt; typically support only a single query language. In Vantage my expression builder supports various vendors and query language dialects and therefore should be able to easily support transition between PostgreSQL and SurrealDB.&lt;/p&gt;

&lt;p&gt;A new implementation of Expression engine (0.3) allows this behaviour and makes it much more intuitive to implement a query builder:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/romaninsh/vantage/pull/53" rel="noopener noreferrer"&gt;https://github.com/romaninsh/vantage/pull/53&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As example, here is implementation of "identifiers". In SQL if your column or table is using reserved keyword, you can use &lt;code&gt;backtick&lt;/code&gt; to escape it. While SurrealDB supports backticks, it also supports &lt;code&gt;⟨mytable⟩&lt;/code&gt;. Here is a reference implementation for identifier escaping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#[derive(Debug, Clone)]
pub struct Identifier {
    identifier: String,
}

impl Identifier {
    pub fn new(identifier: impl Into&amp;lt;String&amp;gt;) -&amp;gt; Self {
        Self {
            identifier: identifier.into(),
        }
    }

    fn needs_escaping(&amp;amp;self) -&amp;gt; bool {
        let reserved_keywords = [
            "DEFINE", "CREATE", "SELECT", "UPDATE",
            "DELETE", "FROM", "WHERE", "SET", "ONLY",
            "TABLE",
        ];

        let upper_identifier = self.identifier.to_uppercase();

        // Check if it contains spaces or is a reserved keyword
        self.identifier.contains(' ') 
          || reserved_keywords.contains(&amp;amp;upper_identifier.as_str())
    }
}

impl Into&amp;lt;OwnedExpression&amp;gt; for Identifier {
    fn into(self) -&amp;gt; OwnedExpression {
        if self.needs_escaping() {
            expr!(format!("⟨{}⟩", self.identifier))
        } else {
            expr!(self.identifier)
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can then be intuitively used like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;expr!(
  "SELECT VALUE {} AS {}",
  Identifier::new(base_expr),
  Identifier::new(alias)
),
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the render_field() construct can easily rely on identifier as needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fn render_fields(&amp;amp;self) -&amp;gt; OwnedExpression {
    if self.fields.is_empty() {
        expr!("*")
    } else {
        let field_expressions: Vec&amp;lt;OwnedExpression&amp;gt; = self
            .fields
            .iter()
            .map(|(alias: &amp;amp;Option&amp;lt;String&amp;gt;, field: &amp;amp;OwnedExpression)| {
                if let Some(alias) = alias {
                    expr!("{} AS {}", field.clone(), Identifier::new(alias.clone()))
                } else {
                    field.clone()
                }
            })
            .collect();
        OwnedExpression::from_vec(field_expressions, ", ")
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Code for each query-builder is clean and can focus on language features of specific query dialect, rather than string formatting.&lt;/p&gt;

&lt;p&gt;Next, I'm going to revisit the concept of LazyExpressions some more and possibility to use cross-vendor queries in expressions seamlessly.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>sql</category>
      <category>surrealdb</category>
    </item>
    <item>
      <title>SQL Expressions in Rust</title>
      <dc:creator>Romans Malinovskis</dc:creator>
      <pubDate>Tue, 08 Jul 2025 11:03:41 +0000</pubDate>
      <link>https://forem.com/romaninsh/sql-expressions-in-rust-4p5n</link>
      <guid>https://forem.com/romaninsh/sql-expressions-in-rust-4p5n</guid>
      <description>&lt;p&gt;Hello,&lt;/p&gt;

&lt;p&gt;I am currently researching a better way to interface between Rust application and SQL (and non-SQL) servers. In the past I have developed &lt;a href="https://atk4-data.readthedocs.io/en/develop/expressions.html" rel="noopener noreferrer"&gt;SQL expressions&lt;/a&gt; as part of Agile Data.&lt;/p&gt;

&lt;p&gt;For my new Rust project (&lt;a href="https://github.com/romaninsh/vantage" rel="noopener noreferrer"&gt;Vantage&lt;/a&gt;) I am now looking again at the  correct approach to render expressions. &lt;a href="https://romaninsh.github.io/vantage/2-expressions-and-queries.html" rel="noopener noreferrer"&gt;Current implementation&lt;/a&gt; is lacking async support and this makes it impossible to embed cross-database requests.&lt;/p&gt;

&lt;h1&gt;
  
  
  First - what is an expression?
&lt;/h1&gt;

&lt;p&gt;At some point in life everyone would write this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let query = format!(
  "SELECT * FROM product WHERE name = \"{}\"",
  user_name
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A much better way is to rely on a specialised expression language. Here is SQLx for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let row: (i64,) = sqlx::query_as("SELECT $1")
        .bind(150_i64)
        .fetch_one(&amp;amp;pool).await?;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  A more sophisticated Expression Engine
&lt;/h1&gt;

&lt;p&gt;For my purposes - conventional SQL expressions are too basic. Some of the features they lack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Limited number of types&lt;/li&gt;
&lt;li&gt;Composable&lt;/li&gt;
&lt;li&gt;First class citizen&lt;/li&gt;
&lt;li&gt;Not database-agnostic&lt;/li&gt;
&lt;li&gt;Not recursive&lt;/li&gt;
&lt;li&gt;Must take ownership&lt;/li&gt;
&lt;li&gt;No async support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For types - SQL databases operate with JSON or Geo primitives as well as 3rd party crates like &lt;a href="https://docs.rs/chrono/latest/chrono/" rel="noopener noreferrer"&gt;Chrono&lt;/a&gt; have types for duration, that you may also want to use inside queries. What if there is no built-in support for your custom type.&lt;/p&gt;

&lt;h2&gt;
  
  
  Composability of Expressions
&lt;/h2&gt;

&lt;p&gt;Composability is a big requirement for me. My query builders are literally operate with dozens of expressions, connecting them together and rendering. When it comes to supporting various types in Expression engine - then expression itself should be suitable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let my_expr = expr!("select id from ({})", expr!("select * from user"));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having named parameters also is great: &lt;code&gt;select {total_sum} from orders&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  First class citizen
&lt;/h2&gt;

&lt;p&gt;Expression should be First class citizen anywhere else. Query builder such as &lt;a href="https://docs.rs/sea-query/latest/sea_query/query/struct.SelectStatement.html#method.order_by" rel="noopener noreferrer"&gt;sea_query implements order&lt;/a&gt; in a very rigid way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let query = Query::select()
    .order_by(Glyph::Image, Order::Desc)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SQL languages can order by expression, so query builder should be able to accept expression, not only a field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let query = query.order_by(expr!("qty * (price - {})", 20));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Identifiers like &lt;code&gt;table_name&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Expression engines mostly deal with parametric arguments, but they should do a better job with identifiers also. Those are not passed as parameters, but also require validation and possible escaping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let expr = expr!("select * from {}", Identifier("users"));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Operations
&lt;/h2&gt;

&lt;p&gt;Diesel creates an interesting trend with implementing operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let downloads = version_downloads
  .filter(version_id.eq(any(versions)))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Their implementation relies on static field, but operations could work beautifully as an extension to expressions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let expr = expr.and(expr!("len(name) &amp;lt; {}", 3);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Database Agnostics and Async
&lt;/h2&gt;

&lt;p&gt;Modern reality is that application often have to work with multiple data sources - databases from various vendors, APIs, local caches, etc. &lt;/p&gt;

&lt;p&gt;Expression engine should be capable of bridging the gap. The biggest challenge is that a expression usually is a lightweight construct which has no binding to a specific data source. If we bridge between data sources - there should be a possibility to cross this gap.&lt;/p&gt;

&lt;p&gt;Consider this query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select * from user where country_id in (
  select id from country where is_eu
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What if a "country" table moves elsewhere or is cached differently?&lt;/p&gt;

&lt;p&gt;Executing such a query would be an async operation, however manipulating it is not.   &lt;/p&gt;

&lt;h2&gt;
  
  
  Owned vs Shared ownership
&lt;/h2&gt;

&lt;p&gt;Most backend services live for a single request. Rust language works for desktop applications too. Imagine you have a table with a filter controls on top. Changing filter parameter should modify part of a query to fetch table data, but there is no reason really to re-build entire query.&lt;/p&gt;

&lt;p&gt;Therefore there should be a concept that would allow part of an expression to be externally modified.&lt;/p&gt;

&lt;h1&gt;
  
  
  My Current approach
&lt;/h1&gt;

&lt;p&gt;Previously Vantage had "Expr" and "Expr_Arc", however it had no async support and therefore couldn't handle cross-database queries. The implementation worked really well inside query building logic and even with fields, but I needed a more refined approach.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/romaninsh/vantage/pull/52" rel="noopener noreferrer"&gt;https://github.com/romaninsh/vantage/pull/52&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;This is a new expression engine implementation and for now it only tackles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Composable - lazy expressions can contain other queries&lt;/li&gt;
&lt;li&gt;Extensible parameters - even async&lt;/li&gt;
&lt;li&gt;Full and Lazy ownership&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will continue with the refactor.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>sql</category>
    </item>
    <item>
      <title>Hello Dev.to</title>
      <dc:creator>Romans Malinovskis</dc:creator>
      <pubDate>Mon, 07 Jul 2025 11:07:57 +0000</pubDate>
      <link>https://forem.com/romaninsh/hello-devto-52gg</link>
      <guid>https://forem.com/romaninsh/hello-devto-52gg</guid>
      <description>&lt;p&gt;Hello,&lt;/p&gt;

&lt;p&gt;I learned PHP in 1998 and fell in love with the language. Never a fan of projects like WordPress or popular frameworks, I appreciated the language in its pure form. My work led me to releasing Agile Toolkit and creating successful consultancies around it, and even though I decided to move on about ten years ago, Agile Toolkit &lt;a href="https://ui.atk4.org/demos/index.php" rel="noopener noreferrer"&gt;lives on&lt;/a&gt; thanks to the community.&lt;/p&gt;

&lt;p&gt;One of the greatest advantages of ATK is the ability to operate with data models in a very unusual yet super-efficient way. Real projects which use ATK significantly reduce the number of database queries simply because those queries are built dynamically inside PHP before they are executed. This makes it possible for UI components (like filters or pagination) to naturally integrate with no additional code lines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entity Abstraction
&lt;/h2&gt;

&lt;p&gt;There are presently two views on how applications interact with the SQL databases - either through custom-written SQL queries (SQLx for Rust) or with ORM (Diesel or SeaORM). Each has pros and cons.&lt;/p&gt;

&lt;p&gt;But what if there is a third way?&lt;/p&gt;

&lt;p&gt;Agile Data (&lt;a href="https://github.com/atk4/data" rel="noopener noreferrer"&gt;https://github.com/atk4/data&lt;/a&gt;) implements an approach of DataSet abstractions - implemented in PHP. While it is similar to ORM in syntax - it operates on an entirely different level, allowing you to dynamically build multi-table queries, abstract away expression logic, and introduce extensions (such as soft-delete). All of that &lt;a href="https://github.com/atk4/ui/blob/develop/demos/collection/crud.php" rel="noopener noreferrer"&gt;integrates into UI directly&lt;/a&gt;) making ATK a "low-code" framework that makes you write very little code.&lt;/p&gt;

&lt;p&gt;If you are curious how powerful it is - &lt;a href="http://www.sortmybooks.com" rel="noopener noreferrer"&gt;www.sortmybooks.com&lt;/a&gt; is a full-featured online accounting suite (much more powerful than Xero), built entirely in ATK and extensively using Agile Data for UI and reports.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving On
&lt;/h2&gt;

&lt;p&gt;A few years ago, I started learning Rust, and while I didn't have much time, I decided to write some backend application in Axum. To this day, I could not find a crate to enable pagination across all my data endpoints universally.&lt;/p&gt;

&lt;p&gt;I thought if we want Rust to be more "appropriate" for serious business applications— an Entity Framework is absolutely required.&lt;/p&gt;

&lt;p&gt;Rust, however, has these unusual ownership and trait systems. On top of that, the desire to apply types to anything makes it even harder to come up with a "good-looking" data abstraction framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  My next challenge - Vantage
&lt;/h2&gt;

&lt;p&gt;This seemed like a very interesting challenge, and I began working on &lt;a href="https://github.com/romaninsh/vantage" rel="noopener noreferrer"&gt;Vantage&lt;/a&gt;. However, as my understanding of Rust continues to evolve, my project will probably undergo several idiomatic rewrites.&lt;/p&gt;

&lt;p&gt;I feel that I need to dump my thoughts and ideas somewhere. It helps me reflect and refine my approach, and if this would also be of interest to anyone, GREAT! &lt;/p&gt;

&lt;h2&gt;
  
  
  What am I planning next?
&lt;/h2&gt;

&lt;p&gt;Vantage cannot be lightweight, otherwise it will fall as "yet-another-light ORM". There are fundamental targets it must achieve in order to advance forward. Here I wanted just to list some of my design goals.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Expressions&lt;/strong&gt; - Implement an extensible interface for nested expressions, which can operate across multiple data sources and demonstrate great policy towards async execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistence Mapping&lt;/strong&gt; - Create a protocol for persistence mapping. Think of this as "serde", which supports multiple data sources, relationships, conditions, optional fields, expressions and tons of other extensions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query Builders&lt;/strong&gt; - This is a glue between the above two components. Provided with a persistence mapping logic - translate operations (such as reading, adding, deleting) into expressions by fully utilising query languages such as SQL or SurrealDB query language.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are many benefits that can come out of this - but getting those 3 fundamentals correct and aligning them with Rust coding paradigms with lovely syntactic sugar is essential.&lt;/p&gt;

&lt;p&gt;If you want to see my current work-in-progress: &lt;a href="https://github.com/romaninsh/vantage" rel="noopener noreferrer"&gt;https://github.com/romaninsh/vantage&lt;/a&gt;, but I also plan to post some updates and reflections or my account here.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>sql</category>
      <category>php</category>
    </item>
  </channel>
</rss>
