<?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: Victor Anuebunwa</title>
    <description>The latest articles on DEV Community by Victor Anuebunwa (@avonnadozie).</description>
    <link>https://dev.to/avonnadozie</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%2F71204%2Fd785053d-1ce4-4ef8-b6be-b4a3e9991ab6.png</url>
      <title>DEV Community: Victor Anuebunwa</title>
      <link>https://dev.to/avonnadozie</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/avonnadozie"/>
    <language>en</language>
    <item>
      <title>Your application should be PCI-DSS-compliant</title>
      <dc:creator>Victor Anuebunwa</dc:creator>
      <pubDate>Fri, 30 May 2025 00:38:54 +0000</pubDate>
      <link>https://dev.to/avonnadozie/your-application-should-be-pci-dss-compliant-1l6n</link>
      <guid>https://dev.to/avonnadozie/your-application-should-be-pci-dss-compliant-1l6n</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Payment Card Industry Data Security Standard (PCI DSS)&lt;/em&gt; is an information security standard for organizations that handle branded credit cards from the major card schemes. - Wikipedia&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;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.”&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why Am I Even Writing This?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;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?”&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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?&lt;/p&gt;

&lt;h2&gt;
  
  
  Tips To Secure Coding
&lt;/h2&gt;

&lt;p&gt;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!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F4xadojhkh1z446kzne5z.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F4xadojhkh1z446kzne5z.gif" alt="Let's go!" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Subnets, NAT Gateways, and the Drama of Networking&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If your app’s database is in a public subnet, I’m going to need you to stop reading and fix that. Now!&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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!&lt;/p&gt;

&lt;p&gt;I love that reference, hope you do too. It took me a while. 😁&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Encrypt All the Things&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;You’ve heard this before. You’ll hear it again.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Encrypt at rest.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Encrypt in transit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Encrypt logs, backups, and even environment variables if you’re feeling spicy.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For cloud services, managing encryption is easy peasy. You only have to enable them.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Security Headers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;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&lt;/p&gt;

&lt;p&gt;At the very least, add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Strict-Transport-Security&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;X-Content-Type-Options&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Content-Security-Policy&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;X-Frame-Options&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They’re easy to configure in most frameworks and cloud services. Do it once, and you’re already better off than most.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Firewalls? Definitely Firewalls.&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fb2omymcn1c7ck4158tkz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fb2omymcn1c7ck4158tkz.gif" alt="Neo stopping bullets" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Route public requests to your application through a firewall. It’s like putting a bouncer in front of your app.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Access Control&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;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!”&lt;/p&gt;

&lt;p&gt;Follow the “Principle of least privilege”, which means every user gets only what they absolutely need.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Staging Is Not Production&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Your staging environment should mirror production in structure, not in secrets.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Encrypt staging data too. Attackers don’t care where they get data from.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Don’t copy production secrets into staging.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement monitoring and WAF rules here as well.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hackers love a staging environment with fewer alarms. What’s a better place to test their scripts?&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Change Management&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This is adulting for DevOps. PCI DSS requires you to have procedures to track changes in code, infrastructure, and security policies.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Logging Saves Lives&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Application logs:&lt;/strong&gt; Log errors from applications and other important information to help you debug. Make sure not to log sensitive information.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Access logs:&lt;/strong&gt; Log who did what and when.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Database logs&lt;/strong&gt;: Catch slow queries, weird access patterns, and potential breaches.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Audit logs:&lt;/strong&gt; Log changes to permissions, firewall rules, and code deployments. Tools like AWS CloudTrail, GuardDuty, and Config can help you with this.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Prefer ORMs&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;DROP TABLE users;&lt;/code&gt; during a late-night commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;In Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;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!&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Stay safe. Stay compliant. And for the love of all that is good, don’t store unencrypted card numbers in a database column called &lt;code&gt;card_number&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>pcidss</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>AWS Lambda and RDS Connection Nightmare (How I Got Out)</title>
      <dc:creator>Victor Anuebunwa</dc:creator>
      <pubDate>Wed, 21 May 2025 16:51:10 +0000</pubDate>
      <link>https://dev.to/avonnadozie/aws-lambda-and-rds-connection-nightmare-how-i-got-out-4m7e</link>
      <guid>https://dev.to/avonnadozie/aws-lambda-and-rds-connection-nightmare-how-i-got-out-4m7e</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F6tnihhrkdv1j393w3sj8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F6tnihhrkdv1j393w3sj8.gif" alt="Old man accidentally deletes computer" width="498" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes, you can assume load testing was never invented.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;For extracting and computing, we used separate Lambdas and communicated via SQS/SNS.&lt;/p&gt;

