<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Victor Anuebunwa's Blog]]></title><description><![CDATA[Victor Anuebunwa's Blog]]></description><link>https://log.victoranuebunwa.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1629683854889/F8B5Agqie.png</url><title>Victor Anuebunwa&apos;s Blog</title><link>https://log.victoranuebunwa.com</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 21 Apr 2026 20:03:20 GMT</lastBuildDate><atom:link href="https://log.victoranuebunwa.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Your application should be PCI-DSS-compliant. 🔒]]></title><description><![CDATA[If you’ve ever had to write an app to process card payments, not like integrating PayPal, but like being PayPal. You’ve probably heard of PCI DSS.

Payment Card Industry Data Security Standard (PCI DSS) is an information security standard for organiz...]]></description><link>https://log.victoranuebunwa.com/your-application-should-be-pci-dss-compliant</link><guid isPermaLink="true">https://log.victoranuebunwa.com/your-application-should-be-pci-dss-compliant</guid><category><![CDATA[pcidss]]></category><category><![CDATA[PCI DSS]]></category><category><![CDATA[Security]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[software security]]></category><dc:creator><![CDATA[Victor Anuebunwa]]></dc:creator><pubDate>Fri, 30 May 2025 00:15:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748102317706/d0cec6b8-e424-4a15-b585-fd51262b00d6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you’ve ever had to write an app to process card payments, not like integrating PayPal, but like being PayPal. You’ve probably heard of PCI DSS.</p>
<blockquote>
<p><em>Payment Card Industry Data Security Standard (PCI DSS)</em> is an information security standard for organizations that handle branded credit cards from the major card schemes. - Wikipedia</p>
</blockquote>
<p>In other words, it’s a giant checklist that says: “Hey, if you’re going to store or process people’s money, maybe don’t leave your database open to the world.”</p>
<h2 id="heading-why-am-i-even-writing-this"><strong>Why Am I Even Writing This?</strong></h2>
<p>After working on a PCI DSS–compliant app, I had a bit of an existential crisis and asked myself, “Why shouldn’t all apps follow PCI DSS secure coding practices?”</p>
<p>Believe me, PCI DSS is not as terrifying as it sounds. Compliance and privacy folks have a way of making every simple process sound enormous to feel good about themselves. Want to have a stressful day? Listen to them talk about PIA, DPIA, DSAR, or ROPA.</p>
<p>Not to oversimplify things, compliance can be painful. But if it helps protect your data and keeps you from ending up on “Have I Been Pwned”. Isn’t it kind of worth it?</p>
<h2 id="heading-tips-to-secure-coding">Tips To Secure Coding</h2>
<p>If you’re building a payment system or just trying to sleep better at night, here are some easy ways to secure coding that will bring you closer to PCI DSS compliance. Let’s dive in!</p>
<p><img src="https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExNnNpOTAxdDlpZzdoYXN3dHgxYjJ1andpdGF0bTh5c3hybWV0Mnp1byZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/hZlU5VZCPanL6I1Dix/giphy.gif" alt class="image--center mx-auto" /></p>
<h3 id="heading-subnets-nat-gateways-and-the-drama-of-networking"><strong>Subnets, NAT Gateways, and the Drama of Networking</strong></h3>
<p>If your app’s database is in a public subnet, I’m going to need you to stop reading and fix that. Now!</p>
<p>Ensure you use private subnets for anything sensitive, then route their traffic through a NAT Gateway so they can reach the internet (for patches, updates, memes, etc.) without being directly exposed. Think of it as giving your app a VPN to access the world, but telling it not to talk to strangers.</p>
<p>For “The Matrix” lovers, the Matrix world is a private subnet, the real world is the public subnet, and the secure phone Neo and his friends use to travel between worlds is the NAT Gateway. They can go through, but the machines can’t!</p>
<p>I love that reference, hope you do too. It took me a while. 😁</p>
<h3 id="heading-encrypt-all-the-things"><strong>Encrypt All the Things</strong></h3>
<p>You’ve heard this before. You’ll hear it again.</p>
<ul>
<li><p>Encrypt at rest.</p>
</li>
<li><p>Encrypt in transit.</p>
</li>
<li><p>Encrypt logs, backups, and even environment variables if you’re feeling spicy.</p>
</li>
</ul>
<p>For cloud services, managing encryption is easy peasy. You only have to enable them.</p>
<p>And please, Base64 is not an encryption. It’s for people who lie to themselves. You know who you are. Sure, it has its uses, but security is not one of them.</p>
<h3 id="heading-security-headers"><strong>Security Headers</strong></h3>
<p>Security headers are HTTP response headers that instruct the browser on how to handle security-related aspects of a website. They help you prevent attacks like Cross-Site Scripting (XSS), clickjacking, and man-in-the-middle attacks. Set It and Forget It</p>
<p>At the very least, add:</p>
<ul>
<li><p>Strict-Transport-Security</p>
</li>
<li><p>X-Content-Type-Options</p>
</li>
<li><p>Content-Security-Policy</p>
</li>
<li><p>X-Frame-Options</p>
</li>
</ul>
<p>They’re easy to configure in most frameworks and cloud services. Do it once, and you’re already better off than most.</p>
<h3 id="heading-firewalls-definitely-firewalls"><strong>Firewalls? Definitely Firewalls.</strong></h3>
<p><img src="https://media1.giphy.com/media/v1.Y2lkPTc5MGI3NjExNmV1eG5wdTR6M21pNzJ4ZnBwZTc5MjMzenJhZGJnbGd3NTZ2NjBvdCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/l41lRvFQYdlfvDTLG/giphy.gif" alt class="image--center mx-auto" /></p>
<p>Route public requests to your application through a firewall. It’s like putting a bouncer in front of your app.</p>
<p>Unlike the days of the boomers, where configuring a firewall is like assembling your furniture with instructions from Asgard. You might get it working, but at what cost? Present-day applications make this process a lot easier. Better if you’re on the cloud.</p>
<p>Cloud services like AWS WAF and Azure Firewall already come with pre-configured rules for common threats; you just have to switch them on, and they get to work.</p>
<p>Firewalls with minimum effort will help you stop known attack patterns, filter out bad IPs, and block that one guy still trying SQL injection from 2008.</p>
<h3 id="heading-access-control"><strong>Access Control</strong></h3>
<p>Your app isn’t a public library. Don’t give every service full access to everything just because it’s easier. You’re not Oprah Winfrey: “You get admin! You get admin! Everyone gets admin!”</p>
<p>Follow the “Principle of least privilege”, which means every user gets only what they absolutely need.</p>
<p>Try to rotate credentials and keys regularly and use roles over static keys when possible. Even cloud services prefer this. And they wrote the cloud.</p>
<h3 id="heading-staging-is-not-production"><strong>Staging Is Not Production</strong></h3>
<p>Your staging environment should mirror production in structure, not in secrets.</p>
<ul>
<li><p>Encrypt staging data too. Attackers don’t care where they get data from.</p>
</li>
<li><p>Don’t copy production secrets into staging.</p>
</li>
<li><p>Implement monitoring and WAF rules here as well.</p>
</li>
</ul>
<p>Hackers love a staging environment with fewer alarms. What’s a better place to test their scripts?</p>
<h3 id="heading-change-management"><strong>Change Management</strong></h3>
<p>This is adulting for DevOps. PCI DSS requires you to have procedures to track changes in code, infrastructure, and security policies.</p>
<p>In simple terms, use Git, use Infrastructure-as-code (Serverless Framework, Terraform, AWS CDK, etc.), and set up alerts. You want to know when someone (maybe even you) decides to push trauma into production.</p>
<h3 id="heading-logging-saves-lives"><strong>Logging Saves Lives</strong></h3>
<p>Logs are crucial. You hope to never need them, but if you do, you really need them. Keep secure and central logs for everything that matters:</p>
<ul>
<li><p><strong>Application logs:</strong> Log errors from applications and other important information to help you debug. Make sure not to log sensitive information.</p>
</li>
<li><p><strong>Access logs:</strong> Log who did what and when.</p>
</li>
<li><p><strong>Database logs</strong>: Catch slow queries, weird access patterns, and potential breaches.</p>
</li>
<li><p><strong>Audit logs:</strong> Log changes to permissions, firewall rules, and code deployments. Tools like AWS CloudTrail, GuardDuty, and Config can help you with this.</p>
</li>
</ul>
<h3 id="heading-prefer-orms"><strong>Prefer ORMs</strong></h3>
<p>This is your first line of defense (and sanity). Forget raw SQL unless you’re a database wizard with a death wish. Use an ORM. Yes, you could parameterize your raw SQL and improve security; however, there’s still a large chance that future you or your colleague will forget to parameterize a query properly, and just like that, you’re cooked.</p>
<p>ORMs abstract your database interactions, making code easier to maintain and less prone to injection attacks. Most modern ORMs parameterize queries out of the box. That means fewer chances to accidentally <code>DROP TABLE users;</code> during a late-night commit.</p>
<h2 id="heading-in-conclusion"><strong>In Conclusion</strong></h2>
<p>Now, seriously, was it that deep? It’s a long list, but I’m sure it’s filled with things you’re already doing. Just a few adjustments here and there, and you’re compliant!</p>
<p>Compliance is annoying, but so is a data breach. PCI DSS might feel like a giant list of “No, you can’t” but it’s just trying to keep you and your customers safe. On cloud platforms like AWS, Azure, and GCP, there’s no excuse not to follow these best practices. You’ve got the tools, you’ve got the docs, and now you’ve got a very persuasive blog post.</p>
<p>Stay safe. Stay compliant. And for the love of all that is good, don’t store unencrypted card numbers in a database column called <code>card_number</code>.</p>
]]></content:encoded></item><item><title><![CDATA[AWS Lambda and RDS Connection Nightmare (How I Got Out) 🧟‍♂️]]></title><description><![CDATA[Some years back, I remember feeling fancy about my AWS Lambda and RDS setup until we had to process some large data from a CSV. In summary, it was a bad day. Database memory usage flew up, database connections flew up, my heart flew up, and the whole...]]></description><link>https://log.victoranuebunwa.com/aws-lambda-and-rds-connection-nightmare-how-i-got-out</link><guid isPermaLink="true">https://log.victoranuebunwa.com/aws-lambda-and-rds-connection-nightmare-how-i-got-out</guid><category><![CDATA[AWS]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[rds]]></category><category><![CDATA[rds proxy]]></category><category><![CDATA[AWS serverless]]></category><category><![CDATA[serverless]]></category><category><![CDATA[AWS Aurora]]></category><dc:creator><![CDATA[Victor Anuebunwa]]></dc:creator><pubDate>Sun, 21 May 2023 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747805475806/af476df2-02d8-47ec-90a9-7fbac1ce7228.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Some years back, I remember feeling fancy about my AWS Lambda and RDS setup until we had to process some large data from a CSV. In summary, it was a bad day. Database memory usage flew up, database connections flew up, my heart flew up, and the whole thing crashed in my face.</p>
<p><img src="https://media1.giphy.com/media/v1.Y2lkPTc5MGI3NjExaWx3N2owMzUxbnJ5cG44dHV6bzBpYnlqYjQxenR6NWQ1MWhvcmRxNSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/OeyAkKTKYSvmw/giphy.gif" alt class="image--center mx-auto" /></p>
<p>Yes, you can assume load testing was never invented.</p>
<p>It’s been a long time since then, and the issue is long gone. However, I still meet developers struggling with the same problem to this day, so I decided to share my experience and how we resolved it.</p>
<h2 id="heading-context">Context</h2>
<p>My team needed to process data from CSV files periodically, which required extracting the data, computing the data, updating the database, and communicating with other services.</p>
<p>For extracting and computing, we used separate Lambdas and communicated via SQS/SNS.</p>
<p>For the database, we went with Aurora Serverless v1 because the processing was periodic. Or that’s what we want you to think. We are cheap people, and we’ll always go for the cheapest best option!</p>
<h2 id="heading-aurora-serverless-v1">Aurora Serverless V1</h2>
<p>Aurora Serverless is an AWS-managed database that scales on demand. It is normally more expensive than regular RDS with a heavy traffic load, but its ability to drop to zero during inactivity does magic. That means you don’t get to pay when the database is not in use.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Pros</strong></td><td><strong>Cons</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Auto-scaling (eventually)</td><td>Cold start delays up to 30s</td></tr>
<tr>
<td>Can shut down when idle</td><td>Dropped transactions during cold boots</td></tr>
<tr>
<td>No instance to manage</td><td>Scaling latency under load</td></tr>
</tbody>
</table>
</div><h2 id="heading-why-did-it-fail">Why did it fail?</h2>
<p>The major issue was the difference in scaling speed between AWS Lambda and RDS.</p>
<p>AWS Lambda is widely known for its scalability, and this is no joke. AWS Lambda functions scale horizontally in seconds, with each synchronously invoked function able to scale by 1,000 concurrent executions every 10 seconds.</p>
<p>Each new invocation may spin up a new environment (cold start) or reuse an existing environment (warm start). In most cases, connections are not shared across instances, and that means N invocations produce an equivalent of N environments, which produces N connections to the database.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747721823750/f038596b-9baa-45a1-b930-4f1e83834142.png" alt class="image--center mx-auto" /></p>
<p>Aurora Serverless can’t keep up. So they pile up and eventually, we hit the dreaded “Too many connections” error from the database.</p>
<h2 id="heading-fixes-we-attempted">Fixes We Attempted</h2>
<ol>
<li><strong>Retry Mechanism</strong></li>
</ol>
<p>We added retries with exponential backoff. It helped… barely.</p>
<ol start="2">
<li><strong>Connection Pooling (Don't Do It)</strong></li>
</ol>
<p>We tried connection pooling inside Lambda. Never worked. As explained earlier, new invocations create new environments, therefore, pools can’t be reused.</p>
<ol start="3">
<li><strong>Leveraged Lambda Execution Freezing</strong></li>
</ol>
<p>Lambda tries to be helpful by freezing (sort of caching) the execution environment after a run. If Lambda is invoked again soon, AWS does a warm start, “thaws” the function, and reuses the environment.</p>
<p>Part of what is frozen by AWS are global variables. You can take advantage of this to reuse DB connections between invocations (if you’re lucky to get a warm start).</p>
<p><strong>Don’t Do This</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">book_handler</span>(<span class="hljs-params">event, context</span>):</span>
    db_url = os.getenv(<span class="hljs-string">"DB_URL"</span>)
    db_client = db.connect(db_url)  <span class="hljs-comment"># Opens a connection on every call</span>
    book = db_client.get(book_id=event[<span class="hljs-string">"book_id"</span>])
    <span class="hljs-keyword">return</span> book
</code></pre>
<p><strong>Do This Instead</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os

db_url = os.getenv(<span class="hljs-string">"DB_URL"</span>)
db_client = db.connect(db_url)  <span class="hljs-comment"># Reused if Lambda stays warm</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">book_handler</span>(<span class="hljs-params">event, context</span>):</span>
    book = db_client.get(book_id=event[<span class="hljs-string">"book_id"</span>])
    <span class="hljs-keyword">return</span> book
</code></pre>
<p>This minimizes connection churn and keeps the database happy. However, for large requests, this all means nothing.</p>
<h2 id="heading-solution-at-last">Solution At Last</h2>
<p><img src="https://media.giphy.com/media/Fs2gBophYnz5m/giphy.gif" alt class="image--center mx-auto" /></p>
<h3 id="heading-rds-proxy"><strong>RDS Proxy</strong></h3>
<p>In late 2019, AWS announced RDS Proxy, and it became GA (General Availability) in mid-2020, and a game changer.</p>
<p>Amazon RDS Proxy is a fully managed database proxy for Amazon RDS. It acts as a connection pool between your application and the database, reducing the stress on database resources and improving application performance.</p>
<h3 id="heading-why-its-your-friend"><strong>Why it’s your friend:</strong></h3>
<p>RDS Proxy provides lots of advantages that will help you sleep at night, these include:</p>
<ul>
<li><p>Manages connection pooling for you</p>
</li>
<li><p>Works across multiple Lambda instances</p>
</li>
<li><p>Handles auth and failover</p>
</li>
<li><p>Reduces DB memory pressure</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747722399669/51de98d9-5bac-4fba-bab1-982d1350e8a5.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-caveats"><strong>Caveats:</strong></h3>
<ul>
<li><p>Adds a small latency (~few ms)</p>
</li>
<li><p>Not free, but worth it</p>
</li>
</ul>
<p>Unfortunately, it doesn’t have support for older RDS engines, and this includes Aurora Serverless V1.</p>
<h3 id="heading-aurora-serverless-v2-to-the-rescue"><strong>Aurora Serverless V2 to the Rescue</strong></h3>
<p>When Aurora Serverless V2 came out, things improved significantly.</p>
<ul>
<li><p>It scaled faster than v1.</p>
</li>
<li><p>It had no cold start delay.</p>
</li>
<li><p>Still cost-effective for bursty traffic</p>
</li>
<li><p>And best of all, it had support for RDS Proxy!</p>
</li>
</ul>
<h2 id="heading-tldr-the-golden-combo"><strong>TL;DR: The Golden Combo</strong></h2>
<p><img src="https://media1.giphy.com/media/v1.Y2lkPTc5MGI3NjExMm40aXhwcGdrbzRvcDQ4NXRtbnE3NmoxZTIxdHdtNzl3dGw1d3Q2ZiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/l4Jz3a8jO92crUlWM/giphy.gif" alt class="image--center mx-auto" /></p>
<p>Here’s the stack I now recommend, especially for periodic processing:</p>
<ul>
<li><p>Lambda (use warm connections wisely)</p>
</li>
<li><p>Aurora Serverless V2</p>
</li>
<li><p>RDS Proxy</p>
</li>
<li><p>Retry logic with backoff</p>
</li>
<li><p>SNS/SQS for decoupling workloads</p>
</li>
</ul>
<h2 id="heading-final-thoughts"><strong>Final Thoughts</strong></h2>
<p>Using Lambda with RDS used to feel like mediating the US and China trade agreement, but with RDS Proxy and Aurora Serverless V2, the dream of serverless with relational DB is now actually viable.</p>
<p>That said, don’t blindly go all in. For long-running or batch-heavy DB jobs, sometimes containers or Fargate are a better fit, and DynamoDB (NoSQL) does a better job at scaling.</p>
<p>But if you’re sticking with Lambda (and I don’t blame you).</p>
<p>Just remember:</p>
<ul>
<li><p>Don’t open DB connections inside the handler</p>
</li>
<li><p>Use Aurora Serverless V2 or DynamoDB</p>
</li>
<li><p>Prefer RDS Proxy for RDS</p>
</li>
<li><p>Load test your setup</p>
</li>
<li><p>And don’t trust blog posts too easily, especially this one. Test for your use case.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[How we reduced our NodeJs app size on AWS Lambda by over 80% 😮]]></title><description><![CDATA[This article highlights the steps we took to reduce the size of our NodeJs apps running on AWS Lambda but it still relates to any Node app running anywhere. In the end, you'll find out how we dropped our 50MB - 75MB (compressed) node apps to 8MB - 10...]]></description><link>https://log.victoranuebunwa.com/how-we-reduced-our-nodejs-app-size-on-aws-lambda</link><guid isPermaLink="true">https://log.victoranuebunwa.com/how-we-reduced-our-nodejs-app-size-on-aws-lambda</guid><dc:creator><![CDATA[Victor Anuebunwa]]></dc:creator><pubDate>Tue, 07 Sep 2021 08:37:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747806061661/d689b13b-eac5-410f-bc6c-07db7ce8694e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This article highlights the steps we took to reduce the size of our NodeJs apps running on AWS Lambda but it still relates to any Node app running anywhere. In the end, you'll find out how we dropped our 50MB - 75MB (compressed) node apps to 8MB - 10MB. Awesome, right?</p>
<p>But first, How did we get here?</p>
<p>How did this set of software developers exhaust the 75GB AWS Lambda storage limit? Oh yes, we did. 🙈</p>
<h2 id="heading-the-mistake">The Mistake</h2>
<p>Deploying microservices on AWS lambda could mean you have to work with other AWS services like SQS, SNS, API Gateway, etc., and using the <a target="_blank" href="https://www.serverless.com/framework/docs/">Serverless Framework</a>, the default practice will be to define handlers for events coming from each of these services.</p>
<p>Under the hood, the Serverless framework creates new lambda functions for each of the handlers you define.</p>
<p>Let's say you want to handle events from SNS, SQS, S3, and API Gateway, four Lambda functions will be created for each of those events using the same code base. This means that our 50MB app when deployed, becomes a huge 200MB app in total.</p>
<p>Here's the interesting part, this was only in our staging environment. Considering the number of microservices we had running, we were already at 50% usage, pushing our microservices to a new environment immediately doubled our storage usage and our deployments broke. Wahala 🙆🏽‍♂️</p>
<h2 id="heading-the-fix-how-we-reduced-our-aws-lambda-size">The Fix: How we reduced our AWS lambda size</h2>
<p><img src="https://media.giphy.com/media/Rk8CZk8M7UHzG/giphy.gif" alt="Tips" /></p>
<h3 id="heading-1-set-aws-sdk-as-a-dev-dependency">1. Set AWS-SDK as a dev dependency</h3>
<p>This is the mother of all. I won't even be bothered if you quit reading after this point.</p>
<p>The <code>aws-sdk</code> package alone is over 60MB (uncompressed). This is huge!</p>
<p>This was almost everything about our app size issue, our misfortune and also our miracle. The good news is that the <code>aws-sdk</code> comes pre-installed in your Lambda runtime, so you don't need to install it again. Only set it as a dev dependency.</p>
<p>Only if we knew this. I'm in severe pain now 😭</p>
<h3 id="heading-2-remove-unnecessary-packages">2. Remove unnecessary packages</h3>
<p><img src="https://media.giphy.com/media/1ncT70kECIAC4O83UD/giphy.gif" alt="Review packages" /></p>
<p>There is a good chance you've done this already. However, for large projects mostly, some unused packages can go undetected easily.</p>
<p>Try packages like <code>depcheck</code>. It helps you scan your app for unused and missing dependencies.</p>
<pre><code class="lang-python">npm install -g depcheck
</code></pre>
<p>The <code>-g</code> flag is important, please. Let's not complicate things here.</p>
<h3 id="heading-3-cross-check-for-dev-packages">3. Cross-check for dev packages</h3>
<p>Just like the <code>aws-sdk</code>, there are other packages that could disguise as production dependencies such as <code>serverless</code> and its plugins like <code>serverless-offline</code>.</p>
<p>These packages, in combination with the extra packages it installs for you, is over 100MB (uncompressed) in size, that is including <code>aws-sdk</code>. Hence, removing <code>aws-sdk</code> without removing these guys won't make any difference. It will be re-installed.</p>
<p>Since these types of packages are used for deployment and need to be installed while deployment is ongoing, one can easily mistake them as production packages. The best approach will be to set them as dev dependencies in your app and install them globally in your CI/CD Docker image. This approach also buys you more deployment time as you won't need to reinstall them every time your CI/CD pipeline runs.</p>
<p>Created this Docker image <a target="_blank" href="https://hub.docker.com/repository/docker/avonnadozie/serverless">avonnadozie/serverless</a> for this purpose, feel free to reuse it. It comes with a <code>serverless-offline</code> plugin and other necessary packages you need to deploy to lambda successfully.</p>
<h3 id="heading-4-use-the-production-flag">4. Use the <code>--production</code> flag</h3>
<p>This should be a common step as well, however, It doesn't hurt to repeat it.</p>
<p>Always remember to use the <code>--production</code> when running npm on production or in your CI/CD script to not install dev dependencies.</p>
<pre><code class="lang-python">npm install --production
</code></pre>
<h3 id="heading-5-reuse-available-lambda-runtime-packages">5. Reuse available Lambda runtime packages</h3>
<p>Just like <code>aws-sdk</code>, there are other packages such as <code>UUID</code> and <code>dotenv</code> that are already available in the lambda runtime which you can reuse.</p>
<p>You can refer to this <a target="_blank" href="https://log.victoranuebunwa.com/list-of-node-packages-pre-installed-on-aws-lambda-runtime">list of node packages in lambda runtime</a>.</p>
<h3 id="heading-6-inspect-deployment">6. Inspect deployment</h3>
<p>The crude way works well too. Download your live code and inspect it manually.</p>
<p>To download, go to your Lambda console, click on the function and choose "Export function" from the "Actions" dropdown.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630064480068/vfaAA1irk.png" alt="Export function" /></p>
<p>Then click on "Download deployment package" afterwards.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630064489265/AAkyr8z7u.png" alt="Download deployment package for lambda" /></p>
<p>Once the download is complete, go to the <code>node_modules</code> folder, rank the folders by size and take a look. It reveals a lot.</p>
<p>This was how we found out <code>aws-sdk</code> was still installed even after we've set it as a dev dependency.</p>
<h3 id="heading-7-move-out-large-non-js-files">7. Move out large non-js files</h3>
<p>Consider hosting large files such as images, or JSON on a private CDN (most likely S3) and read it from there.</p>
<p>This will cause a trade-off in speed but it could be worth it for you depending on the design of your app.</p>
<h3 id="heading-8-merge-serverless-handlers">8. Merge serverless handlers</h3>
<p>This is completely up to you and your app architecture, but you can consider reusing lambda functions to prevent serverless from creating additional and unnecessary functions for you.</p>
<p>For us, we found a way to merge SQS and SNS handlers given that their event input data and the way they work are similar and it saved us a whole lot of MBs.</p>
<p>The lambda console provides you with sample event input data on its test tab you can work with. Choose from the list.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630072857381/oxzIZqBAr.png" alt="Lambda event samples" /></p>
<h2 id="heading-benefits-to-reducing-your-application-size">Benefits to reducing your application size</h2>
<p>You might not be deploying multiple apps that will exhaust your AWS limit as we did, but there's still more you stand to gain by dropping your app size regardless.</p>
<ul>
<li><p>Faster <a target="_blank" href="https://aws.amazon.com/blogs/compute/operating-lambda-performance-optimization-part-1/">cold start</a> time</p>
</li>
<li><p>Faster deployment with CI/CD as the zipping and upload process will be faster</p>
</li>
<li><p>Less cost on storage and memory</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[List of node packages pre-installed on AWS Lambda runtime]]></title><description><![CDATA[In the desire to make our node_modules folder as small as possible when deploying on Lambda, we thought about reusing node packages that could already be existing on Lambda in my team. But after searching and rephrasing keywords severally, we couldn'...]]></description><link>https://log.victoranuebunwa.com/list-of-node-packages-pre-installed-on-aws-lambda-runtime</link><guid isPermaLink="true">https://log.victoranuebunwa.com/list-of-node-packages-pre-installed-on-aws-lambda-runtime</guid><category><![CDATA[AWS]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[lambda]]></category><dc:creator><![CDATA[Victor Anuebunwa]]></dc:creator><pubDate>Thu, 02 Sep 2021 09:11:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1630173621105/HhPXu7iPw.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the desire to make our <code>node_modules</code> folder as small as possible when deploying on Lambda, we thought about reusing node packages that could already be existing on Lambda in my team. But after searching and rephrasing keywords severally, we couldn't find a decent list of pre-installed packages.</p>
<p>Pulled this up by running <code>npm ls -g</code> on a lambda with Node.js 14 runtime. </p>
<p>It's a long list. I would advise you to use your browser search to avoid the pain of scrolling. Press <code>cmd + F</code> for Mac or <code>Ctrl + F</code> on Windows.</p>
<h2 id="list-of-node-packages-on-lambda-nodejs-14-nodejs14x-runtime">List of node packages on Lambda Node.js 14 (nodejs14.x) runtime</h2>
<pre><code class="lang-Text">└─┬ npm@6.14.14
  ├── abbrev@1.1.1
  ├── ansicolors@0.3.2
  ├── ansistyles@0.1.3
  ├── aproba@2.0.0
  ├── archy@1.0.0
  ├─┬ bin-links@1.1.8
  │ ├── bluebird@3.5.5 deduped
  │ ├── cmd-shim@3.0.3 deduped
  │ ├── gentle-fs@2.3.1 deduped
  │ ├── graceful-fs@4.2.4 deduped
  │ ├── npm-normalize-package-bin@1.0.1
  │ └── write-file-atomic@2.4.3 deduped
  ├── bluebird@3.5.5
  ├── byte-size@5.0.1
  ├─┬ cacache@12.0.3
  │ ├── bluebird@3.5.5 deduped
  │ ├── chownr@1.1.4 deduped
  │ ├── figgy-pudding@3.5.1 deduped
  │ ├── glob@7.1.6 deduped
  │ ├── graceful-fs@4.2.4 deduped
  │ ├── infer-owner@1.0.4 deduped
  │ ├── lru-cache@5.1.1 deduped
  │ ├── mississippi@3.0.0 deduped
  │ ├── mkdirp@0.5.5 deduped
  │ ├── move-concurrently@1.0.1 deduped
  │ ├── promise-inflight@1.0.1 deduped
  │ ├── rimraf@2.7.1 deduped
  │ ├── ssri@6.0.2 deduped
  │ ├── unique-filename@1.1.1 deduped
  │ └── y18n@4.0.1
  ├── call-limit@1.1.1
  ├── chownr@1.1.4
  ├── ci-info@2.0.0
  ├─┬ cli-columns@3.1.2
  │ ├─┬ string-width@2.1.1
  │ │ ├── is-fullwidth-code-point@2.0.0
  │ │ └─┬ strip-ansi@4.0.0
  │ │   └── ansi-regex@3.0.0
  │ └─┬ strip-ansi@3.0.1
  │   └── ansi-regex@2.1.1
  ├─┬ cli-table3@0.5.1
  │ ├── colors@1.3.3
  │ ├── object-assign@4.1.1
  │ └── string-width@2.1.1 deduped
  ├─┬ cmd-shim@3.0.3
  │ ├── graceful-fs@4.2.4 deduped
  │ └── mkdirp@0.5.5 deduped
  ├─┬ columnify@1.5.4
  │ ├── strip-ansi@3.0.1 deduped
  │ └─┬ wcwidth@1.0.1
  │   └─┬ defaults@1.0.3
  │     └── clone@1.0.4
  ├─┬ config-chain@1.1.12
  │ ├── ini@1.3.8 deduped
  │ └── proto-list@1.2.4
  ├── debuglog@1.0.1
  ├── detect-indent@5.0.0
  ├── detect-newline@2.1.0
  ├─┬ dezalgo@1.0.3
  │ ├── asap@2.0.6
  │ └── wrappy@1.0.2
  ├── editor@1.0.0
  ├── figgy-pudding@3.5.1
  ├── find-npm-prefix@1.0.2
  ├─┬ fs-vacuum@1.2.10
  │ ├── graceful-fs@4.2.4 deduped
  │ ├── path-is-inside@1.0.2 deduped
  │ └── rimraf@2.7.1 deduped
  ├─┬ fs-write-stream-atomic@1.0.10
  │ ├── graceful-fs@4.2.4 deduped
  │ ├── iferr@0.1.5
  │ ├── imurmurhash@0.1.4 deduped
  │ └─┬ readable-stream@2.3.6
  │   ├── core-util-is@1.0.2
  │   ├── inherits@2.0.4 deduped
  │   ├── isarray@1.0.0
  │   ├── process-nextick-args@2.0.0
  │   ├── safe-buffer@5.1.2 deduped
  │   ├─┬ string_decoder@1.1.1
  │   │ └── safe-buffer@5.1.2 deduped
  │   └── util-deprecate@1.0.2 deduped
  ├─┬ gentle-fs@2.3.1
  │ ├── aproba@1.2.0
  │ ├── chownr@1.1.4 deduped
  │ ├── cmd-shim@3.0.3 deduped
  │ ├── fs-vacuum@1.2.10 deduped
  │ ├── graceful-fs@4.2.4 deduped
  │ ├── iferr@0.1.5
  │ ├── infer-owner@1.0.4 deduped
  │ ├── mkdirp@0.5.5 deduped
  │ ├── path-is-inside@1.0.2 deduped
  │ ├── read-cmd-shim@1.0.5 deduped
  │ └── slide@1.1.6 deduped
  ├─┬ glob@7.1.6
  │ ├── fs.realpath@1.0.0
  │ ├── inflight@1.0.6 deduped
  │ ├── inherits@2.0.4 deduped
  │ ├─┬ minimatch@3.0.4
  │ │ └─┬ brace-expansion@1.1.11
  │ │   ├── balanced-match@1.0.0
  │ │   └── concat-map@0.0.1
  │ ├── once@1.4.0 deduped
  │ └── path-is-absolute@1.0.1
  ├── graceful-fs@4.2.4
  ├── has-unicode@2.0.1
  ├── hosted-git-info@2.8.9
  ├── iferr@1.0.2
  ├── imurmurhash@0.1.4
  ├── infer-owner@1.0.4
  ├─┬ inflight@1.0.6
  │ ├── once@1.4.0 deduped
  │ └── wrappy@1.0.2 deduped
  ├── inherits@2.0.4
  ├── ini@1.3.8
  ├─┬ init-package-json@1.10.3
  │ ├── glob@7.1.6 deduped
  │ ├── npm-package-arg@6.1.1 deduped
  │ ├─┬ promzard@0.3.0
  │ │ └── read@1.0.7 deduped
  │ ├── read@1.0.7 deduped
  │ ├── read-package-json@2.1.1 deduped
  │ ├── semver@5.7.1 deduped
  │ ├── validate-npm-package-license@3.0.4 deduped
  │ └── validate-npm-package-name@3.0.0 deduped
  ├─┬ is-cidr@3.0.0
  │ └─┬ cidr-regex@2.0.10
  │   └── ip-regex@2.1.0
  ├── json-parse-better-errors@1.0.2
  ├─┬ JSONStream@1.3.5
  │ ├── jsonparse@1.3.1
  │ └── through@2.3.8
  ├── lazy-property@1.0.0
  ├─┬ libcipm@4.0.8
  │ ├── bin-links@1.1.8 deduped
  │ ├── bluebird@3.5.5 deduped
  │ ├── figgy-pudding@3.5.1 deduped
  │ ├── find-npm-prefix@1.0.2 deduped
  │ ├── graceful-fs@4.2.4 deduped
  │ ├── ini@1.3.8 deduped
  │ ├── lock-verify@2.1.0 deduped
  │ ├── mkdirp@0.5.5 deduped
  │ ├── npm-lifecycle@3.1.5 deduped
  │ ├── npm-logical-tree@1.2.1
  │ ├── npm-package-arg@6.1.1 deduped
  │ ├── pacote@9.5.12 deduped
  │ ├── read-package-json@2.1.1 deduped
  │ ├── rimraf@2.7.1 deduped
  │ └── worker-farm@1.7.0 deduped
  ├─┬ libnpm@3.0.1
  │ ├── bin-links@1.1.8 deduped
  │ ├── bluebird@3.5.5 deduped
  │ ├── find-npm-prefix@1.0.2 deduped
  │ ├── libnpmaccess@3.0.2 deduped
  │ ├─┬ libnpmconfig@1.2.1
  │ │ ├── figgy-pudding@3.5.1 deduped
  │ │ ├─┬ find-up@3.0.0
  │ │ │ └─┬ locate-path@3.0.0
  │ │ │   ├─┬ p-locate@3.0.0
  │ │ │   │ └─┬ p-limit@2.2.0
  │ │ │   │   └── p-try@2.2.0
  │ │ │   └── path-exists@3.0.0
  │ │ └── ini@1.3.8 deduped
  │ ├── libnpmhook@5.0.3 deduped
  │ ├── libnpmorg@1.0.1 deduped
  │ ├─┬ libnpmpublish@1.1.2
  │ │ ├── aproba@2.0.0 deduped
  │ │ ├── figgy-pudding@3.5.1 deduped
  │ │ ├── get-stream@4.1.0 deduped
  │ │ ├── lodash.clonedeep@4.5.0 deduped
  │ │ ├── normalize-package-data@2.5.0 deduped
  │ │ ├── npm-package-arg@6.1.1 deduped
  │ │ ├── npm-registry-fetch@4.0.7 deduped
  │ │ ├── semver@5.7.1 deduped
  │ │ └── ssri@6.0.2 deduped
  │ ├── libnpmsearch@2.0.2 deduped
  │ ├── libnpmteam@1.0.2 deduped
  │ ├── lock-verify@2.1.0 deduped
  │ ├── npm-lifecycle@3.1.5 deduped
  │ ├── npm-logical-tree@1.2.1 deduped
  │ ├── npm-package-arg@6.1.1 deduped
  │ ├── npm-profile@4.0.4 deduped
  │ ├── npm-registry-fetch@4.0.7 deduped
  │ ├── npmlog@4.1.2 deduped
  │ ├── pacote@9.5.12 deduped
  │ ├── read-package-json@2.1.1 deduped
  │ └── stringify-package@1.0.1 deduped
  ├─┬ libnpmaccess@3.0.2
  │ ├── aproba@2.0.0 deduped
  │ ├─┬ get-stream@4.1.0
  │ │ └── pump@3.0.0 deduped
  │ ├── npm-package-arg@6.1.1 deduped
  │ └── npm-registry-fetch@4.0.7 deduped
  ├─┬ libnpmhook@5.0.3
  │ ├── aproba@2.0.0 deduped
  │ ├── figgy-pudding@3.5.1 deduped
  │ ├── get-stream@4.1.0 deduped
  │ └── npm-registry-fetch@4.0.7 deduped
  ├─┬ libnpmorg@1.0.1
  │ ├── aproba@2.0.0 deduped
  │ ├── figgy-pudding@3.5.1 deduped
  │ ├── get-stream@4.1.0 deduped
  │ └── npm-registry-fetch@4.0.7 deduped
  ├─┬ libnpmsearch@2.0.2
  │ ├── figgy-pudding@3.5.1 deduped
  │ ├── get-stream@4.1.0 deduped
  │ └── npm-registry-fetch@4.0.7 deduped
  ├─┬ libnpmteam@1.0.2
  │ ├── aproba@2.0.0 deduped
  │ ├── figgy-pudding@3.5.1 deduped
  │ ├── get-stream@4.1.0 deduped
  │ └── npm-registry-fetch@4.0.7 deduped
  ├─┬ libnpx@10.2.4
  │ ├── dotenv@5.0.1
  │ ├── npm-package-arg@6.1.1 deduped
  │ ├── rimraf@2.7.1 deduped
  │ ├── safe-buffer@5.1.2 deduped
  │ ├── update-notifier@2.5.0 deduped
  │ ├── which@1.3.1 deduped
  │ ├── y18n@4.0.1 deduped
  │ └─┬ yargs@14.2.3
  │   ├─┬ cliui@5.0.0
  │   │ ├─┬ string-width@3.1.0
  │   │ │ ├── emoji-regex@7.0.3 deduped
  │   │ │ ├── is-fullwidth-code-point@2.0.0
  │   │ │ └── strip-ansi@5.2.0 deduped
  │   │ ├─┬ strip-ansi@5.2.0
  │   │ │ └── ansi-regex@4.1.0
  │   │ └─┬ wrap-ansi@5.1.0
  │   │   ├── ansi-styles@3.2.1 deduped
  │   │   ├─┬ string-width@3.1.0
  │   │   │ ├── emoji-regex@7.0.3 deduped
  │   │   │ ├── is-fullwidth-code-point@2.0.0
  │   │   │ └── strip-ansi@5.2.0 deduped
  │   │   └─┬ strip-ansi@5.2.0
  │   │     └── ansi-regex@4.1.0
  │   ├── decamelize@1.2.0
  │   ├─┬ find-up@3.0.0
  │   │ └─┬ locate-path@3.0.0
  │   │   ├─┬ p-locate@3.0.0
  │   │   │ └─┬ p-limit@2.3.0
  │   │   │   └── p-try@2.2.0
  │   │   └── path-exists@3.0.0 deduped
  │   ├── get-caller-file@2.0.5
  │   ├── require-directory@2.1.1
  │   ├── require-main-filename@2.0.0
  │   ├── set-blocking@2.0.0 deduped
  │   ├─┬ string-width@3.1.0
  │   │ ├── emoji-regex@7.0.3
  │   │ ├── is-fullwidth-code-point@2.0.0
  │   │ └─┬ strip-ansi@5.2.0
  │   │   └── ansi-regex@4.1.0
  │   ├── which-module@2.0.0
  │   ├── y18n@4.0.1 deduped
  │   └─┬ yargs-parser@15.0.1
  │     ├── camelcase@5.3.1
  │     └── decamelize@1.2.0 deduped
  ├─┬ lock-verify@2.1.0
  │ ├── npm-package-arg@6.1.1 deduped
  │ └── semver@5.7.1 deduped
  ├─┬ lockfile@1.0.4
  │ └── signal-exit@3.0.2
  ├── lodash._baseindexof@3.1.0
  ├─┬ lodash._baseuniq@4.6.0
  │ ├── lodash._createset@4.0.3
  │ └── lodash._root@3.0.1
  ├── lodash._bindcallback@3.0.1
  ├── lodash._cacheindexof@3.0.2
  ├─┬ lodash._createcache@3.1.2
  │ └── lodash._getnative@3.9.1 deduped
  ├── lodash._getnative@3.9.1
  ├── lodash.clonedeep@4.5.0
  ├── lodash.restparam@3.6.1
  ├── lodash.union@4.6.0
  ├── lodash.uniq@4.5.0
  ├── lodash.without@4.4.0
  ├─┬ lru-cache@5.1.1
  │ └── yallist@3.0.3
  ├── meant@1.0.2
  ├─┬ mississippi@3.0.0
  │ ├─┬ concat-stream@1.6.2
  │ │ ├── buffer-from@1.0.0
  │ │ ├── inherits@2.0.4 deduped
  │ │ ├─┬ readable-stream@2.3.6
  │ │ │ ├── core-util-is@1.0.2 deduped
  │ │ │ ├── inherits@2.0.4 deduped
  │ │ │ ├── isarray@1.0.0 deduped
  │ │ │ ├── process-nextick-args@2.0.0 deduped
  │ │ │ ├── safe-buffer@5.1.2 deduped
  │ │ │ ├─┬ string_decoder@1.1.1
  │ │ │ │ └── safe-buffer@5.1.2 deduped
  │ │ │ └── util-deprecate@1.0.2 deduped
  │ │ └── typedarray@0.0.6
  │ ├─┬ duplexify@3.6.0
  │ │ ├── end-of-stream@1.4.1 deduped
  │ │ ├── inherits@2.0.4 deduped
  │ │ ├─┬ readable-stream@2.3.6
  │ │ │ ├── core-util-is@1.0.2 deduped
  │ │ │ ├── inherits@2.0.4 deduped
  │ │ │ ├── isarray@1.0.0 deduped
  │ │ │ ├── process-nextick-args@2.0.0 deduped
  │ │ │ ├── safe-buffer@5.1.2 deduped
  │ │ │ ├─┬ string_decoder@1.1.1
  │ │ │ │ └── safe-buffer@5.1.2 deduped
  │ │ │ └── util-deprecate@1.0.2 deduped
  │ │ └── stream-shift@1.0.0
  │ ├─┬ end-of-stream@1.4.1
  │ │ └── once@1.4.0 deduped
  │ ├─┬ flush-write-stream@1.0.3
  │ │ ├── inherits@2.0.4 deduped
  │ │ └─┬ readable-stream@2.3.6
  │ │   ├── core-util-is@1.0.2 deduped
  │ │   ├── inherits@2.0.4 deduped
  │ │   ├── isarray@1.0.0 deduped
  │ │   ├── process-nextick-args@2.0.0 deduped
  │ │   ├── safe-buffer@5.1.2 deduped
  │ │   ├─┬ string_decoder@1.1.1
  │ │   │ └── safe-buffer@5.1.2 deduped
  │ │   └── util-deprecate@1.0.2 deduped
  │ ├─┬ from2@2.3.0
  │ │ ├── inherits@2.0.4 deduped
  │ │ └─┬ readable-stream@2.3.6
  │ │   ├── core-util-is@1.0.2 deduped
  │ │   ├── inherits@2.0.4 deduped
  │ │   ├── isarray@1.0.0 deduped
  │ │   ├── process-nextick-args@2.0.0 deduped
  │ │   ├── safe-buffer@5.1.2 deduped
  │ │   ├─┬ string_decoder@1.1.1
  │ │   │ └── safe-buffer@5.1.2 deduped
  │ │   └── util-deprecate@1.0.2 deduped
  │ ├─┬ parallel-transform@1.1.0
  │ │ ├── cyclist@0.2.2
  │ │ ├── inherits@2.0.4 deduped
  │ │ └─┬ readable-stream@2.3.6
  │ │   ├── core-util-is@1.0.2 deduped
  │ │   ├── inherits@2.0.4 deduped
  │ │   ├── isarray@1.0.0 deduped
  │ │   ├── process-nextick-args@2.0.0 deduped
  │ │   ├── safe-buffer@5.1.2 deduped
  │ │   ├─┬ string_decoder@1.1.1
  │ │   │ └── safe-buffer@5.1.2 deduped
  │ │   └── util-deprecate@1.0.2 deduped
  │ ├─┬ pump@3.0.0
  │ │ ├── end-of-stream@1.4.1 deduped
  │ │ └── once@1.4.0 deduped
  │ ├─┬ pumpify@1.5.1
  │ │ ├── duplexify@3.6.0 deduped
  │ │ ├── inherits@2.0.4 deduped
  │ │ └─┬ pump@2.0.1
  │ │   ├── end-of-stream@1.4.1 deduped
  │ │   └── once@1.4.0 deduped
  │ ├─┬ stream-each@1.2.2
  │ │ ├── end-of-stream@1.4.1 deduped
  │ │ └── stream-shift@1.0.0 deduped
  │ └─┬ through2@2.0.3
  │   ├─┬ readable-stream@2.3.6
  │   │ ├── core-util-is@1.0.2 deduped
  │   │ ├── inherits@2.0.4 deduped
  │   │ ├── isarray@1.0.0 deduped
  │   │ ├── process-nextick-args@2.0.0 deduped
  │   │ ├── safe-buffer@5.1.2 deduped
  │   │ ├─┬ string_decoder@1.1.1
  │   │ │ └── safe-buffer@5.1.2 deduped
  │   │ └── util-deprecate@1.0.2 deduped
  │   └── xtend@4.0.1
  ├─┬ mkdirp@0.5.5
  │ └── minimist@1.2.5
  ├─┬ move-concurrently@1.0.1
  │ ├── aproba@1.2.0
  │ ├─┬ copy-concurrently@1.0.5
  │ │ ├── aproba@1.2.0
  │ │ ├── fs-write-stream-atomic@1.0.10 deduped
  │ │ ├── iferr@0.1.5
  │ │ ├── mkdirp@0.5.5 deduped
  │ │ ├── rimraf@2.7.1 deduped
  │ │ └── run-queue@1.0.3 deduped
  │ ├── fs-write-stream-atomic@1.0.10 deduped
  │ ├── mkdirp@0.5.5 deduped
  │ ├── rimraf@2.7.1 deduped
  │ └─┬ run-queue@1.0.3
  │   └── aproba@1.2.0
  ├─┬ node-gyp@5.1.0
  │ ├── env-paths@2.2.0
  │ ├── glob@7.1.6 deduped
  │ ├── graceful-fs@4.2.4 deduped
  │ ├── mkdirp@0.5.5 deduped
  │ ├── nopt@4.0.3 deduped
  │ ├── npmlog@4.1.2 deduped
  │ ├── request@2.88.0 deduped
  │ ├── rimraf@2.7.1 deduped
  │ ├── semver@5.7.1 deduped
  │ ├── tar@4.4.15 deduped
  │ └── which@1.3.1 deduped
  ├─┬ nopt@4.0.3
  │ ├── abbrev@1.1.1 deduped
  │ └── osenv@0.1.5 deduped
  ├─┬ normalize-package-data@2.5.0
  │ ├── hosted-git-info@2.8.9 deduped
  │ ├─┬ resolve@1.10.0
  │ │ └── path-parse@1.0.6
  │ ├── semver@5.7.1 deduped
  │ └── validate-npm-package-license@3.0.4 deduped
  ├─┬ npm-audit-report@1.3.3
  │ ├── cli-table3@0.5.1 deduped
  │ └── console-control-strings@1.1.0
  ├── npm-cache-filename@1.0.2
  ├─┬ npm-install-checks@3.0.2
  │ └── semver@5.7.1 deduped
  ├─┬ npm-lifecycle@3.1.5
  │ ├── byline@5.0.0
  │ ├── graceful-fs@4.2.4 deduped
  │ ├── node-gyp@5.1.0 deduped
  │ ├── resolve-from@4.0.0
  │ ├── slide@1.1.6 deduped
  │ ├── uid-number@0.0.6 deduped
  │ ├── umask@1.1.0 deduped
  │ └── which@1.3.1 deduped
  ├─┬ npm-package-arg@6.1.1
  │ ├── hosted-git-info@2.8.9 deduped
  │ ├── osenv@0.1.5 deduped
  │ ├── semver@5.7.1 deduped
  │ └── validate-npm-package-name@3.0.0 deduped
  ├─┬ npm-packlist@1.4.8
  │ ├─┬ ignore-walk@3.0.3
  │ │ └── minimatch@3.0.4 deduped
  │ ├─┬ npm-bundled@1.1.1
  │ │ └── npm-normalize-package-bin@1.0.1 deduped
  │ └── npm-normalize-package-bin@1.0.1 deduped
  ├─┬ npm-pick-manifest@3.0.2
  │ ├── figgy-pudding@3.5.1 deduped
  │ ├── npm-package-arg@6.1.1 deduped
  │ └── semver@5.7.1 deduped
  ├─┬ npm-profile@4.0.4
  │ ├── aproba@2.0.0 deduped
  │ ├── figgy-pudding@3.5.1 deduped
  │ └── npm-registry-fetch@4.0.7 deduped
  ├─┬ npm-registry-fetch@4.0.7
  │ ├── bluebird@3.5.5 deduped
  │ ├── figgy-pudding@3.5.1 deduped
  │ ├── JSONStream@1.3.5 deduped
  │ ├── lru-cache@5.1.1 deduped
  │ ├─┬ make-fetch-happen@5.0.2
  │ │ ├─┬ agentkeepalive@3.5.2
  │ │ │ └─┬ humanize-ms@1.2.1
  │ │ │   └── ms@2.1.1
  │ │ ├── cacache@12.0.3 deduped
  │ │ ├── http-cache-semantics@3.8.1
  │ │ ├─┬ http-proxy-agent@2.1.0
  │ │ │ ├─┬ agent-base@4.3.0
  │ │ │ │ └─┬ es6-promisify@5.0.0
  │ │ │ │   └── es6-promise@4.2.8
  │ │ │ └─┬ debug@3.1.0
  │ │ │   └── ms@2.0.0
  │ │ ├─┬ https-proxy-agent@2.2.4
  │ │ │ ├── agent-base@4.3.0 deduped
  │ │ │ └── debug@3.1.0 deduped
  │ │ ├── lru-cache@5.1.1 deduped
  │ │ ├── mississippi@3.0.0 deduped
  │ │ ├─┬ node-fetch-npm@2.0.2
  │ │ │ ├─┬ encoding@0.1.12
  │ │ │ │ └─┬ iconv-lite@0.4.23
  │ │ │ │   └── safer-buffer@2.1.2 deduped
  │ │ │ ├── json-parse-better-errors@1.0.2 deduped
  │ │ │ └── safe-buffer@5.1.2 deduped
  │ │ ├── promise-retry@1.1.1 deduped
  │ │ ├─┬ socks-proxy-agent@4.0.2
  │ │ │ ├─┬ agent-base@4.2.1
  │ │ │ │ └── es6-promisify@5.0.0 deduped
  │ │ │ └─┬ socks@2.3.3
  │ │ │   ├── ip@1.1.5
  │ │ │   └── smart-buffer@4.1.0
  │ │ └── ssri@6.0.2 deduped
  │ ├── npm-package-arg@6.1.1 deduped
  │ └── safe-buffer@5.2.1
  ├── npm-user-validate@1.0.1
  ├─┬ npmlog@4.1.2
  │ ├─┬ are-we-there-yet@1.1.4
  │ │ ├── delegates@1.0.0
  │ │ └─┬ readable-stream@2.3.6
  │ │   ├── core-util-is@1.0.2 deduped
  │ │   ├── inherits@2.0.4 deduped
  │ │   ├── isarray@1.0.0 deduped
  │ │   ├── process-nextick-args@2.0.0 deduped
  │ │   ├── safe-buffer@5.1.2 deduped
  │ │   ├─┬ string_decoder@1.1.1
  │ │   │ └── safe-buffer@5.1.2 deduped
  │ │   └── util-deprecate@1.0.2 deduped
  │ ├── console-control-strings@1.1.0 deduped
  │ ├─┬ gauge@2.7.4
  │ │ ├── aproba@1.2.0
  │ │ ├── console-control-strings@1.1.0 deduped
  │ │ ├── has-unicode@2.0.1 deduped
  │ │ ├── object-assign@4.1.1 deduped
  │ │ ├── signal-exit@3.0.2 deduped
  │ │ ├─┬ string-width@1.0.2
  │ │ │ ├── code-point-at@1.1.0
  │ │ │ ├─┬ is-fullwidth-code-point@1.0.0
  │ │ │ │ └── number-is-nan@1.0.1
  │ │ │ └── strip-ansi@3.0.1 deduped
  │ │ ├── strip-ansi@3.0.1 deduped
  │ │ └─┬ wide-align@1.1.2
  │ │   └─┬ string-width@1.0.2
  │ │     ├── code-point-at@1.1.0 deduped
  │ │     ├── is-fullwidth-code-point@1.0.0 deduped
  │ │     └── strip-ansi@3.0.1 deduped
  │ └── set-blocking@2.0.0
  ├─┬ once@1.4.0
  │ └── wrappy@1.0.2 deduped
  ├── opener@1.5.2
  ├─┬ osenv@0.1.5
  │ ├── os-homedir@1.0.2
  │ └── os-tmpdir@1.0.2
  ├─┬ pacote@9.5.12
  │ ├── bluebird@3.5.5 deduped
  │ ├── cacache@12.0.3 deduped
  │ ├── chownr@1.1.4 deduped
  │ ├── figgy-pudding@3.5.1 deduped
  │ ├── get-stream@4.1.0 deduped
  │ ├── glob@7.1.6 deduped
  │ ├── infer-owner@1.0.4 deduped
  │ ├── lru-cache@5.1.1 deduped
  │ ├── make-fetch-happen@5.0.2 deduped
  │ ├── minimatch@3.0.4 deduped
  │ ├─┬ minipass@2.9.0
  │ │ ├── safe-buffer@5.1.2 deduped
  │ │ └── yallist@3.0.3 deduped
  │ ├── mississippi@3.0.0 deduped
  │ ├── mkdirp@0.5.5 deduped
  │ ├── normalize-package-data@2.5.0 deduped
  │ ├── npm-normalize-package-bin@1.0.1 deduped
  │ ├── npm-package-arg@6.1.1 deduped
  │ ├── npm-packlist@1.4.8 deduped
  │ ├── npm-pick-manifest@3.0.2 deduped
  │ ├── npm-registry-fetch@4.0.7 deduped
  │ ├── osenv@0.1.5 deduped
  │ ├── promise-inflight@1.0.1 deduped
  │ ├─┬ promise-retry@1.1.1
  │ │ ├── err-code@1.1.2
  │ │ └── retry@0.10.1
  │ ├─┬ protoduck@5.0.1
  │ │ └── genfun@5.0.0
  │ ├── rimraf@2.7.1 deduped
  │ ├── safe-buffer@5.1.2 deduped
  │ ├── semver@5.7.1 deduped
  │ ├── ssri@6.0.2 deduped
  │ ├── tar@4.4.15 deduped
  │ ├── unique-filename@1.1.1 deduped
  │ └── which@1.3.1 deduped
  ├── path-is-inside@1.0.2
  ├── promise-inflight@1.0.1
  ├── qrcode-terminal@0.12.0
  ├─┬ query-string@6.8.2
  │ ├── decode-uri-component@0.2.0
  │ ├── split-on-first@1.1.0
  │ └── strict-uri-encode@2.0.0
  ├── qw@1.0.1
  ├─┬ read@1.0.7
  │ └── mute-stream@0.0.7
  ├─┬ read-cmd-shim@1.0.5
  │ └── graceful-fs@4.2.4 deduped
  ├─┬ read-installed@4.0.3
  │ ├── debuglog@1.0.1 deduped
  │ ├── graceful-fs@4.2.4 deduped
  │ ├── read-package-json@2.1.1 deduped
  │ ├── readdir-scoped-modules@1.1.0 deduped
  │ ├── semver@5.7.1 deduped
  │ ├── slide@1.1.6 deduped
  │ └── util-extend@1.0.3
  ├─┬ read-package-json@2.1.1
  │ ├── glob@7.1.6 deduped
  │ ├── graceful-fs@4.2.4 deduped
  │ ├── json-parse-better-errors@1.0.2 deduped
  │ ├── normalize-package-data@2.5.0 deduped
  │ └── npm-normalize-package-bin@1.0.1 deduped
  ├─┬ read-package-tree@5.3.1
  │ ├── read-package-json@2.1.1 deduped
  │ ├── readdir-scoped-modules@1.1.0 deduped
  │ └─┬ util-promisify@2.1.0
  │   └─┬ object.getownpropertydescriptors@2.0.3
  │     ├─┬ define-properties@1.1.3
  │     │ └── object-keys@1.0.12
  │     └─┬ es-abstract@1.12.0
  │       ├─┬ es-to-primitive@1.2.0
  │       │ ├── is-callable@1.1.4 deduped
  │       │ ├── is-date-object@1.0.1
  │       │ └─┬ is-symbol@1.0.2
  │       │   └── has-symbols@1.0.0
  │       ├── function-bind@1.1.1
  │       ├─┬ has@1.0.3
  │       │ └── function-bind@1.1.1 deduped
  │       ├── is-callable@1.1.4
  │       └─┬ is-regex@1.0.4
  │         └── has@1.0.3 deduped
  ├─┬ readable-stream@3.6.0
  │ ├── inherits@2.0.4 deduped
  │ ├─┬ string_decoder@1.3.0
  │ │ └── safe-buffer@5.2.0
  │ └── util-deprecate@1.0.2
  ├─┬ readdir-scoped-modules@1.1.0
  │ ├── debuglog@1.0.1 deduped
  │ ├── dezalgo@1.0.3 deduped
  │ ├── graceful-fs@4.2.4 deduped
  │ └── once@1.4.0 deduped
  ├─┬ request@2.88.0
  │ ├── aws-sign2@0.7.0
  │ ├── aws4@1.8.0
  │ ├── caseless@0.12.0
  │ ├─┬ combined-stream@1.0.6
  │ │ └── delayed-stream@1.0.0
  │ ├── extend@3.0.2
  │ ├── forever-agent@0.6.1
  │ ├─┬ form-data@2.3.2
  │ │ ├── asynckit@0.4.0
  │ │ ├── combined-stream@1.0.6 deduped
  │ │ └── mime-types@2.1.19 deduped
  │ ├─┬ har-validator@5.1.5
  │ │ ├─┬ ajv@6.12.6
  │ │ │ ├── fast-deep-equal@3.1.3
  │ │ │ ├── fast-json-stable-stringify@2.0.0
  │ │ │ ├── json-schema-traverse@0.4.1
  │ │ │ └─┬ uri-js@4.4.0
  │ │ │   └── punycode@2.1.1
  │ │ └── har-schema@2.0.0
  │ ├─┬ http-signature@1.2.0
  │ │ ├── assert-plus@1.0.0
  │ │ ├─┬ jsprim@1.4.1
  │ │ │ ├── assert-plus@1.0.0 deduped
  │ │ │ ├── extsprintf@1.3.0
  │ │ │ ├── json-schema@0.2.3
  │ │ │ └─┬ verror@1.10.0
  │ │ │   ├── assert-plus@1.0.0 deduped
  │ │ │   ├── core-util-is@1.0.2 deduped
  │ │ │   └── extsprintf@1.3.0 deduped
  │ │ └─┬ sshpk@1.14.2
  │ │   ├─┬ asn1@0.2.4
  │ │   │ └── safer-buffer@2.1.2 deduped
  │ │   ├── assert-plus@1.0.0 deduped
  │ │   ├─┬ bcrypt-pbkdf@1.0.2
  │ │   │ └── tweetnacl@0.14.5 deduped
  │ │   ├─┬ dashdash@1.14.1
  │ │   │ └── assert-plus@1.0.0 deduped
  │ │   ├─┬ ecc-jsbn@0.1.2
  │ │   │ ├── jsbn@0.1.1 deduped
  │ │   │ └── safer-buffer@2.1.2 deduped
  │ │   ├─┬ getpass@0.1.7
  │ │   │ └── assert-plus@1.0.0 deduped
  │ │   ├── jsbn@0.1.1
  │ │   ├── safer-buffer@2.1.2
  │ │   └── tweetnacl@0.14.5
  │ ├── is-typedarray@1.0.0
  │ ├── isstream@0.1.2
  │ ├── json-stringify-safe@5.0.1
  │ ├─┬ mime-types@2.1.19
  │ │ └── mime-db@1.35.0
  │ ├── oauth-sign@0.9.0
  │ ├── performance-now@2.1.0
  │ ├── qs@6.5.2
  │ ├── safe-buffer@5.1.2 deduped
  │ ├─┬ tough-cookie@2.4.3
  │ │ ├── psl@1.1.29
  │ │ └── punycode@1.4.1
  │ ├─┬ tunnel-agent@0.6.0
  │ │ └── safe-buffer@5.1.2 deduped
  │ └── uuid@3.3.3 deduped
  ├── retry@0.12.0
  ├─┬ rimraf@2.7.1
  │ └── glob@7.1.6 deduped
  ├── safe-buffer@5.1.2
  ├── semver@5.7.1
  ├─┬ sha@3.0.0
  │ └── graceful-fs@4.2.4 deduped
  ├── slide@1.1.6
  ├── sorted-object@2.0.1
  ├─┬ sorted-union-stream@2.1.3
  │ ├─┬ from2@1.3.0
  │ │ ├── inherits@2.0.4 deduped
  │ │ └─┬ readable-stream@1.1.14
  │ │   ├── core-util-is@1.0.2 deduped
  │ │   ├── inherits@2.0.4 deduped
  │ │   ├── isarray@0.0.1
  │ │   └── string_decoder@0.10.31
  │ └─┬ stream-iterate@1.2.0
  │   ├─┬ readable-stream@2.3.6
  │   │ ├── core-util-is@1.0.2 deduped
  │   │ ├── inherits@2.0.4 deduped
  │   │ ├── isarray@1.0.0 deduped
  │   │ ├── process-nextick-args@2.0.0 deduped
  │   │ ├── safe-buffer@5.1.2 deduped
  │   │ ├─┬ string_decoder@1.1.1
  │   │ │ └── safe-buffer@5.1.2 deduped
  │   │ └── util-deprecate@1.0.2 deduped
  │   └── stream-shift@1.0.0 deduped
  ├─┬ ssri@6.0.2
  │ └── figgy-pudding@3.5.1 deduped
  ├── stringify-package@1.0.1
  ├─┬ tar@4.4.15
  │ ├── chownr@1.1.4 deduped
  │ ├─┬ fs-minipass@1.2.7
  │ │ └─┬ minipass@2.9.0
  │ │   ├── safe-buffer@5.1.2 deduped
  │ │   └── yallist@3.0.3 deduped
  │ ├─┬ minipass@2.9.0
  │ │ ├── safe-buffer@5.1.2 deduped
  │ │ └── yallist@3.0.3 deduped
  │ ├─┬ minizlib@1.3.3
  │ │ └─┬ minipass@2.9.0
  │ │   ├── safe-buffer@5.1.2 deduped
  │ │   └── yallist@3.0.3 deduped
  │ ├── mkdirp@0.5.5 deduped
  │ ├── safe-buffer@5.1.2 deduped
  │ └── yallist@3.0.3 deduped
  ├── text-table@0.2.0
  ├── tiny-relative-date@1.3.0
  ├── uid-number@0.0.6
  ├── umask@1.1.0
  ├─┬ unique-filename@1.1.1
  │ └─┬ unique-slug@2.0.0
  │   └── imurmurhash@0.1.4 deduped
  ├── unpipe@1.0.0
  ├─┬ update-notifier@2.5.0
  │ ├─┬ boxen@1.3.0
  │ │ ├─┬ ansi-align@2.0.0
  │ │ │ └── string-width@2.1.1 deduped
  │ │ ├── camelcase@4.1.0
  │ │ ├── chalk@2.4.1 deduped
  │ │ ├── cli-boxes@1.0.0
  │ │ ├── string-width@2.1.1 deduped
  │ │ ├─┬ term-size@1.2.0
  │ │ │ └─┬ execa@0.7.0
  │ │ │   ├─┬ cross-spawn@5.1.0
  │ │ │   │ ├─┬ lru-cache@4.1.5
  │ │ │   │ │ ├── pseudomap@1.0.2
  │ │ │   │ │ └── yallist@2.1.2
  │ │ │   │ ├─┬ shebang-command@1.2.0
  │ │ │   │ │ └── shebang-regex@1.0.0
  │ │ │   │ └── which@1.3.1 deduped
  │ │ │   ├── get-stream@3.0.0
  │ │ │   ├── is-stream@1.1.0
  │ │ │   ├─┬ npm-run-path@2.0.2
  │ │ │   │ └── path-key@2.0.1
  │ │ │   ├── p-finally@1.0.0
  │ │ │   ├── signal-exit@3.0.2 deduped
  │ │ │   └── strip-eof@1.0.0
  │ │ └─┬ widest-line@2.0.1
  │ │   └── string-width@2.1.1 deduped
  │ ├─┬ chalk@2.4.1
  │ │ ├─┬ ansi-styles@3.2.1
  │ │ │ └─┬ color-convert@1.9.1
  │ │ │   └── color-name@1.1.3
  │ │ ├── escape-string-regexp@1.0.5
  │ │ └─┬ supports-color@5.4.0
  │ │   └── has-flag@3.0.0
  │ ├─┬ configstore@3.1.5
  │ │ ├─┬ dot-prop@4.2.1
  │ │ │ └── is-obj@1.0.1
  │ │ ├── graceful-fs@4.2.4 deduped
  │ │ ├─┬ make-dir@1.3.0
  │ │ │ └── pify@3.0.0
  │ │ ├─┬ unique-string@1.0.0
  │ │ │ └── crypto-random-string@1.0.0
  │ │ ├── write-file-atomic@2.4.3 deduped
  │ │ └── xdg-basedir@3.0.0 deduped
  │ ├── import-lazy@2.1.0
  │ ├─┬ is-ci@1.2.1
  │ │ └── ci-info@1.6.0
  │ ├─┬ is-installed-globally@0.1.0
  │ │ ├─┬ global-dirs@0.1.1
  │ │ │ └── ini@1.3.8 deduped
  │ │ └─┬ is-path-inside@1.0.1
  │ │   └── path-is-inside@1.0.2 deduped
  │ ├── is-npm@1.0.0
  │ ├─┬ latest-version@3.1.0
  │ │ └─┬ package-json@4.0.1
  │ │   ├─┬ got@6.7.1
  │ │   │ ├─┬ create-error-class@3.0.2
  │ │   │ │ └── capture-stack-trace@1.0.0
  │ │   │ ├── duplexer3@0.1.4
  │ │   │ ├── get-stream@3.0.0
  │ │   │ ├── is-redirect@1.0.0
  │ │   │ ├── is-retry-allowed@1.2.0
  │ │   │ ├── is-stream@1.1.0 deduped
  │ │   │ ├── lowercase-keys@1.0.1
  │ │   │ ├── safe-buffer@5.1.2 deduped
  │ │   │ ├── timed-out@4.0.1
  │ │   │ ├── unzip-response@2.0.1
  │ │   │ └─┬ url-parse-lax@1.0.0
  │ │   │   └── prepend-http@1.0.4
  │ │   ├─┬ registry-auth-token@3.4.0
  │ │   │ ├─┬ rc@1.2.8
  │ │   │ │ ├── deep-extend@0.6.0
  │ │   │ │ ├── ini@1.3.8 deduped
  │ │   │ │ ├── minimist@1.2.5
  │ │   │ │ └── strip-json-comments@2.0.1
  │ │   │ └── safe-buffer@5.1.2 deduped
  │ │   ├─┬ registry-url@3.1.0
  │ │   │ └── rc@1.2.8 deduped
  │ │   └── semver@5.7.1 deduped
  │ ├─┬ semver-diff@2.1.0
  │ │ └── semver@5.7.1 deduped
  │ └── xdg-basedir@3.0.0
  ├── uuid@3.3.3
  ├─┬ validate-npm-package-license@3.0.4
  │ ├─┬ spdx-correct@3.0.0
  │ │ ├── spdx-expression-parse@3.0.0 deduped
  │ │ └── spdx-license-ids@3.0.5
  │ └─┬ spdx-expression-parse@3.0.0
  │   ├── spdx-exceptions@2.1.0
  │   └── spdx-license-ids@3.0.5 deduped
  ├─┬ validate-npm-package-name@3.0.0
  │ └── builtins@1.0.3
  ├─┬ which@1.3.1
  │ └── isexe@2.0.0
  ├─┬ worker-farm@1.7.0
  │ └─┬ errno@0.1.7
  │   └── prr@1.0.1
  └─┬ write-file-atomic@2.4.3
    ├── graceful-fs@4.2.4 deduped
    ├── imurmurhash@0.1.4 deduped
    └── signal-exit@3.0.2 deduped
</code></pre>
]]></content:encoded></item><item><title><![CDATA[How I survive testing on NodeJs and Jest 🤒]]></title><description><![CDATA[Coming from a PHP background and with PHPUnit testing, I started my journey into writing tests on NodeJs with some expectations.
For most, I was disappointed but for some, I was blown away. I guess this is a feeling you have to get used to with JavaS...]]></description><link>https://log.victoranuebunwa.com/how-i-survive-testing-on-nodejs-and-jest</link><guid isPermaLink="true">https://log.victoranuebunwa.com/how-i-survive-testing-on-nodejs-and-jest</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Testing]]></category><category><![CDATA[Jest]]></category><category><![CDATA[PHPUnit]]></category><dc:creator><![CDATA[Victor Anuebunwa]]></dc:creator><pubDate>Tue, 31 Aug 2021 09:13:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747806455107/45aca077-80bb-4532-a219-1c347e475cbd.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Coming from a PHP background and with PHPUnit testing, I started my journey into writing tests on NodeJs with some expectations.</p>
<p>For most, I was disappointed but for some, I was blown away. I guess this is a feeling you have to get used to with JavaScript.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://media.giphy.com/media/l3978rwTcbblCh224/giphy.gif">https://media.giphy.com/media/l3978rwTcbblCh224/giphy.gif</a></div>
<p> </p>
<h2 id="heading-phpunit-vs-jest">PHPUnit Vs Jest</h2>
<p>PHPUnit provides you with more test functions to work with, has better error tracing, and is easier to debug.</p>
<p>However, testing on NodeJs is faster than testing with PHPUnit.</p>
<p>Correction, testing on NodeJs is <strong>way faster</strong> than testing with PHPUnit, because Jest runs your tests in parallel, and in the world of CI/CD, this means something very important. Fast deployment time! 🙌🏽</p>
<p>This is great, however, working with tests that run in parallel comes with its own challenges.</p>
<h2 id="heading-tips-for-testing-on-nodejs-using-jest">Tips for testing on NodeJs using Jest</h2>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://media.giphy.com/media/1n833bZxdzKzaErLe9/giphy.gif">https://media.giphy.com/media/1n833bZxdzKzaErLe9/giphy.gif</a></div>
<p> </p>
<h3 id="heading-beware-of-asynchronous-access-to-data">Beware of asynchronous access to data</h3>
<p>Tests running in parallel means that you will have multiple tests making requests to the database at the same time.</p>
<p>Expect inconsistency from tests like this</p>
<pre><code class="lang-JavaScript"><span class="hljs-comment">// Get User Test</span>
 test(<span class="hljs-string">'get user'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> request
      .get(<span class="hljs-string">'/v1/user/1'</span>)
      .set(<span class="hljs-string">'Authorization'</span>, <span class="hljs-string">`Bearer sample-token`</span>)
      .send();

    expect(response.status).toBe(<span class="hljs-number">200</span>);
 });

