&gt; But that&#x27;s what RethinkDB&#x27;s query builder is. You construct a free-standing AST, let&#x27;s call it x, then call x.run(conn) or (IIRC) conn.run(x) to run it.<p>That&#x27;s not what <a href="http:&#x2F;&#x2F;rethinkdb.com&#x2F;docs&#x2F;quickstart&#x2F;" rel="nofollow">http:&#x2F;&#x2F;rethinkdb.com&#x2F;docs&#x2F;quickstart&#x2F;</a> says. It shows a connection that exposes context-bound Builder methods with trigger (insert, changes, run, etc) methods.<p>Though that is a little more high-level than I thought at first. You&#x27;re right that not everything is a `run`.<p>It feels like a stretch to call it an AST but maybe that&#x27;s just a question of taking a closer look at the wire protocol.<p>&gt; An SQL query building API can easily be understood by developers that know SQL<p>It can, but it&#x27;s more complex with more mental overhead. It&#x27;s the difference between Active Record scopes and HQL. Where HQL is a flavor of SQL with extensions for object notation that can feel pretty similar to qualified database.table.column syntax, AR scopes are (I feel) objectively more complex.<p>&gt; you don&#x27;t need to syntax check it<p>You actually do. It&#x27;s just your interpreter or compiler that does so. A compiler plugin (for native support) or embedded DSL such as LINQ or the Slick DSL can accomplish the same for SQL though.<p>&gt; it&#x27;s hard not to need to dynamically construct queries anyway<p>True, but IME a Builder based API is a poor choice for that unless the scope of the Builder usage is private to a single class. Once you start passing around a mutable builder you can easily end up with unintentional side-effects.<p>In SQL builders it&#x27;s easy for example to override a sort clause to be incompatible with a previous aggregation if the builder doesn&#x27;t go to a lot of care to make that impossible. And then you might well also end up in a situation where you need to discard previously defined scopes (such as the problematic aggregation). How do you do that?<p>IME once you let a builder escape a very narrow scope (class-level at the most) you create a great maintenance burden and create a new category of very difficult to avoid bugs.<p>Interpolation&#x2F;materialization of terms into an AST is a much simpler, and encourages simpler designs (IME). IOW: Writing builders should be up to the user. If the &quot;driver&quot; provides it, it&#x27;s too easy to fall into anti-pattern traps with unintended consequences.<p>That&#x27;s just my opinion. But bottom line, the API is much too high-level for my taste. I&#x27;d prefer something much closer to Cloudant, with a well defined syntax (if you imagined the HTTP end-points query-parameters folded into the request body instead; query vs update APIs aren&#x27;t super consistent on which goes where, eg: _deleted or _rev vs keys or include_docs).<p>Such a high-level interface for building queries being provided for the driver is fairly unique among the databases I&#x27;m familiar with. And while it seems like a nice affordance for JavaScript users, it feels like an anti-pattern for other platforms.<p>I don&#x27;t mean to trash RethinkDB though. On the other end of the spectrum you have DynamoDB&#x27;s very cumbersome API. If I had to pick between the two I&#x27;d take RethinkDB&#x27;s any day. I just think a more formal syntax for other platforms would make it feel less JavaScript centric and more at home on other platforms.