<?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: East Pole</title>
    <description>The latest articles on DEV Community by East Pole (@eastpole).</description>
    <link>https://dev.to/eastpole</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%2Forganization%2Fprofile_image%2F894%2F984d6c8c-d782-4d0e-8f50-f8524caf1b97.png</url>
      <title>DEV Community: East Pole</title>
      <link>https://dev.to/eastpole</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eastpole"/>
    <language>en</language>
    <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>
