<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: wilfred@eastpole.nl</title>
    <description>The latest articles on DEV Community by wilfred@eastpole.nl (@wspringer).</description>
    <link>https://dev.to/wspringer</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%2F188897%2F649926ed-276b-48e0-8e0b-d595c9d5f7f5.png</url>
      <title>DEV Community: wilfred@eastpole.nl</title>
      <link>https://dev.to/wspringer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wspringer"/>
    <language>en</language>
    <item>
      <title>GraphQL Tools Transformations</title>
      <dc:creator>wilfred@eastpole.nl</dc:creator>
      <pubDate>Wed, 09 Aug 2023 11:00:37 +0000</pubDate>
      <link>https://dev.to/wspringer/graphql-tools-transformations-n9k</link>
      <guid>https://dev.to/wspringer/graphql-tools-transformations-n9k</guid>
      <description>&lt;p&gt;The &lt;a href="https://the-guild.dev/graphql/tools/docs/api/modules/wrap_src#wrapschema" rel="noopener noreferrer"&gt;&lt;code&gt;wrapSchema&lt;/code&gt;&lt;/a&gt; function from &lt;a href="https://the-guild.dev/graphql/tools/docs/api/modules/wrap_src" rel="noopener noreferrer"&gt;@graphql-tools/wrap – GraphQL Tools&lt;/a&gt; allows you to wrap an existing schema, add some transformations and as a result get a new schema, implementing a different contract, which is really useful when implementing your own gateway. &lt;/p&gt;

&lt;p&gt;Unfortunately, it’s not all that well documented. Consider this to be a work in progress to compensate for that. &lt;/p&gt;

&lt;p&gt;First of all, &lt;code&gt;wrapSchema&lt;/code&gt; is taking an array of &lt;code&gt;Transform&lt;/code&gt; implementations. A &lt;code&gt;Transform&lt;/code&gt; itself is not isolated to a specific bit of the underlying schema, or the requests and results passing through it.&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Transform&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;transformSchema&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;SchemaTransform&lt;/span&gt;
  &lt;span class="nx"&gt;transformRequest&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;RequestTransform&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  
  &lt;span class="nx"&gt;transformResult&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ResultTransform&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are some helper classes easing the construction of a Transform. In most cases, those helper classes allow you to easily isolate the scope of your transformation to particular subset of the schema, and prevent you from having to do all of at the legwork. &lt;/p&gt;

&lt;p&gt;In many of those cases, the constructors of those &lt;code&gt;Transform&lt;/code&gt; implementations take &lt;em&gt;Transformers&lt;/em&gt;. Naming is not really helping here, and it quickly becomes pretty confusing. But here are some basic rules of thumb to preserve your sanity:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Transforms&lt;/strong&gt; are &lt;strong&gt;classes&lt;/strong&gt; that implement the &lt;code&gt;Transform&lt;/code&gt; contract.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Transforms&lt;/strong&gt; provided by graphql-tools &lt;strong&gt;never&lt;/strong&gt; have the word “Transform” in them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transforms&lt;/strong&gt; often take &lt;strong&gt;Transformer&lt;/strong&gt; (note the &lt;strong&gt;er&lt;/strong&gt; suffix) instances as constructor arguments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transformers&lt;/strong&gt; are functions that perform a specific bit of the transformation. &lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Transformers&lt;/strong&gt; defined by graphql-tools &lt;strong&gt;all&lt;/strong&gt; have the word “Transformer” in them, and they are merely defined as function types.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transformers&lt;/strong&gt; are the functions you need to implement in order to use a graphql-tools provided &lt;strong&gt;Transform&lt;/strong&gt;. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transformer&lt;/strong&gt; functions &lt;em&gt;with&lt;/em&gt; the word &lt;strong&gt;Node&lt;/strong&gt; in their name affect the data passing through the &lt;strong&gt;Transform&lt;/strong&gt;; they typically produce &lt;a href="https://graphql.org/graphql-js/type/" rel="noopener noreferrer"&gt;some sort of &lt;code&gt;GraphQL…Config&lt;/code&gt; object&lt;/a&gt;, representing a part of the &lt;em&gt;schema&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transformer&lt;/strong&gt; functions &lt;em&gt;without&lt;/em&gt; the word &lt;strong&gt;Node&lt;/strong&gt; in their name affect the schema; they typically produce some sort of  &lt;code&gt;Node&lt;/code&gt;  object, representing part of the &lt;em&gt;data&lt;/em&gt; floating through the system. (Data that is expected to be compliant with the aforementioned &lt;em&gt;schema.&lt;/em&gt;.)&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>graphql</category>
      <category>gateway</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Hapi-Swagger Gotchas</title>
      <dc:creator>wilfred@eastpole.nl</dc:creator>
      <pubDate>Sun, 10 Nov 2019 15:09:45 +0000</pubDate>
      <link>https://dev.to/eastpole/hapi-swagger-gotchas-506p</link>
      <guid>https://dev.to/eastpole/hapi-swagger-gotchas-506p</guid>
      <description>&lt;p&gt;I am currently working on an API and I'm using Hapi to make sure my endpoints are validated without too much trouble, and to make sure I'm getting a properly documented API in Swagger for (almost) free.&lt;/p&gt;

