<?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: Sagacité</title>
    <description>The latest articles on DEV Community by Sagacité (@fikkyman1).</description>
    <link>https://dev.to/fikkyman1</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%2F436480%2Fb35b4709-2ee9-4a1d-9569-c3c683cf6737.png</url>
      <title>DEV Community: Sagacité</title>
      <link>https://dev.to/fikkyman1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fikkyman1"/>
    <language>en</language>
    <item>
      <title>Exploring Caching with NestJS and Redis</title>
      <dc:creator>Sagacité</dc:creator>
      <pubDate>Sun, 18 Jun 2023 00:41:16 +0000</pubDate>
      <link>https://dev.to/fikkyman1/exploring-caching-with-nestjs-and-redis-56aj</link>
      <guid>https://dev.to/fikkyman1/exploring-caching-with-nestjs-and-redis-56aj</guid>
      <description>&lt;p&gt;In this tutorial, we will be exploring the world of caching, with NestJS and Redis. &lt;/p&gt;

&lt;p&gt;Before we dive head-in into Redis, NestJS provides its &lt;strong&gt;in-memory cache&lt;/strong&gt;, that can serve as an in-memory data store, but can easily be switched to high-performant caching system like Redis.&lt;/p&gt;

&lt;p&gt;To get started, you can fork this &lt;a href="https://github.com/D-Lite/nest-cache-tutorial" rel="noopener noreferrer"&gt;github repository&lt;/a&gt;. Please, ensure you select the base branch as it contains the base application setup for this tutorial. &lt;/p&gt;

&lt;p&gt;After setting up you base application setup, you can proceed to install the &lt;code&gt;cache-manager&lt;/code&gt; package and its dependencies.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npm &lt;span class="nb"&gt;install&lt;/span&gt; @nestjs/cache-manager cache-manager


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In case you are familiar with NestJS in-memory cache implementation, there is a current update to the cache-manager, we are using version 5 throughout this tutorial. As opposed to the version 4, the version 5 now provides &lt;strong&gt;ttl (Time-To-Live) in milliseconds&lt;/strong&gt; instead. We have to do the conversion before parsing it to NestJS as NestJs does not do the conversion for us.&lt;/p&gt;

&lt;p&gt;To enable caching, we have to import the &lt;code&gt;CacheModule&lt;/code&gt; into the &lt;code&gt;app.module.ts&lt;/code&gt; file.&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt; &lt;span class="p"&gt;}&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;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppController&lt;/span&gt; &lt;span class="p"&gt;}&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;./app.controller&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppService&lt;/span&gt; &lt;span class="p"&gt;}&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;./app.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MongooseModule&lt;/span&gt; &lt;span class="p"&gt;}&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;@nestjs/mongoose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ContactModule&lt;/span&gt; &lt;span class="p"&gt;}&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;./contact/contact.module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CacheModule&lt;/span&gt; &lt;span class="p"&gt;}&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;@nestjs/cache-manager&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="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;  
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
  &lt;span class="c1"&gt;// mongodb://127.0.0.1:27017/nest-redis - use env variables or other secure options in production&lt;/span&gt;
    &lt;span class="nx"&gt;MongooseModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongodb://127.0.0.1:27017/nest-redis&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;useNewUrlParser&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="nx"&gt;ContactModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="nx"&gt;CacheModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;  
  &lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="na"&gt;controllers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppController&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  
&lt;span class="p"&gt;})&lt;/span&gt;  
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Our &lt;code&gt;app.module.ts&lt;/code&gt; file should look like the one above.&lt;/p&gt;

&lt;p&gt;We are also going to import it into our &lt;code&gt;Contact&lt;/code&gt; module. With any feature module in NestJS, there is a need to import the &lt;code&gt;cache-manager&lt;/code&gt; into them respectively. If you have a module that does not require caching, then it is not a necessity. &lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;contact.module.ts&lt;/code&gt; should be similar to the code block below:&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt; &lt;span class="p"&gt;}&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;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ContactService&lt;/span&gt; &lt;span class="p"&gt;}&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;./contact.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ContactController&lt;/span&gt; &lt;span class="p"&gt;}&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;./contact.controller&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MongooseModule&lt;/span&gt; &lt;span class="p"&gt;}&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;@nestjs/mongoose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ContactSchema&lt;/span&gt; &lt;span class="p"&gt;}&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;./schemas/schema&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CacheModule&lt;/span&gt; &lt;span class="p"&gt;}&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;@nestjs/cache-manager&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="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;  
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
    &lt;span class="nx"&gt;MongooseModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forFeature&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Contact&lt;/span&gt;&lt;span class="dl"&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;ContactSchema&lt;/span&gt; &lt;span class="p"&gt;}]),&lt;/span&gt;  
    &lt;span class="nx"&gt;CacheModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;  
  &lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ContactService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  
  &lt;span class="na"&gt;controllers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ContactController&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  
&lt;span class="p"&gt;})&lt;/span&gt;  
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ContactModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A good alternative, is setting the &lt;code&gt;isGlobal&lt;/code&gt; option for the &lt;strong&gt;CacheModule.register()&lt;/strong&gt;. This option can be considered if you need caching availability across all your modules.&lt;/p&gt;