<span class="hljs-comment">// Delete User Test</span>
 test(<span class="hljs-string">'delete user'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> request
      .delete(<span class="hljs-string">'/v1/user/1'</span>)
      .set(<span class="hljs-string">'Authorization'</span>, <span class="hljs-string">`Bearer sample-token`</span>)
      .send();

    expect(response.status).toBe(<span class="hljs-number">200</span>);
 });
</code></pre>
<h4 id="heading-the-problem">The problem</h4>
<p>The "Get user" test is going to be inconsistent depending on which of the tests runs first. If the "Delete User" test runs first, the "Get User" test will fail by the time it runs because the user will no longer exist.</p>
<h4 id="heading-the-solution">The solution</h4>
<p>Ensure that each test works with its own unique data.</p>
<pre><code class="lang-JavaScript"><span class="hljs-comment">// Get User Test</span>
 test(<span class="hljs-string">'get user'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-comment">// Create a new user</span>
    <span class="hljs-keyword">const</span> user = User.create({<span class="hljs-attr">name</span>: <span class="hljs-string">"Sample user 1"</span>});
   <span class="hljs-comment">// Get the user</span>
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> request
      .get(<span class="hljs-string">`/v1/user/<span class="hljs-subst">${user.id}</span>`</span>)
      .set(<span class="hljs-string">'Authorization'</span>, <span class="hljs-string">`Bearer sample-token`</span>)
      .send();

    expect(response.status).toBe(<span class="hljs-number">200</span>);
 });

