<?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: Roman K</title>
    <description>The latest articles on Forem by Roman K (@romeerez).</description>
    <link>https://forem.com/romeerez</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%2F507537%2Fc5134c54-cde5-4f4c-944c-d7d77124e2ab.jpeg</url>
      <title>Forem: Roman K</title>
      <link>https://forem.com/romeerez</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/romeerez"/>
    <language>en</language>
    <item>
      <title>Type-safe Express validations with Zod</title>
      <dc:creator>Roman K</dc:creator>
      <pubDate>Tue, 25 Apr 2023 11:46:19 +0000</pubDate>
      <link>https://forem.com/romeerez/type-safe-express-validations-with-zod-26ip</link>
      <guid>https://forem.com/romeerez/type-safe-express-validations-with-zod-26ip</guid>
      <description>&lt;p&gt;TypeScript is known for its ability to catch the bugs early when writing the code, rather than letting users to find problems for us in production. But TypeScript has one downside: its checks are easily disabled by &lt;code&gt;any&lt;/code&gt; type. When we have &lt;code&gt;any&lt;/code&gt; types in our code, TS becomes just a wordy version of JavaScript, and it won't provide any guarantees.&lt;/p&gt;

&lt;p&gt;I want to share &lt;a href="https://github.com/romeerez/express-ts-handler"&gt;a library&lt;/a&gt; which was inspired by the fact that all or almost all examples you can find on the internet for how to do validations with Express are not type safe, which means we can still make mistakes that aren't caught by TypeScript.&lt;/p&gt;

&lt;p&gt;At the moment, my library only supports &lt;a href="https://github.com/colinhacks/zod"&gt;Zod&lt;/a&gt; which is easy to use and it can infer types for us.&lt;/p&gt;

&lt;p&gt;To see the problem, try googling "express typescript zod" to see how it's commonly done.&lt;/p&gt;

&lt;p&gt;Example from the first link:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/create&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataSchema&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Response&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;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&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;Here &lt;code&gt;req.body&lt;/code&gt; is of type &lt;code&gt;any&lt;/code&gt;, so we can easily break the code inside of route handler by changing a &lt;code&gt;dataSchema&lt;/code&gt; and have a bug in production:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/create&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataSchema&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// access any property, no matter how `dataSchema` looks like&lt;/span&gt;
    &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;one&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;two&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;three&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&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;We can use &lt;code&gt;z.infer&amp;lt;typeof dataSchema&amp;gt;&lt;/code&gt; to let typescript know that this &lt;code&gt;any&lt;/code&gt; type should be treated as &lt;code&gt;dataSchema&lt;/code&gt; type. And it actually solves the problem. But this is a workaround, and we can still make a mistake, we can forget to specify this &lt;code&gt;as&lt;/code&gt;, we can accidentally write &lt;code&gt;typeof otherSchema&lt;/code&gt;, and it's more code to write.&lt;/p&gt;

&lt;p&gt;Second link is &lt;a href="https://github.com/RobinTail/express-zod-api"&gt;express-zod-api&lt;/a&gt;, it looks similar to my library, but instead of validating &lt;code&gt;req.params&lt;/code&gt;, &lt;code&gt;req.query&lt;/code&gt;, and &lt;code&gt;req.body&lt;/code&gt;, it offers &lt;code&gt;input&lt;/code&gt; and &lt;code&gt;output&lt;/code&gt; instead. This makes it very different from usual Express workflows.&lt;/p&gt;

&lt;p&gt;The rest of google results seems pretty irrelevant, but if you try to search &lt;code&gt;express zod typescript&lt;/code&gt; on dev.to you'll also see a lot of post of how to make it in the unsafe way. I didn't find a single example of how make it properly.&lt;/p&gt;

&lt;p&gt;When we are making a public API interfaces, it seems to be important to return correct responses. It seems so, but again, you won't find any examples over the internet of how to validate and type-check the response type with Express.&lt;/p&gt;