&lt;p&gt;Now, the good news is: there is not a lot that I need to do in order to have an API that is documented quite properly. The bad news is, instead of having to understand Swagger, you now have to understand all the Hapi incantations to get Swagger output in the way you want it. &lt;/p&gt;

&lt;p&gt;There are a couple of things I ran into that I'm sure I will have forgotten the next time I get to it, so I will post a note here. In some of these cases, it required some digging around to understand how to get it to work properly, so hopefully, by posting it here, you don't have to. &lt;/p&gt;

&lt;h2&gt;
  
  
  Producing either JSON or a text stream
&lt;/h2&gt;

&lt;p&gt;Hapi already allows you to document to validations for the response of your endpoint. Unfortunately, that only works in case your response is an object. So what do you do if your endpoint can both produce a JSON object, as well as a plain text &lt;em&gt;stream&lt;/em&gt;? In that case, you might not care too much about the validations, but having the JSON version documented might still be useful. &lt;/p&gt;

&lt;p&gt;In that case, rather than documenting your response as a Joi schema in the &lt;code&gt;validate&lt;/code&gt; section of your endpoint configuration, you might consider relying on Hapi-Swagger's mechanism for documenting responses. &lt;/p&gt;

&lt;p&gt;So rather than doing this:&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="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;keys&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;You would do this:&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="nx"&gt;options&lt;/span&gt;&lt;span class="p"&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;hapi-swagger&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="nl"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;:&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="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;keys&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Labels
&lt;/h2&gt;

&lt;p&gt;If your endpoints are returning JSON and &lt;em&gt;if&lt;/em&gt; you define the structure of your JSON with Joi validations, either using the &lt;code&gt;validate&lt;/code&gt; key or the &lt;code&gt;hapi-swagger.responses.200.schema&lt;/code&gt; key of your definition, then you might want to consider adding labels to all of your &lt;code&gt;Joi.object()&lt;/code&gt;s. Without it, &lt;code&gt;hapi-swagger&lt;/code&gt; will automatically label the 'models', which will render your documentation pretty much unreadable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Markdown
&lt;/h2&gt;

&lt;p&gt;This might be a surprise, but Swagger descriptions are supporting Markdown. I suggest you use it. It helps to add a little bit more structure to your documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security
&lt;/h2&gt;

&lt;p&gt;If you don't provide unauthorized access to your endpoints, then it helps including information on how to authenticate in your swagger.json file. I found the Hapi-Swagger documentation a little dense on how to do it. It turns out you will have to do two things:&lt;/p&gt;

&lt;p&gt;In the general plugin options of your Hapi-Swagger configuration, you will have to add a &lt;code&gt;securityDefinitions&lt;/code&gt; option:&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="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;securityDefinitions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API Key&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;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apiKey&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// apiKey is defined by the Swagger spec&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apiKey&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// the name of the query parameter / header&lt;/span&gt;
      &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;        &lt;span class="c1"&gt;// how the key is passed&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 next thing you need to do is add an option to the route configuration of every route that requires authentication:&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="nx"&gt;options&lt;/span&gt;&lt;span class="p"&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;hapi-swagger&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="nl"&gt;security&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API Key&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the key in your security definitions and the key in your route options need to correspond. &lt;/p&gt;

&lt;p&gt;If you add it this way, then the Swagger UI will include an «Authorize» button at the top of your API description, allowing you to set the API key for all further invocations. You can also click the little lock in all the endpoints. That will bring up the same dialog. &lt;/p&gt;

&lt;p&gt;Now, bear in mind that adding this does &lt;em&gt;not&lt;/em&gt; guarantee that your API is also protected against unauthorized access. It just guarantees that Swagger understands the need for an API key. You will still have to add an authentication scheme to your Hapi server, and include it in the &lt;code&gt;auth&lt;/code&gt; key of your route configuration. &lt;/p&gt;