<span class="hljs-comment">// Delete User Test</span>
 test(<span class="hljs-string">'delete user'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-comment">// Create a new user</span>
    <span class="hljs-keyword">const</span> user = User.create({<span class="hljs-attr">name</span>: <span class="hljs-string">"Sample user 2"</span>});
    <span class="hljs-comment">// Delete the user</span>
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> request
      .delete(<span class="hljs-string">`/v1/user/<span class="hljs-subst">${user.id}</span>`</span>)
      .set(<span class="hljs-string">'Authorization'</span>, <span class="hljs-string">`Bearer sample-token`</span>)
      .send();

    expect(response.status).toBe(<span class="hljs-number">200</span>);
 });
</code></pre>
<h3 id="heading-always-remember-your-promises">Always remember your <code>Promises</code></h3>
<p>Always remember to await functions that return a promise.</p>
<p>Obvious right? I will bet you still forgot one some minutes ago.</p>
<p>On a serious note, these kinds of errors in tests can mess up your week and are difficult to detect. For example:</p>
<pre><code class="lang-JavaScript"><span class="hljs-keyword">const</span> user = User.findByPk(<span class="hljs-number">1</span>); <span class="hljs-comment">// no await</span>
expect(user).not.toBeNull();
</code></pre>
<h4 id="heading-the-problem-1">The problem</h4>
<p>This will always be true as it will be testing on the returned <code>Promise</code> object which will not be null.</p>
<h4 id="heading-the-solution-1">The solution</h4>
<p>Await</p>
<pre><code class="lang-JavaScript"><span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> User.findByPk(<span class="hljs-number">1</span>); <span class="hljs-comment">// await </span>
expect(user).not.toBeNull();
</code></pre>
<h3 id="heading-prefer-debugger-to-consolelog">Prefer Debugger to <code>console.log</code></h3>
<p>Debugger adds more flare to error tracing, get used to it.</p>
<p>Debuggers allow you to literally go into the function and see what happens step by step and view the real content of each variable at any point, while <code>console.log</code> only shows you the string representation of the variable you log which could be hiding that extra piece of information you need to figure the bug out.</p>
<p>Additionally, <code>console.log</code> codes can easily find their way to production and you find yourself unknowingly logging sensitive information which could be dangerous.</p>
<h3 id="heading-mock-calls-to-external-apis-or-resources">Mock calls to external APIs or resources</h3>
<p>This is more of a general tip when testing with any framework.</p>
<p>For most, your test should focus on testing the functions and features of your app, not the functionality or output of an external application.</p>
<p>Avoid consuming external resources during tests as this could introduce inconsistencies to your code when those requests fail and also increase the time your tests take to run.</p>
<p>It's best practice to mock these resources or API responses instead.</p>
<p>Example:</p>
<pre><code class="lang-python">const getSignedUrl = (key, bucket = null) =&gt; {
  <span class="hljs-keyword">if</span> (process.env.NODE_ENV === <span class="hljs-string">'test'</span>) {
    <span class="hljs-keyword">return</span> `https://s3.eu-west<span class="hljs-number">-2.</span>amazonaws.com/sample/${key}`;
  }

  <span class="hljs-keyword">return</span> s3.getSignedUrl(<span class="hljs-string">'getObject'</span>, {
    Bucket: bucket,
    Key: key,
    Expires: <span class="hljs-number">60</span>,
  });
};
</code></pre>
]]></content:encoded></item><item><title><![CDATA[First Post 🥳]]></title><description><![CDATA[Today is one of those days I succumbed to peer pressure, or maybe it's what I wanted all along.
Today, I get to start writing!
I'm excited, for real.
So what will I be writing on?
As much as I would love to write more tutorials on topics that's alrea...]]></description><link>https://log.victoranuebunwa.com/first-post</link><guid isPermaLink="true">https://log.victoranuebunwa.com/first-post</guid><dc:creator><![CDATA[Victor Anuebunwa]]></dc:creator><pubDate>Fri, 27 Aug 2021 02:25:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1629293807675/vASyrdvp9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today is one of those days I succumbed to peer pressure, or maybe it's what I wanted all along.</p>
<p>Today, I get to start writing!</p>
<p>I'm excited, for real.</p>
<h3 id="heading-so-what-will-i-be-writing-on">So what will I be writing on?</h3>
<p>As much as I would love to write more tutorials on topics that's already been written on, or on tech you might never use, I've decided to make this [b]log a bit more personal and share those tips and moments that you won't regularly find in your Software Development books or blogs.</p>
<p>It's about being unique right? Actually, I don't care. </p>
<p>So far in my software development experience, some moments have come to become notably weird or too awesome for me not to write on.</p>
<p>So on this blog, I'll be sharing:</p>
<ul>
<li>"Not so obvious tech tips" I come across daily</li>
<li>My "Eureka" moments, which come with tips too</li>
<li>Maybe some mid-level tutorials here and there. No <code>Promises</code> (pun intended)</li>
<li>And those other moments you discover your two-week-old bug was all about a misplaced comma 😒</li>
</ul>
<p>In summary, it's the tips and cruise for me.</p>
<p>If you're getting the vibes and as excited as I am, welcome to my [b]log! </p>
<p><img src="https://media.giphy.com/media/l4JyOCNEfXvVYEqB2/giphy.gif" alt="Welcome" /></p>
]]></content:encoded></item></channel></rss>