&lt;p&gt;Another issue is &lt;code&gt;req.user&lt;/code&gt; which is commonly used for storing a current user record. No matter if you have used the middleware or not, all existing examples have it defined always.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/romeerez/express-ts-handler"&gt;express-ts-handler&lt;/a&gt; solves all of the mentioned problems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/path/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="c1"&gt;// middleware is setting req.user&lt;/span&gt;
    &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authorizeUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// coerce and validate id from route path&lt;/span&gt;
    &lt;span class="na"&gt;params&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="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coerce&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// validate query string&lt;/span&gt;
    &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&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="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// validate request body&lt;/span&gt;
    &lt;span class="na"&gt;body&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="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// validate route response&lt;/span&gt;
    &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// finally, route handler. It may be sync or async&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// req.user has the exact type which was set by the middleware&lt;/span&gt;
      &lt;span class="nx"&gt;req&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="nx"&gt;id&lt;/span&gt;

      &lt;span class="c1"&gt;// all the data is typed properly&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="c1"&gt;// { id: number }&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="c1"&gt;// { key: string }&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="c1"&gt;// { name: string }&lt;/span&gt;

      &lt;span class="c1"&gt;// TS error when trying to return extra fields:&lt;/span&gt;
      &lt;span class="c1"&gt;// return { prop: true, extra: true }&lt;/span&gt;

      &lt;span class="c1"&gt;// returned data is validated when NODE_ENV !== production&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;prop&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="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;Let me know what you think about it!&lt;/p&gt;

</description>
      <category>express</category>
      <category>typescript</category>
      <category>zod</category>
    </item>
    <item>
      <title>Announcing a new TypeScript ORM</title>
      <dc:creator>Roman K</dc:creator>
      <pubDate>Fri, 03 Feb 2023 20:58:29 +0000</pubDate>
      <link>https://forem.com/romeerez/announcing-a-new-typescript-orm-1pdm</link>
      <guid>https://forem.com/romeerez/announcing-a-new-typescript-orm-1pdm</guid>
      <description>&lt;p&gt;There are already a lot of ORMs for node.js with different strong and weak sides, but still, this is not enough, there is still not a single tool to cover typical needs simultaneously:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Type safety&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flexibility&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easiness of use for more complex cases&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Performance&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why &lt;a href="https://orchid-orm.netlify.app/" rel="noopener noreferrer"&gt;Orchid ORM&lt;/a&gt; was created - to answer all of these challenges!&lt;/p&gt;

&lt;p&gt;Before creating a new ORM, I examined 5 top popular node.js ORMs and 2 query builders to find out if there is a "go to" solution I can safely pick for any project without worrying, and have concluded there is no such tool I was looking for.&lt;/p&gt;

&lt;p&gt;In this post, I'll cover how different ORMs and query builders are responding to expectations, and what &lt;code&gt;Orchid&lt;/code&gt; can offer in comparison.&lt;/p&gt;

&lt;h2&gt;
  
  
  Raw SQL vs query builders vs ORMs
&lt;/h2&gt;

&lt;p&gt;But why do we need ORMs at all, isn't raw SQL or a query builder all we need?&lt;/p&gt;

&lt;p&gt;They operate on different levels of abstraction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Raw SQL means writing SQL manually, with no abstraction at all. This makes it hard to work with dynamic queries. Imagine you're building an endpoint for fetching a list of posts, and the client can send various parameters: how to order, filtering, pagination, and more. It is all possible with Raw SQL, but you'll end up with a much larger amount of code, that will look messy, and be vulnerable to SQL injections unless done very carefully.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Query Builder solves the problem of dynamic queries, but it doesn't abstract away relations. When you do a join on two tables, and if one record in the left table has many records on the right, this will cause duplications of the left table data. This is solvable by using GROUP BY, or by using a subquery, or by de-duplicating data on the JS side, but this is cumbersome. In addition, if the relationship is complex (multiple tables are involved), you may want to use it in various places, it causes duplicating of quite complex code parts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ORMs solve the problems above, but they are often criticized for generating inefficient code and being not flexible, while Raw SQL and Query Builders are better at this. The goal of Orchid ORM is to prove that ORM can have all the benefits of Query Builder and ORMs combined.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Type safety
&lt;/h2&gt;