&lt;p&gt;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!&lt;/p&gt;

&lt;h2&gt;
  
  
  Aurora Serverless V1
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Auto-scaling (eventually)&lt;/td&gt;
&lt;td&gt;Cold start delays up to 30s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can shut down when idle&lt;/td&gt;
&lt;td&gt;Dropped transactions during cold boots&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No instance to manage&lt;/td&gt;
&lt;td&gt;Scaling latency under load&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Why did it fail?
&lt;/h2&gt;

&lt;p&gt;The major issue was the difference in scaling speed between AWS Lambda and RDS.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fxo1juook2oc7i7ex06uq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fxo1juook2oc7i7ex06uq.png" alt="Flow diagram with Lambda and RDS" width="800" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Aurora Serverless can’t keep up. So they pile up and eventually, we hit the dreaded “Too many connections” error from the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixes We Attempted
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Retry Mechanism&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We added retries with exponential backoff. It helped… barely.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Connection Pooling (Don't Do It)&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We tried connection pooling inside Lambda. Never worked. As explained earlier, new invocations create new environments, therefore, pools can’t be reused.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Leveraged Lambda Execution Freezing&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t Do This&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;book_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;db_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;db_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Opens a connection on every call
&lt;/span&gt;    &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db_client&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="n"&gt;book_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;book_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Do This Instead&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;db_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;db_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Reused if Lambda stays warm
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;book_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db_client&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="n"&gt;book_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;book_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This minimizes connection churn and keeps the database happy. However, for large requests, this all means nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution At Last
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fzsv5e4aq7y79ccn1125l.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fzsv5e4aq7y79ccn1125l.gif" alt="Caterpillar excavator crossing a river using it's arm" width="346" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;RDS Proxy&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In late 2019, AWS announced RDS Proxy, and it became GA (General Availability) in mid-2020, and a game changer.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why it’s your friend:&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;RDS Proxy provides lots of advantages that will help you sleep at night, these include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Manages connection pooling for you&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Works across multiple Lambda instances&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handles auth and failover&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reduces DB memory pressure&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1747722399669%2F51de98d9-5bac-4fba-bab1-982d1350e8a5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1747722399669%2F51de98d9-5bac-4fba-bab1-982d1350e8a5.png" alt="Flow diagram with Lambda, RDS proxy and RDS" width="800" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Caveats:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Adds a small latency (~few ms)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Not free, but worth it&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unfortunately, it doesn’t have support for older RDS engines, and this includes Aurora Serverless V1.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Aurora Serverless V2 to the Rescue&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When Aurora Serverless V2 came out, things improved significantly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It scaled faster than v1.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It had no cold start delay.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Still cost-effective for bursty traffic&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And best of all, it had support for RDS Proxy!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;TL;DR: The Golden Combo&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fw2k06lonm8n1di61bzuw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fw2k06lonm8n1di61bzuw.gif" alt="Chef adding salt to steak" width="480" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s the stack I now recommend, especially for periodic processing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Lambda (use warm connections wisely)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Aurora Serverless V2&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;RDS Proxy&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Retry logic with backoff&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SNS/SQS for decoupling workloads&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Final Thoughts&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;But if you’re sticking with Lambda (and I don’t blame you).&lt;/p&gt;

&lt;p&gt;Just remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Don’t open DB connections inside the handler&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use Aurora Serverless V2 or DynamoDB&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Prefer RDS Proxy for RDS&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Load test your setup&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And don’t trust blog posts too easily, especially this one. Test for your use case.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>rds</category>
      <category>serverless</category>
    </item>
    <item>
      <title>How we reduced our NodeJs app size on AWS Lambda by over 80% 😮</title>
      <dc:creator>Victor Anuebunwa</dc:creator>
      <pubDate>Tue, 07 Sep 2021 08:58:03 +0000</pubDate>
      <link>https://dev.to/avonnadozie/how-we-reduced-our-nodejs-app-size-on-aws-lambda-by-over-80-3pf7</link>
      <guid>https://dev.to/avonnadozie/how-we-reduced-our-nodejs-app-size-on-aws-lambda-by-over-80-3pf7</guid>
      <description>&lt;p&gt;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. &lt;br&gt;
In the end, you'll find out how we dropped our 50MB - 75MB (compressed) node apps to 8MB - 10MB. Awesome, right?&lt;/p&gt;

&lt;p&gt;But first, How did we get here?&lt;/p&gt;

&lt;p&gt;How did this set of software developers exhaust the 75GB AWS Lambda storage limit? &lt;br&gt;
Oh yes, we did. 🙈&lt;/p&gt;
&lt;h2&gt;
  
  
  The Mistake
&lt;/h2&gt;

&lt;p&gt;Deploying microservices on AWS lambda could mean you have to work with other AWS services like SQS, SNS, API Gateway, etc., and using the  &lt;a href="https://www.serverless.com/framework/docs/" rel="noopener noreferrer"&gt;Serverless Framework&lt;/a&gt;, the default practice will be to define handlers for events coming from each of these services.&lt;/p&gt;

&lt;p&gt;Under the hood, the Serverless framework creates new lambda functions for each of the handlers you define.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Here's the interesting part, this was only on our staging environment. &lt;br&gt;
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 🙆🏽‍♂️&lt;/p&gt;
&lt;h2&gt;
  
  
  The Fix: How we reduced our AWS lambda size
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/Rk8CZk8M7UHzG/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/Rk8CZk8M7UHzG/giphy.gif" alt="Tips"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Set AWS-SDK as dev dependency
&lt;/h3&gt;

&lt;p&gt;This is the mother of all. I won't even be bothered if you quit reading after this point.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;aws-sdk&lt;/code&gt; package alone is over 60MB (uncompressed). This is huge!&lt;/p&gt;

&lt;p&gt;This was almost everything about our app size issue, our misfortune and also our miracle. The good news is that the &lt;code&gt;aws-sdk&lt;/code&gt; comes pre-installed in your Lambda runtime, so you don't need to install it again. Only set it as a dev dependency.&lt;/p&gt;

&lt;p&gt;Only if we knew this. I'm in severe pain now 😭&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Remove unnecessary packages
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/1ncT70kECIAC4O83UD/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/1ncT70kECIAC4O83UD/giphy.gif" alt="Review packages"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is a good chance you've done this already. However, for large projects mostly, some unused packages can go undetected easily.&lt;/p&gt;

&lt;p&gt;Try packages like &lt;code&gt;depcheck&lt;/code&gt;. It helps you scan your app for unused and missing dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g depcheck
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-g&lt;/code&gt; flag is important, please. Let's not complicate things here.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Cross-check for dev packages
&lt;/h3&gt;

&lt;p&gt;Just like the &lt;code&gt;aws-sdk&lt;/code&gt;, there are other packages that could disguise as production dependencies such as &lt;code&gt;serverless&lt;/code&gt; and its plugins like &lt;code&gt;serverless-offline&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;These packages, in combination with the extra packages it installs for you, is over 100MB (uncompressed) in size, that is including &lt;code&gt;aws-sdk&lt;/code&gt;. Hence, removing &lt;code&gt;aws-sdk&lt;/code&gt; without removing these guys won't make any difference. It will be re-installed.&lt;/p&gt;

&lt;p&gt;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.&lt;br&gt;
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.&lt;/p&gt;

&lt;p&gt;Created this Docker image &lt;a href="https://hub.docker.com/repository/docker/avonnadozie/serverless" rel="noopener noreferrer"&gt;avonnadozie/serverless&lt;/a&gt; for this purpose, feel free to reuse.&lt;br&gt;
It comes with &lt;code&gt;serverless-offline&lt;/code&gt; plugin and other necessary packages you need to deploy to lambda successfully.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Use &lt;code&gt;--production&lt;/code&gt; flag
&lt;/h3&gt;

&lt;p&gt;This should be a common step as well, however, It doesn't hurt to repeat it.&lt;/p&gt;

&lt;p&gt;Always remember to use the &lt;code&gt;--production&lt;/code&gt; when running npm on production or in your CI/CD script to not install dev dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Reuse available Lambda runtime packages
&lt;/h3&gt;

&lt;p&gt;Just like &lt;code&gt;aws-sdk&lt;/code&gt;, there are other packages such as &lt;code&gt;uuid&lt;/code&gt; and &lt;code&gt;dotenv&lt;/code&gt; that are already available in the lambda runtime which you can reuse.&lt;/p&gt;

&lt;p&gt;You can refer to this &lt;a href="https://log.victoranuebunwa.com/list-of-node-packages-pre-installed-on-aws-lambda-runtime" rel="noopener noreferrer"&gt;list of node packages in lambda runtime&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Inspect deployment
&lt;/h3&gt;

&lt;p&gt;The crude way works well too. Download your live code and inspect it manually.&lt;/p&gt;

&lt;p&gt;To download, go to your Lambda console, click on the function and choose "Export function" from the "Actions" dropdown.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1630064480068%2FvfaAA1irk.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1630064480068%2FvfaAA1irk.png" alt="Export function"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then click on "Download deployment package" afterwards.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1630064489265%2FAAkyr8z7u.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1630064489265%2FAAkyr8z7u.png" alt="Download deployment package for lambda"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the download is complete, go to the &lt;code&gt;node_modules&lt;/code&gt; folder, rank the folders by size and take a look. It reveals a lot.&lt;/p&gt;

&lt;p&gt;This was how we found out &lt;code&gt;aws-sdk&lt;/code&gt; was still installed even after we've set it as a dev dependency.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Move out large non-js files
&lt;/h3&gt;

&lt;p&gt;Consider hosting large files such as images, or JSON on a private CDN (most likely S3) and read it from there.&lt;/p&gt;

&lt;p&gt;This will cause a trade-off in speed but it could be worth it for you depending on the design of your app.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Merge serverless handlers
&lt;/h3&gt;

&lt;p&gt;This is completely up to you and your app architecture, but you can consider reusing lambda functions where necessary to prevent serverless from creating additional and unnecessary functions for you.&lt;/p&gt;

&lt;p&gt;For us, we found a way to merge SQS and SNS handlers given that their event input data and the way they work is similar and it saved us a whole lot of MBs.&lt;/p&gt;

&lt;p&gt;The lambda console provides you with sample event input data on its test tab you can work with. Choose from the list.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1630072857381%2FoxzIZqBAr.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1630072857381%2FoxzIZqBAr.png" alt="Lambda event samples"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits to reducing your application size
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster  &lt;a href="https://aws.amazon.com/blogs/compute/operating-lambda-performance-optimization-part-1/" rel="noopener noreferrer"&gt;cold start&lt;/a&gt;  time&lt;/li&gt;
&lt;li&gt;Faster deployment with CI/CD as the zipping and upload process will be faster&lt;/li&gt;
&lt;li&gt;Less cost on storage and memory&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>node</category>
    </item>
    <item>
      <title>How I survive testing on NodeJs and Jest 🤒</title>
      <dc:creator>Victor Anuebunwa</dc:creator>
      <pubDate>Tue, 31 Aug 2021 15:57:28 +0000</pubDate>
      <link>https://dev.to/avonnadozie/how-i-survive-testing-on-nodejs-and-jest-503i</link>
      <guid>https://dev.to/avonnadozie/how-i-survive-testing-on-nodejs-and-jest-503i</guid>
      <description>&lt;p&gt;Coming from a PHP background and with PHPUnit testing, I started my journey into writing tests on NodeJs with some expectations.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/l3978rwTcbblCh224/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l3978rwTcbblCh224/giphy.gif" alt="Giphy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  PHPUnit Vs Jest
&lt;/h2&gt;

&lt;p&gt;PHPUnit provides you with more test functions to work with, has better error tracing, and is easier to debug.&lt;/p&gt;

&lt;p&gt;However, testing on NodeJs is faster than testing with PHPUnit.&lt;/p&gt;

&lt;p&gt;Correction, testing on NodeJs is &lt;strong&gt;way faster&lt;/strong&gt; 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! 🙌🏽&lt;/p&gt;

&lt;p&gt;This is great, however, working with tests that run in parallel comes with its own challenges.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tips for testing on NodeJs using Jest
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/1n833bZxdzKzaErLe9/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/1n833bZxdzKzaErLe9/giphy.gif" alt="Giphy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Beware of asynchronous access to data
&lt;/h3&gt;

&lt;p&gt;Tests running in parallel means that you will have multiple tests making requests to the database at the same time.&lt;/p&gt;

&lt;p&gt;Expect inconsistency from tests like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Get User Test&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;get user&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&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;/v1/user/1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`Bearer sample-token`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&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="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Delete User Test&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;delete user&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/v1/user/1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`Bearer sample-token`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&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="mi"&gt;200&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;h4&gt;
  
  
  The problem
&lt;/h4&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h4&gt;
  
  
  The solution
&lt;/h4&gt;

&lt;p&gt;Ensure that each test works with its own unique data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Get User Test&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;get user&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="c1"&gt;// Create a new user&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&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="s2"&gt;Sample user 1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="c1"&gt;// Get the user&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&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="s2"&gt;`/v1/user/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`Bearer sample-token`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&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="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Delete User Test&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;delete user&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="c1"&gt;// Create a new user&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&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="s2"&gt;Sample user 2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="c1"&gt;// Delete the user&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/v1/user/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`Bearer sample-token`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&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="mi"&gt;200&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;h3&gt;
  
  
  Always remember your &lt;code&gt;Promises&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Always remember to await functions that return a promise.&lt;/p&gt;

&lt;p&gt;Obvious right? I will bet you still forgot one some minutes ago.&lt;/p&gt;

&lt;p&gt;On a serious note, these kinds of errors in tests can mess up your week and are difficult to detect. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findByPk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// no await&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;user&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toBeNull&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  The problem
&lt;/h4&gt;

&lt;p&gt;This will always be true as it will be testing on the returned &lt;code&gt;Promise&lt;/code&gt; object which will not be null. &lt;/p&gt;

&lt;h4&gt;
  
  
  The solution
&lt;/h4&gt;

&lt;p&gt;Await&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findByPk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// await &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;user&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toBeNull&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Prefer Debugger to &lt;code&gt;console.log&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Debugger adds more flare to error tracing, get used to it.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;console.log&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;Additionally, &lt;code&gt;console.log&lt;/code&gt; codes can easily find their way to production and you find yourself unknowingly logging sensitive information which could be dangerous.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mock calls to external APIs or resources
&lt;/h3&gt;

&lt;p&gt;This is more of a general tip when testing with any framework.&lt;/p&gt;

&lt;p&gt;For most, your test should focus on testing the functions and features of your app, not the functionality or output of an external application.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;It's best practice to mock these resources or API responses instead.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getSignedUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`https://s3.eu-west-2.amazonaws.com/sample/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getSignedUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getObject&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;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Expires&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&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;



</description>
      <category>node</category>
      <category>javascript</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