&lt;p&gt;Let's proceed to our &lt;code&gt;Contact controller&lt;/code&gt; to modify some of our existing endpoints. For the &lt;code&gt;@Get('contacts/:contactId')&lt;/code&gt; endpoint, we want to check if our &lt;code&gt;response&lt;/code&gt; is in cache, before calling the method. This is achievable using the &lt;code&gt;@useInterceptors&lt;/code&gt; decorator:&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;getContact&lt;/code&gt; method should be similar to this below:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;UseInterceptors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CacheInterceptor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Automatically cache the response for this endpoint  &lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contacts/:contactId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getContact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Res&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contactId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;contactId&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contactService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contactId&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;contact&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NotFoundException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Contact does not exist&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HttpStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contact&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;Things to note: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The expiry time is 5 seconds&lt;/li&gt;
&lt;li&gt;The interceptor autogenerates the cache key for the cache entry based on the route path.
Both options can be effectively controlled and overriden.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Implementing an override, our &lt;code&gt;getContact&lt;/code&gt; will be:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;UseInterceptors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CacheInterceptor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Automatically cache the response for this endpoint  &lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;CacheKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getcontact-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="nd"&gt;CacheTTL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// now in milliseconds (1 minute === 60000)&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contacts/:contactId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getContact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Res&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contactId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;contactId&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contactService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contactId&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;contact&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NotFoundException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Contact does not exist&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HttpStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contact&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;ul&gt;
&lt;li&gt;According to the official Nestjs documentation, the CacheModule will not work properly with GraphQL applications*&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Now Redis&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;According to &lt;a href="https://redis.io/docs/about/#:~:text=Redis%20is%20an%20open%20source,%2C%20geospatial%20indexes%2C%20and%20streams." rel="noopener noreferrer"&gt;the official Redis website&lt;/a&gt;, “Redis is an open source, in-memory data structure store used as a database, cache, message broker, and streaming engine.”&lt;/p&gt;

&lt;p&gt;Polled from &lt;a href="https://redis.io/" rel="noopener noreferrer"&gt;Redis&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To use Redis instead of the in-memory cache, we will need to install the relevant package:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npm i &lt;span class="nt"&gt;--save&lt;/span&gt; cache-manager-redis-yet


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This package is a &lt;code&gt;nestjs&lt;/code&gt; wrapper for passing configurations to the &lt;code&gt;node_redis&lt;/code&gt; package. We can now change some config of our &lt;code&gt;app.module.ts&lt;/code&gt; to use Redis.&lt;/p&gt;

&lt;p&gt;Note: &lt;code&gt;cache-manager-redis-store&lt;/code&gt; is been discontinued to allow for the package we just installed. This &lt;code&gt;package&lt;/code&gt; we installed is been &lt;a href="https://github.com/node-cache-manager/node-cache-manager-redis-yet" rel="noopener noreferrer"&gt;tracked directly by the &lt;code&gt;Nestjs&lt;/code&gt; team&lt;/a&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt; &lt;span class="p"&gt;}&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;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppController&lt;/span&gt; &lt;span class="p"&gt;}&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;./app.controller&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppService&lt;/span&gt; &lt;span class="p"&gt;}&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;./app.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MongooseModule&lt;/span&gt; &lt;span class="p"&gt;}&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;@nestjs/mongoose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ContactModule&lt;/span&gt; &lt;span class="p"&gt;}&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;./contact/contact.module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CacheModule&lt;/span&gt; &lt;span class="p"&gt;}&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;@nestjs/cache-manager&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;redisStore&lt;/span&gt; &lt;span class="p"&gt;}&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;cache-manager-redis-yet&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="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;  
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
    &lt;span class="nx"&gt;MongooseModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongodb://127.0.0.1:27017/nest-redis&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;useNewUrlParser&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="nx"&gt;CacheModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;  
      &lt;span class="na"&gt;isGlobal&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="na"&gt;useFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;  
        &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;redisStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;  
          &lt;span class="na"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
            &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6379&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;span class="nx"&gt;ContactModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
  &lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="na"&gt;controllers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppController&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  
&lt;span class="p"&gt;})&lt;/span&gt;  
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This has allowed us to add new &lt;code&gt;config options&lt;/code&gt; like:&lt;br&gt;
    redisStore: represent the &lt;code&gt;node-cache-manager-redis-yet&lt;/code&gt; we just installed&lt;br&gt;
    host and port are set to their default&lt;/p&gt;

&lt;p&gt;Incase you don't have Redis server running yet, you can look up various installation methods for your operating system or consider the &lt;a href="https://redis.io/docs/stack/get-started/install/docker/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; option&lt;/p&gt;

&lt;p&gt;Heading to our &lt;code&gt;contact.controller&lt;/code&gt; page, we can configure our &lt;code&gt;getContact&lt;/code&gt; method, to check if we have a cached data yet before querying the main database. If it exists, we want to return it else we want to set it after querying the DB.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;getContact&lt;/code&gt; should be similar to this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contacts/:contactId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getContact&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Res&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contactId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;contactId&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;cachedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cacheService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contactId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&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;cachedData&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Getting data from cache&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HttpStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cachedData&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;contact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contactService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contactId&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;contact&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NotFoundException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Contact does not exist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cacheService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contactId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;contact&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;newCachedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cacheService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contactId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data set to cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newCachedData&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="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HttpStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contact&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's take a brief look at the code block above:&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;cachedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cacheService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contactId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&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;cachedData&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Getting data from cache&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HttpStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cachedData&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 &lt;code&gt;cachedData&lt;/code&gt; variable is checking if we have an existing cache, if it exists, you can check your logger and you'll get &lt;em&gt;Getting data from cache&lt;/em&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cacheService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contactId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;contact&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;newCachedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cacheService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contactId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data set to cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newCachedData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If our data does not exist in cache, the codeblock above helps us to set in cache.&lt;/p&gt;

&lt;p&gt;Your cached data will now persist to your local Redis server.&lt;/p&gt;