&lt;p&gt;I believe that TypeScript is the best thing that happened to JavaScript. If before it was normal to be extremely careful when changing something, we had to keep everything in our head and search for all places where some object is used to update the code accordingly, now we have the luxury of TS pointing to where we need to update the code.&lt;/p&gt;

&lt;p&gt;Ideally, ORM must be capable of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Preserving a correct type when selecting specific columns&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Knowing which relations were included&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Different operations are available for different types: "lower than" and "greater than" are available for numeric types, "contains", and "starts with" are available for text types, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mixing a raw statement into a query with the ability to specify its type&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most of the ORMs always have a full record as a returned type, ignoring selecting a subset of columns. This is done by design in OOP-inspired ORMs, where you have an "Entity" and it must be always fully loaded. So for some people, this point might be not a problem, but a valid and preferred approach. But for me, an ideal ORM allows you to select only what is needed and preserve the correct types. Among all popular ORMs in node.js, only Prisma serves this purpose well.&lt;/p&gt;

&lt;p&gt;Knowing which relations were included: slowly but steadily things are improving, and some ORMs are gaining this ability. &lt;code&gt;MikroORM&lt;/code&gt; gained this last year, and &lt;code&gt;Sequelize&lt;/code&gt; has an actively developed alpha version where it probably can do this.&lt;/p&gt;

&lt;p&gt;Mixing a raw statement into a query with the ability to specify its type: as far as I know, not a single ORM can do this.&lt;/p&gt;

&lt;p&gt;Let me share a use case to demonstrate how Orchid ORM is solving the points from above:&lt;/p&gt;

&lt;p&gt;Imagine two tables, posts, and likes. Users can like the post.&lt;/p&gt;