&lt;p&gt;If you do that, and if you use a query param based API key, then you might find that your query param validations no longer work. After all, you are now all of a sudden passing in an additional &lt;code&gt;apiKey&lt;/code&gt; parameter, that your endpoint itself is completely unaware of. &lt;/p&gt;

&lt;p&gt;There are two ways to solve it: either you change the Joi object associated with the &lt;code&gt;query&lt;/code&gt; key of your route definition to include unknowns, or you &lt;em&gt;remove&lt;/em&gt; the apiKey in the lifecycle hook of your authentication scheme. (I did the latter.)&lt;/p&gt;

</description>
      <category>hapi</category>
      <category>swagger</category>
    </item>
    <item>
      <title>Redis Async Generators</title>
      <dc:creator>wilfred@eastpole.nl</dc:creator>
      <pubDate>Mon, 01 Jul 2019 15:51:21 +0000</pubDate>
      <link>https://dev.to/eastpole/redis-async-generators-3ed</link>
      <guid>https://dev.to/eastpole/redis-async-generators-3ed</guid>
      <description>&lt;p&gt;If you have never gone through &lt;a href="https://www.infoq.com/articles/API-Design-Joshua-Bloch/" rel="noopener noreferrer"&gt;Joshua Bloch's API Design in Bumper Stickers presentation&lt;/a&gt;, then you certainly should. Here are four of the guidelines he's suggesting:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;APIs should be easy to use and hard to misuse.  &lt;/p&gt;

&lt;p&gt;When in Rome, do as the Romans do.  &lt;/p&gt;

&lt;p&gt;APIs should be self-documenting  &lt;/p&gt;

&lt;p&gt;Don't make the client do anything the library could do.  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It sounds so obvious. However, I don't have to think too deep to come up with examples of APIs that clearly miss the mark. &lt;/p&gt;

&lt;p&gt;To be fair, "easy to use" is a little vague. "Easy to use" might mean that it requires very little code to use the API. But it could also mean that the code you do have to write is idiomatic for the language in which you're coding. Or perhaps it should be easy to read, without even knowing the library. &lt;/p&gt;

&lt;p&gt;Scala's Dispatch library is an example of a library that optimized for compactness of the calling code instead of readability. And with Scala's flexible syntax, nobody even knew for sure what "idiomatic" really meant in Scala's world. &lt;/p&gt;

&lt;p&gt;JavaScript is different. It's syntax is less flexible than Scala's. It doesn't allow you to "extend" the syntax and make it behave as something else. You have to live with whatever it has baked in. As a consequence, I would expect JavaScript libraries to converge more quickly towards an idiomatic and common way of doing things. &lt;/p&gt;

&lt;p&gt;So, moving on to Redis. Redis is — in a way — a foreign entity in JavaScript. It's API wasn't designed with JavaScript in mind in particular. It seems that some of that spilled over in its client binding. A lot of things would be much simpler if the standard redis library for node would be based on promises rather than callbacks. I'd say promises and async await constructs are the more idiomatic way to do things these days. &lt;/p&gt;

&lt;p&gt;The same goes for iterators. If you have code that iterates over a collection, then Redis' JavaScript API makes that quite painful. That is, it requires quite some careful control by the caller. It violates not just one but &lt;em&gt;all&lt;/em&gt; of four of the above  guidelines from Josh Bloch's talk. &lt;/p&gt;

&lt;p&gt;This is a slightly modified version of some code I found on StackOverflow to iterate over all keys matching the pattern &lt;code&gt;person*&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;redisClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;…&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;scan&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;redisClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MATCH&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;COUNT&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;1000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&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;err&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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;cursor&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&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;let&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;callback&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="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;person*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I found this code hard to digest. You need to understand about ‘0’ being the magic marker to mark the end of the matching values. That not only feels not all that idiomatic, but it also forces me to control a whole bunch of things I’d rather leave to the API instead.&lt;/p&gt;

&lt;p&gt;So therefore, I present to you &lt;a href="https://github.com/wspringer/redis-async-gen" rel="noopener noreferrer"&gt;redis-async-gen&lt;/a&gt; — async generators for Redis. That’s quite a mouthful, but the important thing is that it compacts this code considerably, that it’s easier on the eyes, and that it relieves the caller from having to carefully manage a cursor:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;redisClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;…&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;generators&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;redis-async-gen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&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;keysMatching&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;generators&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redisClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &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;key&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nf"&gt;keysMatching&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;person*&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>redis</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