&lt;p&gt;You can test out the endpoint and you should get a result similar to my output  in your logger:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="o"&gt;[&lt;/span&gt;Nest] 3720  - 05/09/2023, 10:00:49 AM     LOG &lt;span class="o"&gt;[&lt;/span&gt;NestApplication] Nest application successfully started +5ms
data &lt;span class="nb"&gt;set &lt;/span&gt;to cache &lt;span class="o"&gt;{&lt;/span&gt;
  _id: &lt;span class="s1"&gt;'6459510cfc398baa01998a66'&lt;/span&gt;,
  first_name: &lt;span class="s1"&gt;'Daniel'&lt;/span&gt;,
  last_name: &lt;span class="s1"&gt;'Olabemiwo'&lt;/span&gt;,
  email: &lt;span class="s1"&gt;'dee@gmail.com'&lt;/span&gt;,
  phone: &lt;span class="s1"&gt;'+23832101'&lt;/span&gt;,
  message: &lt;span class="s1"&gt;'Welcome to this side'&lt;/span&gt;,
  __v: 0
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can confirm in your GUI of choice (I like &lt;a href="https://tableplus.com/" rel="noopener noreferrer"&gt;TablePlus&lt;/a&gt;) by sending a request to your NestJS app where caching is used and you'll see the data is now persisted:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fspagl1eehtyqtom6t0q4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fspagl1eehtyqtom6t0q4.png" alt="Redis on TablePlus"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations, you have just added a Redis cache to your NestJS application.&lt;/p&gt;

&lt;p&gt;In this tutorial, we have successfully added a Redis cache to our Nestjs application. &lt;br&gt;
Redis enables lower application latency and very high data access. This allows software engineers to build highly performant, reliable solutions.&lt;/p&gt;

&lt;p&gt;And that's it! What do you think? Let me know in the comments below.&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>redis</category>
      <category>caching</category>
      <category>typescript</category>
    </item>
    <item>
      <title>DOCUMENTING YOUR API WITH SWAGGER</title>
      <dc:creator>Sagacité</dc:creator>
      <pubDate>Tue, 02 Aug 2022 18:17:39 +0000</pubDate>
      <link>https://dev.to/fikkyman1/documenting-your-api-with-swagger-15dn</link>
      <guid>https://dev.to/fikkyman1/documenting-your-api-with-swagger-15dn</guid>
      <description>&lt;p&gt;Ever gone through the engineering your API's knowing fully well, you will need to come up with a documentation so other devs can work with it?&lt;/p&gt;

&lt;p&gt;You have a short answer here. I will try to make this docs as simple and comprehensive as it might be. Might not be as comprehensive as you want, but you willl be able to scale it up to meet your demands.&lt;/p&gt;

&lt;p&gt;Stacks and technologies needed: Node.js, Express, MongoDB, Swagger.&lt;/p&gt;

&lt;p&gt;Initialize a project using &lt;code&gt;npm init&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Follow the step by step guideline/prompt...&lt;/p&gt;

&lt;p&gt;You can go ahead to install express and Mongo and some other dependecies necessary&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm i express mongoose nodemon dotenv cors&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Express will be used for the middleware to create various CRUD endpoints.&lt;/li&gt;
&lt;li&gt;Mongoose for managing data in MongoDB.&lt;/li&gt;
&lt;li&gt;Nodemon to restart our server every time we save our file.&lt;/li&gt;
&lt;li&gt;Dotenv to manage a .env file.&lt;/li&gt;
&lt;li&gt;cors to manage and provide a middleware that can be used to enable CORS with various options&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once the installation is complete, go ahead to create an index.js in the root directory of your application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const express = require('express');
const mongoose = require('mongoose');

const app = express();

app.use(express.json());

app.listen(3000, () =&amp;gt; {
    console.log(`Server Started at ${3000}`)
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Things to note from the code lines above:&lt;br&gt;
We have imported express and mongoose as dependencies and assigned express to app. &lt;br&gt;
Now we can listen on the port we assigned once we start the server.&lt;/p&gt;

&lt;p&gt;In &lt;em&gt;package.json&lt;/em&gt; file, you can add the line below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nodemon index.js"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember &lt;strong&gt;Nodemon&lt;/strong&gt; will help us auto-restart the server on code changes.&lt;/p&gt;

&lt;p&gt;You can just do a &lt;code&gt;npm start&lt;/code&gt; to start your node server. &lt;/p&gt;

&lt;p&gt;We won't be going deep into getting our MongoDB ready and set up in this tutorial course. You can check this out from  &lt;a href="https://www.section.io/engineering-education/nodejs-mongoosejs-mongodb/"&gt;this section.io post&lt;/a&gt;&lt;br&gt;
** I will be using MongoDB Compass which allows for local development and testing.&lt;/p&gt;

&lt;p&gt;After setting up our DB, we can create a &lt;code&gt;.env&lt;/code&gt; file and add our DB details.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MONGO_DB=mongodb://localhost:27017/
PORT=3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The value for MONGO_DB in your .env might be different depending on the MongoDB URL you get (compass/atlas).&lt;/p&gt;

&lt;p&gt;Now we can modify &lt;code&gt;index.js&lt;/code&gt; file to effect some needed changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require("dotenv").config();
const cors = require("cors");
const express = require("express");

const { connect } = require("mongoose");
const app = express();

app.use(cors());
app.use(express.urlencoded({ extended: false }));
app.use(express.json());

const runApp = async () =&amp;gt; {
    try {
        await connect(process.env.MONGO_DB, {
            useUnifiedTopology: true,
            useNewUrlParser: true,
        });
        console.log(`Successfully connected to database ${process.env.MONGO_DB}`);
        app.listen(process.env.PORT, () =&amp;gt; {
            console.log(`Server started successfully on PORT ${process.env.PORT}`);
        })
    } catch(err) {
        console.log(err);
        runApp();
    }
};

runApp();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;dotenv&lt;/code&gt;  allows for .env variables to be accessible on this page.&lt;br&gt;
In the try block, we put our .env variables to use there. &lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;npm start&lt;/code&gt; should give us this result below&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e1931QZx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y9lg46owik46e6rw3kpz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e1931QZx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y9lg46owik46e6rw3kpz.png" alt="Image description" width="880" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you get that result, congratulations. Let us go ahead and install our swagger dependecies.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm i swagger-autogen swagger-ui-express&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Routes for our endpoints
&lt;/h2&gt;

&lt;p&gt;Create a folder called &lt;strong&gt;routes&lt;/strong&gt; and inside, make a file called &lt;strong&gt;index.js&lt;/strong&gt;&lt;br&gt;
Before proceeding, let's import this file into our main script file.&lt;/p&gt;

&lt;p&gt;Our main script file should look like this now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require("dotenv").config();
const cors = require("cors");
const express = require("express");

const { connect } = require("mongoose"); 
const app = express();
const router = require("./routes/index");

app.use(cors());
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.use(router);

const runApp = async () =&amp;gt; {
    try {
        await connect(process.env.MONGO_DB, {
            useUnifiedTopology: true,
            useNewUrlParser: true,
        });
        console.log(`Successfully connected to database ${process.env.MONGO_DB}`);
        app.listen(process.env.PORT, () =&amp;gt; {
            console.log(`Server started successfully on PORT ${process.env.PORT}`);
        })
    } catch(err) {
        console.log(err);
        runApp();
    }
};

runApp();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will be getting an error now because our &lt;code&gt;routes/index.js&lt;/code&gt; file is empty.&lt;/p&gt;

&lt;p&gt;** We are going to create a simple student database, no authentication or authorization needed. &lt;/p&gt;

&lt;p&gt;Let's create a &lt;code&gt;students.js&lt;/code&gt; file in our routes folder.&lt;br&gt;
Add the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    const router = require("express").Router();

    router.get("/students", async (req, res) =&amp;gt; {
        res.send('Get All students')
    })

    //Post 
    router.post('/students', async (req, res) =&amp;gt; {
        res.send('Add a new student')
    })

    //Get by ID 
    router.get('/students/get/:id', async (req, res) =&amp;gt; {
        res.send('Get by one student by ID')
    })

    //Update by ID 
    router.patch('/students/update/:id', async (req, res) =&amp;gt; {
        res.send('Update student by ID')
    })

    //Delete by ID 
    router.delete('/students/delete/:id', async (req, res) =&amp;gt; {
        res.send('Delete by ID')
    })


    module.exports = router
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can go back to our &lt;code&gt;routes/index.js&lt;/code&gt; file to import our students file. This method allows us to define different routes for different collections in our DB.&lt;/p&gt;

&lt;p&gt;We have five methods that use the REST Methods for our CRUD operation. &lt;/p&gt;

&lt;p&gt;This router is taking the route as the first parameter. Then in the second parameter it's taking a callback.&lt;/p&gt;

&lt;p&gt;In the callback, we have a res and a req. res means response, and req means request. We use res for sending responses to our client, like Postman, or any front-end client. And we use req for receiving requests from a client app like Postman, or any front-end client.&lt;/p&gt;

&lt;p&gt;Then in the callback body, we are printing a message that says the respective API message.&lt;/p&gt;

&lt;p&gt;Let's populate our &lt;code&gt;routes/index.js&lt;/code&gt; with code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const router = require("express").Router();

const studentRoutes = require('./students.js');

router.use("/api", studentRoutes); 

module.exports = router;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Checking our server instance now, there won't be any process if you followed through to this point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Models for our DB structure
&lt;/h2&gt;

&lt;p&gt;Create a folder called model and inside, a file called student.js.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const mongoose = require('mongoose');

const studentSchema = new mongoose.Schema({
    firstname: {
        required: true,
        type: String
    },
    lastname: {
        required: true,
        type: String
    },
    middlename: {
        required: false,
        type: String
    },
    class: {
        required: true,
        type: String
    },
    age: {
        required: true,
        type: Number
    }
})

module.exports = mongoose.model('Student', studentSchema)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we have a schema that defines our database structure. It has some properties. The fields have types with some required and some optional.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Controllers for our APIs
&lt;/h2&gt;

&lt;p&gt;Create a folder called controllers. This helps abstract our functions and make out routes files less &lt;em&gt;bulky and buggy&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We can then create a &lt;code&gt;students.js&lt;/code&gt; file in the controllers folder&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Student = require("../model/students.js");

const addOne = async(req, res) =&amp;gt; {
    try {
        const newRecord = new Student({
            ...req.body,
        });
        await newRecord.save();
        return res.status(201).json({
            message: "Student successfully created",
            success: true
        });
    } catch(err) {
        return res.status(500).json({
            message: err.message,
            success: false,
        });
    }
};

const removeOne = async(req, res) =&amp;gt; {
    try {
        const deleted = await Student.findByIdAndDelete(req.params.id);
        if(!deleted) {
            return res.status(404).json({
                message: "Student not found",
                success: false,
            });
        }
        return res.status(204).json({
            message: "Student successfully deleted",
            success: true,
        });
    } catch(err) {
        return res.status(500).json({
            message: err.message,
            success: false,
        });
    }
};

const updateOne = async(req, res) =&amp;gt; {
    try {
        await Student.findByIdAndUpdate(req.params.id, req.body);
        return res.status(201).json({
            message: "Student's data successfully updated",
            success: true,
        });
    } catch(err) {
        return res.status(500).json({
            message: err.message,
            success: false,
        });
    }
};

const getAll = async(req, res) =&amp;gt; {
    try {
        const [results, studentCount] = await
        Promise.all([
            Student.find({}) 
                .sort({createdAt: -1})
                .limit(req.query.limit)
                .skip(req.skip)
                .lean()
                .exec(),
                Student.count({}),
        ]);
        const pageCount = Math.ceil(studentCount / req.query.limit);
        return res.status(201).json({
            data: results,
            pageCount,
            studentCount,
        });
    } catch(err) {
        return res.status(500).json({
            message: err.message,
            success: false,
        });
    }
};

const getOne = async(req, res) =&amp;gt; {
    try {
        const student = await Student.findById(req.params.id);
        if(student) {
            return res.status(200).json(student);
        }
        return res.status(404).json({
            message: "Student not found",
            success: false,
        });
    } catch(err) {
        return res.status(500).json({
            message: err.message,
            success: false,
        });
    }
};

module.exports = {
    addOne,
    removeOne,
    updateOne,
    getAll,
    getOne
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can go back to &lt;code&gt;routes/students.js&lt;/code&gt; to make use of these functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    const router = require("express").Router();
    const { addOne, removeOne, updateOne, getAll, getOne } = require("../controller/students.js");


    router.get("/students", async (req, res) =&amp;gt; {
        await getAll(req, res);
    })

    //Post 
    router.post('/students', async (req, res) =&amp;gt; {
        await addOne(req, res);
    })

    //Get by ID 
    router.get('/students/get/:id', async (req, res) =&amp;gt; {
        await getOne(req, res);
    })

    //Update by ID 
    router.patch('/students/update/:id', async (req, res) =&amp;gt; {
        await updateOne(req, res);
    })

    //Delete by ID 
    router.delete('/students/delete/:id', async (req, res) =&amp;gt; {
        await removeOne(req, res);
    })


    module.exports = router
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Swagger APIs
&lt;/h2&gt;

&lt;p&gt;We are going to generate our API documentation from code based on comments  in the function headers&lt;br&gt;
Back to &lt;code&gt;routes/students.js&lt;/code&gt; we are going to add &lt;strong&gt;swagger.tags&lt;/strong&gt; to each of the route. This creates a subsection in our docs page with the tag. (It groups tags with the same name together).&lt;br&gt;
Remember, we are enclosing it in a comment block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/*  
    #swagger.tags = ['Students']
*/  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thats what we are adding to each of the routes. Now our &lt;code&gt;students.js&lt;/code&gt; routes should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    const router = require("express").Router();
    const { addOne, removeOne, updateOne, getAll, getOne } = require("../controller/students.js");


    router.get("/students", async (req, res) =&amp;gt; {
        /*  
            #swagger.tags = ['Students']
        */ 
        await getAll(req, res);
    })

    //Post 
    router.post('/students', async (req, res) =&amp;gt; {
        /*  
            #swagger.tags = ['Students']
        */ 
        await addOne(req, res);
    })

    //Get by ID 
    router.get('/students/get/:id', async (req, res) =&amp;gt; {
        /*  
            #swagger.tags = ['Students']
        */  
        await getOne(req, res);
    })

    //Update by ID 
    router.patch('/students/update/:id', async (req, res) =&amp;gt; {
        /*  
            #swagger.tags = ['Students']
        */ 
        await updateOne(req, res);
    })

    //Delete by ID 
    router.delete('/students/delete/:id', async (req, res) =&amp;gt; {
        /*  
            #swagger.tags = ['Students']
        */ 
        await removeOne(req, res);
    })


    module.exports = router
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be able to see our changes to the route file, we should add a new command to our &lt;code&gt;package.json&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {
    "test": "echo \"Error: no test specified\" &amp;amp;&amp;amp; exit 1",
    "start": "nodemon index.js",
    "start-gendoc": "node swagger.js" 
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our scripts in &lt;code&gt;package.json&lt;/code&gt; should be like the one above.&lt;/p&gt;

&lt;p&gt;The last line will help us generate a json file for swagger that we can view on our browser environment.&lt;/p&gt;

&lt;p&gt;Let's go ahead and create a &lt;code&gt;swagger.js&lt;/code&gt; file in our root directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const swaggerAutogen = require("swagger-autogen")()
require("dotenv").config();

const doc = {
    info: {
        version: "1.0.0",
        title: "Quick start to auto-docs with swagger",
        description: "Auto-documenting our APIs with Swagger documentation. Tutorial by Daniel Olabemiwo"
    },
    host: "localhost:3000", // 3000 is our port
    basePath: "/",
    schemes: ["http", "https"],
    consumes: ["application/json"],
    produces: ["application/json"],
    tags: [
    ],
    definitions: {
        StudentModel: {
            $firstname: "Ade",
            $lastname: "Samuel",
            $middlename: "Daniel",
            $class: "BASIC 3",
            $age: 10
        },
    }
};


const outputFile = "./swagger_output.json";
const endpointFiles = ["./routes/index.js"];

swaggerAutogen(outputFile, endpointFiles, doc).then(() =&amp;gt; {
    require("./index");
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how our &lt;code&gt;swagger.js&lt;/code&gt; file will look like.&lt;/p&gt;

&lt;p&gt;We should also add some &lt;strong&gt;swagger lines&lt;/strong&gt; to our  root &lt;strong&gt;index.js&lt;/strong&gt; file.&lt;br&gt;
It should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require("dotenv").config();
const cors = require("cors");
const express = require("express");

const { connect } = require("mongoose");
const swaggerUi = require("swagger-ui-express");
const swaggerFile = require("./swagger_output.json");

const app = express();
const router = require("./routes/index");

app.use(cors());
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.use(router);
app.use("/doc", swaggerUi.serve, swaggerUi.setup(swaggerFile));

const runApp = async () =&amp;gt; {
    try {
        await connect(process.env.MONGO_DB, {
            useUnifiedTopology: true,
            useNewUrlParser: true,
        });
        console.log(`Successfully connected to database ${process.env.MONGO_DB}`);
        app.listen(process.env.PORT, () =&amp;gt; {
            console.log(`Server started successfully on PORT ${process.env.PORT}`);
        })
    } catch(err) {
        console.log(err);
        runApp();
    }
};

runApp();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look out for all the new addition to our index.js file.&lt;/p&gt;

&lt;p&gt;We should stop our server and run this new command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run start-gendoc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's visit our browser and check out &lt;a href="http://localhost:3000/doc/"&gt;http://localhost:3000/doc/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Pym7B0ZQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8cl3r9ud628bq0h6bnxr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Pym7B0ZQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8cl3r9ud628bq0h6bnxr.png" alt="Image description" width="880" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations if your browser is looking like the one above!&lt;br&gt;
Now let's wrap up and add necessary parameters to our post request as it requires parameters from users.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    //Post 
    router.post('/students', async (req, res) =&amp;gt; {
         /*  
         #swagger.tags = ['Students'],
        #swagger.security = [{
        "Authorization": []
        }]
        #swagger.parameters['obj'] = {
            in: 'body',
            required: true,
            schema: { $ref: "#/definitions/StudentModel" }
    } */ 
        await addOne(req, res);
    })


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have to rerun &lt;code&gt;npm run start-gendoc&lt;/code&gt; to reflect all the newest additions.&lt;br&gt;
If you followed this tutorial to this point, you can test the &lt;code&gt;POST&lt;/code&gt; endpoint on your browser and should get a result like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LbCQESZO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z6qjicrm0s9il555v70c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LbCQESZO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z6qjicrm0s9il555v70c.png" alt="Image description" width="880" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the end of this tutorial session. &lt;/p&gt;

&lt;p&gt;Feel free to drop your comments, or connect with me via &lt;a href="https://www.twitter.com/fikkyman1"&gt;twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/d-lite"&gt;linkedIn&lt;/a&gt;&lt;/p&gt;

</description>
      <category>swagger</category>
      <category>node</category>
      <category>api</category>
      <category>documentation</category>
    </item>
    <item>
      <title>Test Driven Development: How to test using Vue.js, VueTestUtils and Jest
</title>
      <dc:creator>Sagacité</dc:creator>
      <pubDate>Tue, 12 Oct 2021 01:06:58 +0000</pubDate>
      <link>https://dev.to/fikkyman1/test-driven-development-how-to-test-using-vuejs-vuetestutils-and-jest-3cpd</link>
      <guid>https://dev.to/fikkyman1/test-driven-development-how-to-test-using-vuejs-vuetestutils-and-jest-3cpd</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Test Driven Development is a software practice. TDD focuses on three (3) important things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Testing&lt;/li&gt;
&lt;li&gt;Coding&lt;/li&gt;
&lt;li&gt;Refactoring.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal of TDD is to ensure developers have a roadmap of their code outcome before writing the actual code. In TDD, you write a test (mostly unit tests). The test is expected to fail because there is no corresponding code. After writing your test, you are required to write your code or script. After, you can continuously refractor your codebase to successfully pass all your test cases. The test process is the driving force of software development. It helps build a resilient and optimized coding structure over a long time. TDD ensures developers write only the necessary codes required for a software or a codebase. TDD helps reduce breakage in applications while in production mode and improved productivity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Unit Testing&lt;/em&gt;&lt;/strong&gt; &lt;br&gt;
TDD requires that you write unit tests often. Unit is mostly referred to as class or group of functions. Keeping your unit minimal is a good practice in TDD. Minimal units help in reducing debugging period. In a component-based app like Vue.js, the unit is a component.&lt;/p&gt;

&lt;p&gt;To read more on Test Driven Development, get &lt;a href="http://www.amazon.fr/dp/0321146530"&gt;Test Driven Development: By Example&lt;/a&gt; by Kent Beck&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Introduction to Node.js, Vue.js, VueTestUtils&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Vue.js&lt;/em&gt;&lt;/strong&gt; &lt;br&gt;
Vue.js is a progressive framework for building user interfaces. Learning Vue.js requires an intermediate knowledge of HTML, CSS and Javascript. Grasping the basics before going into the framework might be the best decision in any chosen language of your choice. Vue.js is a javascript framework. For an introduction to Vue.js syntax, you can check out this &lt;a href="https://codepen.io/team/Vue/pen/KKpRVpx"&gt;Helloworld example&lt;/a&gt; by the Vue.js team. Vue.js is a component-based framework.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Node.js&lt;/em&gt;&lt;/strong&gt; &lt;br&gt;
Node.js is an open-source project that runs the V8 javascript engine, it is also a cross-platform runtime environment. Node.js has helped developers write server-side code. Node.js uses the javascript syntax. With a vast module of libraries, developers have shorter development time as most of the libraries handle bulky code contents. Node.js also have frameworks like &lt;a href="https://expressjs.com/"&gt;Express&lt;/a&gt;, &lt;a href="https://fastify.io/"&gt;Fastify&lt;/a&gt;, &lt;a href="https://feathersjs.com/"&gt;FeatherJs&lt;/a&gt;, &lt;a href="https://socket.io/"&gt;Socket.io&lt;/a&gt;, &lt;a href="https://strapi.io/"&gt;Strapi&lt;/a&gt; and others.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Vue Test Utils&lt;/em&gt;&lt;/strong&gt; &lt;br&gt;
How do we test our components? Earlier, we introduced units and for component-based apps, units are components. Vue.js is a component-based app needs components to be isolated to allow for testing.Vue test utils help with the isolation. Vue Test Utils is an official library of helper functions to help users test their Vue.js components. It provides some methods to mount and interact with Vue.js components in an isolated manner. We refer to this as a wrapper.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;But what is a wrapper?&lt;/em&gt; &lt;br&gt;
A wrapper is an abstraction of the mounted component. It provides some utility functions such as when users want to trigger a click or an event. We'll use this to execute some input ( props, store changes, etc.) so we can check that the output is correct (component rendering, Vue events, function calls, etc.).&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;For this tutorial, you're required to have:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://nodejs.dev/learn/introduction-to-nodejs"&gt;Node.js&lt;/a&gt; installed.&lt;/li&gt;
&lt;li&gt;Also, we will be using &lt;a href="https://v3.vuejs.org/guide/introduction.html"&gt;Vue3&lt;/a&gt; for this tutorial&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://vue-test-utils.vuejs.org/"&gt;Vue test utils 2&lt;/a&gt; (Vue test utils 1 target and earlier versions)&lt;/li&gt;
&lt;li&gt;A code editor.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Objectives&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learn the basic principles of Test Driven Development&lt;/li&gt;
&lt;li&gt;Why you should test your Vue.js app&lt;/li&gt;
&lt;li&gt;Learn how to unit test a Vue.js app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Setting up our environment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Vue3 gives us the opportunity to select unit tests while creating a vue project. You can follow the steps below for the &lt;a href="https://vue-test-utils.vuejs.org/installation/#using-vue-test-utils-with-jest-recommended"&gt;manual installation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For existing projects, you can use Vue CLI to set up Vue Test Utils in your current Vue app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vue add unit-jest
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @vue/test-utils
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your &lt;code&gt;package.json&lt;/code&gt; file should have added a new command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;package.json&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test:unit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vue-cli-service test:unit"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installation of all relevant dependecies either manually or to existing projects, we proceed to our code editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 -- Setting up our files
&lt;/h2&gt;

&lt;p&gt;After opening our code in our code editor, we will go to the test directory. Test directory is a root folder in our &lt;code&gt;&amp;lt;project-name&amp;gt;&lt;/code&gt;. Open the unit folder, then you can create a new file (&lt;code&gt;&amp;lt;project-name&amp;gt;/tests/unit/&amp;lt;file-name.spec.js&amp;gt;&lt;/code&gt;). It is a good practice to name the file as the component. Initially, there is an &lt;code&gt;example.spec.js&lt;/code&gt; file in the unit folder. Remember the goal of TDD is testing before code. You will create a boilerplate for the vue component in the component folder (&lt;code&gt;&amp;lt;project-name&amp;gt;/src/component/loginpage.vue&lt;/code&gt;). The boilerplate structureis provided below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;[&lt;span class="nt"&gt;&amp;lt;project-name&amp;gt;&lt;/span&gt;/src/component/loginpage.vue]

&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;style &lt;/span&gt;&lt;span class="na"&gt;scoped&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our spec file, we are importing our vue component and making use of the Vue test utils.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;import&lt;span class="o"&gt;{&lt;/span&gt; shallowMount &lt;span class="o"&gt;}&lt;/span&gt; from &lt;span class="s1"&gt;'@vue/test-utils'&lt;/span&gt;
import Login from &lt;span class="s1"&gt;'@/components/Login'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2 -- First test
&lt;/h2&gt;

&lt;p&gt;Our first test is to ensure if our Login components displays a form.&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="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="sr"&gt;/tests/u&lt;/span&gt;&lt;span class="nx"&gt;nit&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;loginpage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;shallowMount&lt;/span&gt; &lt;span class="p"&gt;}&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;@vue/test-utils&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Login&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;@/components/Login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;login.vue&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should show the form element on the user output&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="o"&gt;=&amp;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;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;shallowMount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Login&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;toBe&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running our test using the &lt;code&gt;yarn test:unit --watch&lt;/code&gt; or &lt;code&gt;npm run test:unit&lt;/code&gt; command, our test failed!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;FAIL  tests/unit/loginpage.spec.js
 login.vue
   ✕ should show the form element on the screen &lt;span class="o"&gt;(&lt;/span&gt;13ms&lt;span class="o"&gt;)&lt;/span&gt;

 ● login.vue › should show the form element on the screen
 Cannot call isVisible on an empty DOMWrapper.

   Expected: &lt;span class="nb"&gt;true
   &lt;/span&gt;Received: &lt;span class="nb"&gt;false

&lt;/span&gt;Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        1.174s
Ran all &lt;span class="nb"&gt;test &lt;/span&gt;suites related to changed files.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the error? Cannot call &lt;code&gt;isVisible&lt;/code&gt; on an empty DOMWrapper. We wrote a test without the code it will act on. Our component booilerplate is empty. To resolve this, we simply go to boilerplate our and write this code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;[&lt;span class="nt"&gt;&amp;lt;project-name&amp;gt;&lt;/span&gt;/src/component/loginpage.vue]

&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;style &lt;/span&gt;&lt;span class="na"&gt;scoped&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our test should pass now. Congratulations! You just wrote your first successful test!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;PASS  tests/unit/loginpage.spec.js
 login.vue
   ✓ should show the form element on the screen &lt;span class="o"&gt;(&lt;/span&gt;60ms&lt;span class="o"&gt;)&lt;/span&gt;

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.273s, estimated 9s
Ran all &lt;span class="nb"&gt;test &lt;/span&gt;suites related to changed files.

Watch Usage: Press w to show more.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 -- Further testing
&lt;/h2&gt;

&lt;p&gt;Let's go further by checking if our input field exists.&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="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="sr"&gt;/tests/u&lt;/span&gt;&lt;span class="nx"&gt;nit&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;loginpage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should contain input fields&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="o"&gt;=&amp;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;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;shallowMount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Login&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form &amp;gt; input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;toBe&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="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form should contain input fields with type="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="o"&gt;=&amp;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;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;shallowMount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Login&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input[type=tjavascriptext]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;toBe&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our test failed as there was no input field present in our form element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;FAIL  tests/unit/loginpage.spec.js
 login.vue
   ✓ should show the form element on the screen &lt;span class="o"&gt;(&lt;/span&gt;10ms&lt;span class="o"&gt;)&lt;/span&gt;
   ✕ should contain input fields &lt;span class="o"&gt;(&lt;/span&gt;5ms&lt;span class="o"&gt;)&lt;/span&gt;
   ✕ form should contain input fields with &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;10ms&lt;span class="o"&gt;)&lt;/span&gt;

 ● login.vue › should contain input fields

   expect&lt;span class="o"&gt;(&lt;/span&gt;received&lt;span class="o"&gt;)&lt;/span&gt;.toBe&lt;span class="o"&gt;(&lt;/span&gt;expected&lt;span class="o"&gt;)&lt;/span&gt; // Object.is equality

   Expected: &lt;span class="nb"&gt;true
   &lt;/span&gt;Received: &lt;span class="nb"&gt;false&lt;/span&gt;

 ● login.vue › form should contain input fields with &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;

   Unable to get input[type&lt;span class="o"&gt;=&lt;/span&gt;text] within: &amp;lt;div&amp;gt;


Test Suites: 1 failed, 1 total
Tests:       2 failed, 1 passed, 3 total
Snapshots:   0 total
Time:        3.549s
Ran all &lt;span class="nb"&gt;test &lt;/span&gt;suites related to changed files.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's open our &lt;code&gt;Login component&lt;/code&gt; and add some codes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;[&lt;span class="nt"&gt;&amp;lt;project-name&amp;gt;&lt;/span&gt;/src/component/loginpage.vue]

&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Username"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our test passed!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;PASS  tests/unit/loginpage.spec.js
 login.vue
   ✓ should show the form element on the screen &lt;span class="o"&gt;(&lt;/span&gt;13ms&lt;span class="o"&gt;)&lt;/span&gt;
   ✓ should contain input fields &lt;span class="o"&gt;(&lt;/span&gt;2ms&lt;span class="o"&gt;)&lt;/span&gt;
   ✓ form should contain input fields with &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;2ms&lt;span class="o"&gt;)&lt;/span&gt;

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        1.805s, estimated 2s
Ran all &lt;span class="nb"&gt;test &lt;/span&gt;suites related to changed files.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A bonus test is confirming the attribute of our input field. The &lt;code&gt;get()&lt;/code&gt; function allows for parameters. We can check for tag attributes like &lt;code&gt;type=text&lt;/code&gt;. isVisible check the visibility status (showing on the user output device). Though &lt;code&gt;isVisible()&lt;/code&gt; is deprecated, latest release of Vue still accept it.&lt;/p&gt;

&lt;p&gt;Our last test! Testing if our button triggers a click event. We trigger the click event listener, so that the Component executes the submit method. We use await to make sure the action is being reflected by Vue.&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="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="sr"&gt;/tests/u&lt;/span&gt;&lt;span class="nx"&gt;nit&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;loginpage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button trigger event&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form &amp;gt; button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emitted&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;toHaveProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;submit&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have a failed test again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;FAIL  tests/unit/loginpage.spec.js
 login.vue
   ✓ should show the form element on the screen &lt;span class="o"&gt;(&lt;/span&gt;12ms&lt;span class="o"&gt;)&lt;/span&gt;
   ✓ should contain input fields &lt;span class="o"&gt;(&lt;/span&gt;3ms&lt;span class="o"&gt;)&lt;/span&gt;
   ✓ form should contain input fields with &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;1ms&lt;span class="o"&gt;)&lt;/span&gt;
   ✕ button trigger event &lt;span class="o"&gt;(&lt;/span&gt;4ms&lt;span class="o"&gt;)&lt;/span&gt;

 ● login.vue › button trigger event

   Cannot call trigger on an empty DOMWrapper.

Test Suites: 1 failed, 1 total
Tests:       1 failed, 3 passed, 4 total
Snapshots:   0 total
Time:        3s
Ran all &lt;span class="nb"&gt;test &lt;/span&gt;suites related to changed files.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our triggering test failed as we don't have a corresponding button element in our login component. In our login component, we are going to add the button element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;[&lt;span class="nt"&gt;&amp;lt;project-name&amp;gt;&lt;/span&gt;/src/component/loginpage.vue]

&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Username"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

            &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our test failed as we do not have a corresponding method in our component boilerplate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;[&lt;span class="nt"&gt;&amp;lt;project-name&amp;gt;&lt;/span&gt;/src/component/loginpage.vue]

&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Username"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

            &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;()&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="nx"&gt;$emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;email&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our complete login component. Notice the additional change to the script section of the our component. Now all our tests should pass.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;PASS  tests/unit/loginpage.spec.js
 login.vue
   ✓ should show the form element on the screen &lt;span class="o"&gt;(&lt;/span&gt;11ms&lt;span class="o"&gt;)&lt;/span&gt;
   ✓ should contain input fields &lt;span class="o"&gt;(&lt;/span&gt;2ms&lt;span class="o"&gt;)&lt;/span&gt;
   ✓ form should contain input fields with &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;1ms&lt;span class="o"&gt;)&lt;/span&gt;
   ✓ button trigger event &lt;span class="o"&gt;(&lt;/span&gt;5ms&lt;span class="o"&gt;)&lt;/span&gt;

Test Suites: 1 passed, 1 total
Tests:       4 passed, 4 total
Snapshots:   0 total
Time:        1.88s, estimated 2s
Ran all &lt;span class="nb"&gt;test &lt;/span&gt;suites.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make our test codes easier, We can refractor by making the wrapper variable a global variable and our tests still passed.&lt;/p&gt;

&lt;p&gt;Due to the isVisible being deprecated, we can use the &lt;code&gt;exists()&lt;/code&gt; function. Testing depends on your contract with your end user.&lt;/p&gt;

&lt;p&gt;You need to be sure "DO I CARE IF THIS CHANGE?" If you care, test, else move to the next detail. TDD helps write robust tests (not too many, not too few).&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;An introduction to TEST DRIVEN DEVELOPMENT&lt;/li&gt;
&lt;li&gt;Benefit of TDD.&lt;/li&gt;
&lt;li&gt;Setting up our Vue project.&lt;/li&gt;
&lt;li&gt;Writing our first tests suites successfully.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To get the full Vue project, clone it on &lt;a href="https://github.com/D-Lite/vuetestapp"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vue</category>
      <category>testdev</category>
      <category>javascript</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