&lt;p&gt;We want to load multiple posts with specific fields (point #1).&lt;/p&gt;

&lt;p&gt;If this is an authorized request we want to load a boolean whether the post is liked by a current user (including data from the related table, point #2).&lt;/p&gt;

&lt;p&gt;If this request is not authorized, we don't have a &lt;code&gt;currentUserId&lt;/code&gt;, so we're selecting a constant &lt;code&gt;false&lt;/code&gt; instead of a related table (point #4). &lt;code&gt;t.boolean()&lt;/code&gt; here indicates the returning type of SQL expression.&lt;/p&gt;

&lt;p&gt;Filter posts by title containing a word to show #3.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&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;body&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;liked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentUserId&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;likes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentUserId&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;false&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="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;word&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting type is completely inferred and equals to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;liked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
&lt;span class="p"&gt;}[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Flexibility
&lt;/h2&gt;

&lt;p&gt;Except they're not helping with relations, query builders are great at their flexibility, they are allowed to build complex queries with nested sub-queries.&lt;/p&gt;

&lt;p&gt;ORMs are handling this differently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;exposing only a limited interface (&lt;code&gt;Prisma&lt;/code&gt;, &lt;code&gt;Sequelize&lt;/code&gt;), so they become unusable when you need more control over a query.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;switch between a limited interface and a query builder (&lt;code&gt;MikroORM&lt;/code&gt;, &lt;code&gt;TypeORM&lt;/code&gt;). This feels like using two different libraries, switching between two different sets of limitations. &lt;a href="https://github.com/koskimas/kysely" rel="noopener noreferrer"&gt;Kysely&lt;/a&gt; is a nice query builder with good TS support, but MikroORM is using &lt;a href="https://knexjs.org/guide/query-builder.html#knex" rel="noopener noreferrer"&gt;Knex&lt;/a&gt; instead so you're losing TS, and &lt;code&gt;TypeORM&lt;/code&gt; has a custom query builder, less user-friendly than &lt;code&gt;Knex&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Built on top of query builder (&lt;code&gt;Objection&lt;/code&gt;, &lt;code&gt;OrchidORM&lt;/code&gt;) - in such a way, querying feels natural and remains powerful.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Query builder-based ORM allows building the query step by step, adding pieces based on conditions. &lt;code&gt;Orchid ORM&lt;/code&gt; example code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&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;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;or&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&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="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&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="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;newer&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="nx"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DESC&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="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;older&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="nx"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ASC&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Orchid ORM&lt;/code&gt; has a custom query builder, specifically designed to be as TypeScript-friendly as possible. Query builder here is inspired by &lt;code&gt;Knex&lt;/code&gt; and supports all the same query methods (and more).&lt;/p&gt;

&lt;p&gt;To make things even cleaner, &lt;code&gt;Orchid ORM&lt;/code&gt; has a &lt;a href="https://orchid-orm.netlify.app/guide/orm-repo.html" rel="noopener noreferrer"&gt;repository feature&lt;/a&gt;, that allows hiding complex or repeated parts under custom methods. In the following example, &lt;code&gt;search&lt;/code&gt; and &lt;code&gt;customOrder&lt;/code&gt; are custom methods defined somewhere else, and you can build nice looking easily readable queries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;postRepo&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;customOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Easiness of use for more complex cases
&lt;/h2&gt;

&lt;p&gt;Let's say we want to load post records, include the post author, tags, and a few last comments, and comments should include the author. Here is how it looks with &lt;code&gt;Orchid&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&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;title&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;description&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;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&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;firstName&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;lastName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postTags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tagName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;lastComments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;comments&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&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;text&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;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&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;firstName&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;lastName&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DESC&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="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;commentsPerPost&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="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DESC&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;14 lines of code.&lt;/p&gt;

&lt;p&gt;The same with &lt;code&gt;Prisma&lt;/code&gt; took me 48 lines of code (&lt;a href="https://github.com/romeerez/orchid-orm-examples/blob/main/packages/benchmarks/src/benchmarks/nestedSelect.ts#L104" rel="noopener noreferrer"&gt;source&lt;/a&gt;), and it looks clean and well, but requires mapping of result, as it doesn't support naming the fields as you need in the query, so we can't just load comments as "lastComments", but this may be required by our API spec.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Sequelize&lt;/code&gt; code for the same query (&lt;a href="https://github.com/romeerez/orchid-orm-examples/blob/main/packages/benchmarks/src/benchmarks/nestedSelect.ts#L170" rel="noopener noreferrer"&gt;source&lt;/a&gt;) looks more complicated and includes inevitable dangerous type casts.&lt;/p&gt;

&lt;p&gt;The limited interface of &lt;code&gt;MikroORM&lt;/code&gt; and &lt;code&gt;TypeORM&lt;/code&gt; would not be enough for this query, so if in case of using them, we would have to switch to a query builder.&lt;/p&gt;

&lt;p&gt;But, how is it even possible with a query builder? Using &lt;code&gt;MikroORM&lt;/code&gt;, &lt;code&gt;TypeORM&lt;/code&gt;, &lt;code&gt;Knex&lt;/code&gt;, &lt;code&gt;Kysely&lt;/code&gt; for such a query would much more time and effort, resulting in something really scary to maintain. We would probably end up with one separate query per table, and then combining records on the JS side. And need to do this carefully to not introduce the &lt;code&gt;N + 1&lt;/code&gt; problem. Let me know if I'm wrong, I'd love to be wrong at this and to see how it could be done with a query builder properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;Premature optimization is evil, so the performance doesn't matter as much as other characteristics, but still, in some cases, it can be critical.&lt;/p&gt;

&lt;p&gt;The initial version of &lt;code&gt;OrchidORM&lt;/code&gt; was built to test the idea: if Postgres can include nested resources on its end via sub-queries, and return them as JSON columns, why not a single ORM is using this? Would it be efficient to follow this way?&lt;/p&gt;

&lt;p&gt;SQL example to demonstrate this approach: loading posts with JSON array of comments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="o"&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;SELECT&lt;/span&gt; &lt;span class="n"&gt;json_agg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&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;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;postId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how &lt;code&gt;OrchidORM&lt;/code&gt; handles relations under the hood, so all nested select queries are turned into a single SQL query.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Prisma&lt;/code&gt; loads each new relation as a new query, then combines results on the JS side.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Sequelize&lt;/code&gt; produces huge queries with joins.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;OrchidORM&lt;/code&gt; is the fastest in comparison with other ORMs and even query builders, see benchmarks &lt;a href="https://orchid-orm.netlify.app/guide/benchmarks.html#benchmarks" rel="noopener noreferrer"&gt;here&lt;/a&gt;, and other benchmarks in my other &lt;a href="https://romeerez.hashnode.dev/nodejs-orms-overview-and-comparison#heading-benchmarks" rel="noopener noreferrer"&gt;article&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing a model
&lt;/h2&gt;

&lt;p&gt;Defining columns in &lt;code&gt;OrchidORM&lt;/code&gt; may look familiar if you ever have used &lt;code&gt;Zod&lt;/code&gt;, &lt;code&gt;Yup&lt;/code&gt;, &lt;code&gt;Joi&lt;/code&gt;, and similar. It is more compact than defining types separately (&lt;code&gt;Sequelize&lt;/code&gt;) or using TypeScript decorators and ! signs as in &lt;code&gt;TypeORM&lt;/code&gt; and &lt;code&gt;MikroORM&lt;/code&gt;, and it doesn't require generating code on each schema change as in &lt;code&gt;Prisma&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's possible to convert the table schema to &lt;code&gt;Zod&lt;/code&gt; to use for validations later.&lt;/p&gt;

&lt;p&gt;All columns are required (not nullable) by default.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;text&lt;/code&gt; type requires min and max arguments, so our table is protected from empty or billion chars long texts. All column types can be customized in &lt;code&gt;BaseTable&lt;/code&gt;, this can be used to set a common min and max for all columns together.&lt;/p&gt;

&lt;p&gt;Supported relation types are &lt;code&gt;belongsTo&lt;/code&gt;, &lt;code&gt;hasOne&lt;/code&gt;, &lt;code&gt;hasMany&lt;/code&gt;, &lt;code&gt;hasAndBelongsToMany&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;hasOne&lt;/code&gt; and &lt;code&gt;hasMany&lt;/code&gt; supports through the option for a table in the middle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArticleTable&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseTable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;article&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;columns&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="nf"&gt;setColumns&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serial&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;primaryKey&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;foreignKey&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;favoritesCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timestamps&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;

  &lt;span class="nx"&gt;relations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;author&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="nf"&gt;belongsTo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;UserTable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;primaryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;foreignKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userId&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="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// convert columns schema to Zod, use it later for validations&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ArticleSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tableToZod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ArticleTable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's the catch?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;only Postgres is supported at this point&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;not possible to have a relation between tables in different databases (to be done in the future)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;though it works on demo projects, it is too green for production&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;though it contains all methods from Knex and inserting/updating in the style of Prisma, many features are to be done yet&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;it can generate migrations from the existing database, it can generate table files from running migrations. But generating migrations from table schemas (as in Prisma) or updating the database schema on the fly (as in many ORMs) is yet to be done.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary of OrchidORM
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Type safety is a top priority&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Query-builder interface enables writing complex custom queries&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Creating and updating nested records is as powerful as in Prisma&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Zod-like defining columns makes it simpler than the others&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cares about performance&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it out!
&lt;/h2&gt;

&lt;p&gt;I hope you could appreciate &lt;code&gt;OrchidORM&lt;/code&gt; and try it on non-critical personal projects and share feedback, so the ORM could grow and prosper.&lt;/p&gt;

&lt;p&gt;To speed up the setup, I added a script to do all routine preparations automatically, simply run this in a new directory and check out &lt;a href="https://orchid-orm.netlify.app/guide/quickstart.html" rel="noopener noreferrer"&gt;quickstart&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;npx&lt;/span&gt; &lt;span class="nx"&gt;orchid&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;orm&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or you can clone &lt;a href="https://github.com/romeerez/orchid-orm-examples/tree/main/packages/blog-api" rel="noopener noreferrer"&gt;this examples repo&lt;/a&gt; and play with the Blog API code.&lt;/p&gt;

&lt;p&gt;Don't forget to star the project if you liked it, and share feedback.&lt;/p&gt;

&lt;p&gt;Does it look interesting to you? Do you agree existing tools are not good enough?&lt;/p&gt;

</description>
      <category>privacy</category>
      <category>crypto</category>
      <category>web3</category>
      <category>discuss</category>
    </item>
    <item>
      <title>React.js localStorage library showcase</title>
      <dc:creator>Roman K</dc:creator>
      <pubDate>Sun, 16 Jan 2022 20:05:15 +0000</pubDate>
      <link>https://forem.com/romeerez/reactjs-localstorage-library-showcase-5gj</link>
      <guid>https://forem.com/romeerez/reactjs-localstorage-library-showcase-5gj</guid>
      <description>&lt;p&gt;localStorage is used almost in every React project, but is it easy to manage it, do we need a lib for this and what do we have?&lt;/p&gt;

&lt;p&gt;There are popular ways of handing auth: one way we store JWT token in localStorage, in other way we store it in httpOnly cookie which is more secure. But even in second way it still makes sense to store &lt;code&gt;authorizedUserId&lt;/code&gt; id or at least &lt;code&gt;isAuthorized&lt;/code&gt; boolean in localStorage to not make redundant requests to a server side later, remove it when logging out or when server responds with '401 Not Authorized' code.&lt;/p&gt;

&lt;p&gt;If after performing authorization request we simply set a value of authorized user to the localStorage, other components won't be updated automatically, because React is not aware of state change.&lt;/p&gt;

&lt;h1&gt;
  
  
  How localStorage is usually handled in React
&lt;/h1&gt;

&lt;p&gt;One popular option is Context API, documentation even says it's good for storing current user (&lt;a href="https://reactjs.org/docs/context.html#when-to-use-context"&gt;here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Another popular way is to use a state manager.&lt;/p&gt;

&lt;p&gt;Why I'd like to avoid both of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;boilerplate:&lt;/strong&gt; they both requires writing much more code than we want to, yes, even with Redux Toolkit, need to write slice, add it to global store, if we are using TypeScript it's even more code since both Context and state manager cannot infer type from initial data&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;synchronization of two sources of truth is error prone&lt;/strong&gt;: each change of value in the store must be reflected by setting value to localStorage, if localStorage was updated in a separate browser tab the change must be saved to the store&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;efficiency&lt;/strong&gt;: Context is known to be less efficient than a state manager. With state manager you have to read, parse and validate the value at every page load when store or context is initializing. In case of global Redux it means that even if we want to handle some specific value only in some distant page of our app, it has to be read, parsed and validated to initialize our global monolithic store, no matter what page is currently open.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  The simplest solution
&lt;/h1&gt;

&lt;p&gt;Here is my library: &lt;a href="https://github.com/romeerez/react-local-storage-manager"&gt;react-local-storage-manager&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's consider example of storing &lt;code&gt;authToken&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;first, create a separate file for a 'store', it may be named auth.store.js or as you like:&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;// auth.store.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;createLocalStore&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-local-storage-manager&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createLocalStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;authToken&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// local storage key&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// export setter function:&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;saveAuthToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;

&lt;span class="c1"&gt;// export getter function:&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getAuthToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;

&lt;span class="c1"&gt;// export hook to use a value:&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useAuthToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;

&lt;span class="c1"&gt;// export other useful hooks:&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useIsAuthorized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;useAuthToken&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;// token to boolean&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now it's so easy to get, set, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// set token after successful authorization result:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;callApiToAuthorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loginData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;saveAuthorizedUserId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// to keep track of authorized state in any component:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isAuthorized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useIsAuthorized&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// get token outside of component:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUserToken&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the library values are read, parsed and validated only when accessing them and only for the first time, second time it will get value from cache. Validation is enforced to not be forgotten, multiple tabs will be synchronized.&lt;/p&gt;

&lt;p&gt;To see it in action check this &lt;a href="https://codesandbox.io/s/clever-night-zugq9?file=/src/App.tsx"&gt;codesandbox&lt;/a&gt;, one more example of shopping cart is in readme on github.&lt;/p&gt;

&lt;p&gt;Please share you thoughts!&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
