<?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: Corey Cleary</title>
    <description>The latest articles on DEV Community by Corey Cleary (@ccleary00).</description>
    <link>https://dev.to/ccleary00</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%2F86669%2F31217482-a6a0-4370-aea5-76d3326e11b9.jpg</url>
      <title>DEV Community: Corey Cleary</title>
      <link>https://dev.to/ccleary00</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ccleary00"/>
    <language>en</language>
    <item>
      <title>Practice System Design interview question walkthrough</title>
      <dc:creator>Corey Cleary</dc:creator>
      <pubDate>Tue, 25 Mar 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/ccleary00/practice-system-design-interview-question-walkthrough-28n0</link>
      <guid>https://dev.to/ccleary00/practice-system-design-interview-question-walkthrough-28n0</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Originally published at &lt;a href="https://www.coreycleary.me/practice-system-design-interview-question-walkthrough" rel="noopener noreferrer"&gt;coreycleary.me&lt;/a&gt;&lt;/strong&gt;. This is a cross-post from my content blog. I publish new content every week or two, and you can &lt;a href="https://www.coreycleary.me/about/" rel="noopener noreferrer"&gt;sign up to my newsletter&lt;/a&gt; if you'd like to receive my articles directly to your inbox! I also regularly send cheatsheets and other freebies.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Tech job interviews are notoriously difficult. You never know what questions they are going to ask you - are they going to ask about obscure language trivia, have you build some UI component while doing a screenshare, ask you some algorithm puzzles that have no relevance to the job day-to-day, or ask you to design an architecture for some theoretical problem?&lt;/p&gt;

&lt;p&gt;There are a million things they could ask you, and unfortunately you don't have much control over that. You can look on Glassdoor to see what the company tends to ask, but even then it's a gamble.&lt;/p&gt;

&lt;p&gt;Personally I'm not a fan of how most technical interviews are done. Trivia questions aren't relevant and even experienced candidates often forget simple things all the time. Screensharing/livecoding some feature is more realistic, but time-consuming and nerve wracking. And Leetcode-style/algorithm puzzle questions you will almost never encounter in the real programming world, not to mention those can require weeks or months of studying - not something I personally have time for.&lt;/p&gt;

&lt;p&gt;The one interview question type that I think is actually relevant are System Design questions. Relevant, because discussing with your future team members different approaches to building out a new feature or system is what you do everyday on the job. Sometimes these interview questions are very formal, where you have a list of requirements, and other times they're more informal, like a back and forth discussion. Something like "we have this current problem we are working through solving, how would you go about it?", and you talk through different options.&lt;/p&gt;

&lt;p&gt;Although those are both different styles - and sometimes you're not designing a whole system but instead a slice of it, like a &lt;em&gt;feature&lt;/em&gt; - for the sake of conciseness I am calling both of them &lt;a href="https://coreycleary.me/the-difference-between-project-structure-vs-vs-design-patterns-vs-architecture" rel="noopener noreferrer"&gt;"System Design"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With the job market being as tough as it is right now, having peace of mind that you have gotten practice in going over these types of questions will go a long way in reducing your pre-interview anxiety.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And if you don't have a ton of experience designing systems/doing architecture work&lt;/strong&gt; - maybe you have a couple of years experience and now you're looking to move to a more Senior-level role - learning how to go about designing a feature or system is extremely useful.&lt;/p&gt;

&lt;p&gt;Maybe you have helped out Architects or Senior developers on your team before, but didn't quite understand why certain decisions were made, or how they even came up with their approach.&lt;/p&gt;

&lt;p&gt;No matter what scenario you find yourself in - the key to both is being exposed to lots of patterns and company architectures. Once you have seen enough, the pattern recognition starts to kick in and you can more easily identify which patterns to apply to which problems. While it's always best to gain this experience in the "real world", there is only so much you can learn at any given company, so being exposed to these patterns is key.&lt;/p&gt;

&lt;p&gt;With enough exposure to different patterns and architectures, you can ace the System Design interview and/or learn enough to move from junior-mid to senior.&lt;/p&gt;

&lt;p&gt;This is a continuous learning process, but a continuous learning process starts with step 1, &lt;strong&gt;which is why I am going to cover a system design interview question I received in the "real world" recently when interviewing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This post will accomplish two main things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;get practice with an interview question (if you are actively applying/interviewing)&lt;/li&gt;
&lt;li&gt;learn a few patterns/approaches you can apply in your work (if you are just trying to level up design/architecture knowledge)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The interview question
&lt;/h2&gt;

&lt;p&gt;I picked this particular question because in addition to it being one I was recently asked in a real interview, it also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;is a common question asked in interviews&lt;/li&gt;
&lt;li&gt;is a problem that allows us to go over a few different intersting achitectural topics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The question asked was &lt;strong&gt;"how would you go about designing a food delivery service like Grubhub/DoorDash?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When I mentioned the formal vs. informal distinction before, this one definitely fits in the informal category. Which has some challenges, because we don't have a clearly defined scope up front. But for purposes of this post it's actually helpful because it presents an opportunity to go over how to define and narrow scope, which we will do now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: narrowing scope and clarifications
&lt;/h2&gt;

&lt;p&gt;The way I broke down this problem is first identifying the core pieces. You get live updates for where your driver is, so there is a real time component needed here. A restaurant needs to manage their "inventory". The service needs to efficiently route drivers.&lt;/p&gt;

&lt;p&gt;Think about how you use an app like Grubhub, and think about how restaurants and drivers might use it. This process will quickly help you realize there are lots of pieces involved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;users must be able to register&lt;/li&gt;
&lt;li&gt;users must be able to set favorites/bookmark restaurants&lt;/li&gt;
&lt;li&gt;users must be able to setup payment options&lt;/li&gt;
&lt;li&gt;users must be able to search for restaurants&lt;/li&gt;
&lt;li&gt;the service must determine which drivers to allocate to a food pickup and delivery&lt;/li&gt;
&lt;li&gt;the service must serve data to the front-end containing restaurant details&lt;/li&gt;
&lt;li&gt;the service must prevent orders from being made before or after a restaurant's opening/closing hours&lt;/li&gt;
&lt;li&gt;the service must allow the user to track their order status and delivery in real-time&lt;/li&gt;
&lt;li&gt;the service must send text or push notifications when food has been picked up, when it's on the way, and when it has been delivered&lt;/li&gt;
&lt;li&gt;restaurants must be able to specify menu items and prices&lt;/li&gt;
&lt;li&gt;restaurants must be able to mark items as "sold out" (either manually or using a quantity available) and the service must not allow a user to order those&lt;/li&gt;
&lt;li&gt;the service must provide directions for the driver - to the restaurant and to the customer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;... and there are probably many more components/features we could think of.&lt;/p&gt;

&lt;p&gt;But think of this a "whittling down" from open and abstract, to something clearer and clearer. It's not necessary to go so deep that you are essentially doing requirements gathering, but enough you can identify the high-level building blocks.&lt;/p&gt;

&lt;p&gt;For purposes of a 30 minute (maybe 1 hour) long conversation though, going over all the above would take way too much time. A lot of these components are important but kind of standard to all apps. Things like registration, user settings, billing/payments, etc. So we can make an assumption those aren't really important to go over for purposes of an interview question.&lt;/p&gt;

&lt;p&gt;But these elements do seem important, because they are relatively unique to a food delivery service when compared to some other app/service:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;del&gt;users must be able to register&lt;/del&gt; (Common to most apps)&lt;/li&gt;
&lt;li&gt;
&lt;del&gt;users must be able to set favorites/bookmark restaurants&lt;/del&gt; (Common to most apps)&lt;/li&gt;
&lt;li&gt;
&lt;del&gt;users must be able to setup payment options&lt;/del&gt; (Common to most apps)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;users must be able to search for restaurants&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;the service must determine which drivers to allocate to a food pickup and delivery&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;del&gt;the service must serve data to the front-end containing restaurant details&lt;/del&gt; (Common to most apps)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;the service must prevent orders from being made before or after a restaurant's opening/closing hours&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;the service must allow the user to track their order status and delivery in real-time&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;the service must send text or push notifications when food has been picked up, when it's on the way, and when it has been delivered&lt;/em&gt; (Likely less important, given we will discuss real-time updates)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;restaurants must be able to specify menu items and prices&lt;/em&gt; (Likely less important)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;restaurants must be able to mark items as "sold out" (either manually or using a quantity available) and the service must not allow a user to order those&lt;/em&gt; (Likely less important, and more of an "implementation detail")&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;the service must provide directions for the driver - to the restaurant and to the customer&lt;/em&gt; (Important but likely too big for current scope)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So in my interview I went over this list (all items) and then asked if the narrowed down scope (everything in bold) was a correct assumption, and they confirmed. Not only is this useful for limiting the discussion, but it also shows the interviewer that you have enough experience to even &lt;em&gt;think&lt;/em&gt; about all the pieces potentially involved, even though you won't be discussing all of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coming up with a design
&lt;/h2&gt;

&lt;p&gt;Now we start on the design work. In my interview I didn't go through each component in a specific order, unless they were related to each other.&lt;/p&gt;

&lt;p&gt;For purposes of this post, the real-time update component is an interesting one to think through so let's start with that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature - the service must allow the user to track their order status and delivery in real-time
&lt;/h3&gt;

&lt;p&gt;Either WebSockets or SSE (Server-Sent Events) are viable options for handling the real-time aspect of updating the user on their order and delivery status.&lt;/p&gt;

&lt;p&gt;A discussion comparing and contrasting these two technologies/patterns is an entire post by itself, but generally speaking here is a comparison:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WebSockets are &lt;strong&gt;bi-directional&lt;/strong&gt;, meaning both client and server can communicate with each other. Whereas SSE is &lt;strong&gt;uni-directional&lt;/strong&gt; - only the server can send data to the client.&lt;/li&gt;
&lt;li&gt;WebSockets are low latency, because a connection stays open and there is no additional overhead required to re-open connections. SSE connections do automatically reconnect, however.&lt;/li&gt;
&lt;li&gt;WebSockets can maintain millions of connections, but they need stateful scaling. The server needs to keep track of the connection state of each client connected to it. SSE on the other hand, since it uses HTTP which is stateless, can scale more easily.&lt;/li&gt;
&lt;li&gt;For load balancing requests, WebSockets require a proxy that supports WebSockets, whereas SSE's since they just use HTTP, and load balancer will work.&lt;/li&gt;
&lt;li&gt;Some ISP's will block WebSocket connections, so some users may not be able to connect depending on their ISP's rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With that in mind, you could use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WebSockets:

&lt;ul&gt;
&lt;li&gt;for the bi-directional capability of a driver being able to send updates to the server, and the server pushing those updates to the user&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;SSE:

&lt;ul&gt;
&lt;li&gt;you want to account for users having bad network connections where it drops frequently (SSE's will auto-reconnect)&lt;/li&gt;
&lt;li&gt;the server is the only one that needs to push updates (updates to the user like "Preparing food", "Out for delivery", "Delivered")&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;In an interview situation, if you have two potential options or solutions, you don't have to necessarily propose just one as the "right" option. For example, you can mention the above two options and discuss tradeoffs between the two. This is actually a good thing to do because most of the job day-to-day is thinking through and discussing tradeoffs.&lt;/p&gt;

&lt;p&gt;In our case, you could probably argue for either option. The bi-directionality of WebSockets is likely to be useful, but that comes with more infrastructural and configuarion costs. On the other hand, if you don't need that SSE's are more "lightweight" and you have less to think about when it comes to scaling and handling dropped connections.&lt;/p&gt;

&lt;p&gt;One last thing - in theory you could also use short polling, which is where the client makes HTTP requests for status updates every few seconds. The positives of this approach are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the implementation is only on the app/client code, instead of &lt;em&gt;also&lt;/em&gt; needing server code to support this&lt;/li&gt;
&lt;li&gt;no connection management to keep track of&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, this approach is inefficient for a service and app that works at scale, because now you will have many more requests made to your REST API servers handling these update requests (let's say one request every 5 seconds, which is 12 req/min, times potentially hundreds of thousand of clients making these requests). Short polling is also not real-time, which may not matter in the food delivery case because a user doesn't need &lt;em&gt;precisely&lt;/em&gt; real-time. But because of this inefficiency of putting more load on the servers, WebSockets or SSE are a better option.&lt;/p&gt;

&lt;h4&gt;
  
  
  Interviewer follow-up question
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;Let's say that you did choose WebSockets. How would you handle reconnecting disconnected clients - OR - if their connection is completely blocked, as by their ISP?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I got this question in my interview, and my answer was that while theire WebSocket connection reconnects you could use a fallback polling until the WebSocket becomes reconnected. The tradeoff there is that you then sacrifice some REST API server efficiency (as we went over in the short polling section just above), but that you then have a good fallback experience for the user, so that they can still receive updates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature - users must be able to search for restaurants
&lt;/h3&gt;

&lt;p&gt;A user is going to want to be able to search by restaurant name, restaurant type, food item, by location, etc.&lt;/p&gt;

&lt;p&gt;To support this feature, there are many options out there but Elasticsearch or PostgreSQL "Full Text Search" are good options. Similar to WebSockets vs. SSE, there are tradeoffs with both approaches and neither is the "correct" choice. But there are some clear use cases for when you would want to use one over the other.&lt;/p&gt;

&lt;p&gt;Both Postgres and Elasticsearch are very commonly used technologies, so with either choice you won't have to worry about finding developers with lack of knowledge in either or a poorly maintained solution (no development, no bugfixes, etc). While Elasticsearch is a dedicated search solution, Postgres also has search functionality built-in (despite being a dedicated database solution).&lt;/p&gt;

&lt;p&gt;Postgres Full Text Search (referred to as "FTS" going forward) gives us some of the features of a search engine, and which we can make use of in designing the restaurant and search feature of the food delivery service. The topic of Postgres FTS could be covered in a post by itself but at a high level, here are the benefits this feature provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic search by terms&lt;/li&gt;
&lt;li&gt;Ranking support&lt;/li&gt;
&lt;li&gt;You don't need an additional technology you have to add to your infrastructure and then maintain if you are already using Postgres&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The drawbacks are that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It doesn't support typo correction&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-fuzzy-query.html" rel="noopener noreferrer"&gt;Fuzzy searching&lt;/a&gt; is not as robust as it is with Elasticsearch&lt;/li&gt;
&lt;li&gt;It doesn't support more robust searches (like by location, ratings, delivery time, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Elasticsearch, on the other hand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requires additional infrastructure to be added to your stack and maintained&lt;/li&gt;
&lt;li&gt;Supports handling typos&lt;/li&gt;
&lt;li&gt;Supports fuzzy searches/matches&lt;/li&gt;
&lt;li&gt;Supports more advanced searching (like by location, ratings, delivery time, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As with the real-time feature, we again come to tradeoffs. In this case you can propose either. I lean towards Elasticsearch because a user is going to want to search by restaurant name, food, location and get those results back sorted/ranked by restaurant rating, delivery time, etc. Elasticsearch just supports this better than Postgres FTS, which you can do a lot with but is still more basic.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NOTE: Look for a post on Postgres FTS coming in the future!&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Interviewer follow-up question
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;Is there a way you could take a hybrid approach and use both?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One approach would be to start with Postgres FTS and if you run into scaling issues or find there are features you need to add that it doesn't support very well, you can migrate to Elasticsearch. Another approach would be to use FTS for some search categories that are more basic (like a restaurant manager searching for their own menu items to update via their management dashboard), and Elasticsearch for other that are more complex (like user searches).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How would Elasticsearch ingest restaurant data?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The restaurant details - location, hours, menu items, prices, etc - are likely going to be stored in the database via some management/dashboard UI the restaurant uses to set these details. Everytime there is an update, Elasticsearch could then ingest the new data and store it in a format that is designed for searchability.&lt;/p&gt;

&lt;p&gt;This conveniently skips over how thinking through how Elasticsearch would know there is an update - for example, would this be accomplished via a cron job? Is there something like a webhook or emitted event from the database that lets Elasticsearch know to ingest the new data? Because this post is already getting kind of long I am going to skip over the details on that, but this is a good practice to answer in more detail on your own if you are preparing for an interview.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature - the service must determine which drivers to allocate to a food pickup and delivery
&lt;/h3&gt;

&lt;p&gt;In order to determine which drivers to allocate to pickup and delivery a customer order, we need to know three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pickup address (restaurant location)&lt;/li&gt;
&lt;li&gt;driver's current location&lt;/li&gt;
&lt;li&gt;delivery address&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And then the selection algorithm would need to essentially triangulate between those three. &lt;/p&gt;

&lt;p&gt;Because this is a System Design question and not a coding/algorithm question, you probably won't be asked what that algorithm would be. But figuring out how to &lt;em&gt;store&lt;/em&gt; those pieces of information is relevant to designing a system and, thus, worth talking through.&lt;/p&gt;

&lt;p&gt;Fortunately, the pickup and delivery addresses are static, and input by the restaurant and by the custom. So we only need to store those in a &lt;code&gt;restaurants&lt;/code&gt; table and a &lt;code&gt;customers&lt;/code&gt; table (or have some foreign key relationship to an addresses table).&lt;/p&gt;

&lt;p&gt;But figuring out and storing the driver's location is more challenging, because of the dynamic nature of their coordinates. Because they are pretty much always driving, their location is going to be always changing.&lt;/p&gt;

&lt;p&gt;A potential solution to this is &lt;strong&gt;Geohashing&lt;/strong&gt;. Using this technique, you &lt;em&gt;spatially index&lt;/em&gt; coordinates of drivers by encoding the latitude and longitude coordinates into a hash. &lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Location&lt;/th&gt;
&lt;th&gt;Latitude&lt;/th&gt;
&lt;th&gt;Longitude&lt;/th&gt;
&lt;th&gt;Geohash (Precision 5)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Restaurant 1&lt;/td&gt;
&lt;td&gt;40.7128&lt;/td&gt;
&lt;td&gt;-74.0060&lt;/td&gt;
&lt;td&gt;dr5r7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Driver 1&lt;/td&gt;
&lt;td&gt;40.7139&lt;/td&gt;
&lt;td&gt;-74.0075&lt;/td&gt;
&lt;td&gt;dr5r7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Driver 2&lt;/td&gt;
&lt;td&gt;40.7291&lt;/td&gt;
&lt;td&gt;-73.9995&lt;/td&gt;
&lt;td&gt;dr5r8&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In this example, because the Geohash of Driver 1 and Restaurant 1 match, we know that Driver 1 is closer to the restaurant than Driver 2.&lt;/p&gt;

&lt;p&gt;I mentioned above that we already have the restaurant's and customer's addresses. We would need to take those, get the latitude/longitude coordinates (likely using some lookup service), and then store as a Geohash. This would likely happen asynchronously using some &lt;a href="https://coreycleary.me/understand-how-to-approach-designing-queues-in-node" rel="noopener noreferrer"&gt;queueing system&lt;/a&gt; to create a job for this work, since it doesn't need to happen immediately. With each of the three data points we need as a Geohash, we can then triangulate.&lt;/p&gt;

&lt;p&gt;We can either store these hashes in a database, or we can use &lt;a href="https://redis.io/docs/latest/develop/data-types/geospatial" rel="noopener noreferrer"&gt;Redis' Geohashing&lt;/a&gt; feature to accomplish the storage piece of this. Redis handles the hashing part of it for you, and provides a nice &lt;code&gt;GEOSEARCH&lt;/code&gt; feature that lets you search by a radius. Too much to go into in this post, but you can checkout the link above to learn more about it.&lt;/p&gt;

&lt;p&gt;You may be wondering - &lt;em&gt;because the driver's location is dynamic and always updating, how do we handle updating it?&lt;/em&gt; The Geohash would therefore also always be changing.&lt;/p&gt;

&lt;p&gt;There are a couple approaches to this we could take. One would be for the driver's version of the delivery app to update their location every 30 seconds or so (or whatever timeframe would not overwhelm or Geohash storage solution). Another would be for the driver's app to track when it's gone more than X miles since the last Geohash update, or to even detect when it's sitting idle and to not send updates then. Ultimately, this logic best lives on the client.&lt;/p&gt;

&lt;p&gt;And the client needs to make sure it's not overwhelming the server with updates. Fairly easily accomplishable.&lt;/p&gt;

&lt;p&gt;This is another potential use case for WebSockets - in this case the driver app would open a WebSocket connection to our delivery service and pipe updates. This would be closer to "real-time" than the suggestions above, but then again, we get back to tradeoffs. How &lt;em&gt;precisely&lt;/em&gt; real-time does this need to be, and is the increased load handling of hundreds of thousands (or however many drivers are active) worth it?&lt;/p&gt;

&lt;h4&gt;
  
  
  Interviewer follow-up question
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;What if a driver is not active or offline? How can we be sure they aren't selected for a pickup/delivery?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Rather than go over this, I'm going to leave this as a good practice exercise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature - the service must prevent orders from being made before or after a restaurant's opening/closing hours
&lt;/h3&gt;

&lt;p&gt;While in the context of a System Design interview, this leans more towards the "implementation detail"/programming side of things, I think in the context of a food delivery service it's a fairly important part of the system. In my interview I briefly discussed this with the interviewers, so I'm going to go over that here.&lt;/p&gt;

&lt;p&gt;An approach you could take here is to store each restaurant’s operating hours in a table, and then for each order validate it against the stored hours before confirming the order. Because this table would be hit often, it's best to index it, most likely on the &lt;code&gt;hours&lt;/code&gt; column, in order to not slow down performance.&lt;/p&gt;

&lt;p&gt;The REST endoint for placing an order would:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;get the opening/closing hours of the restaurant from the table&lt;/li&gt;
&lt;li&gt;check if the time the order was placed is greater than the closing hours (or less than the opening hour)&lt;/li&gt;
&lt;li&gt;allow if not, reject if so&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the front-end you would do this same check, and ideally you would already have the restaurant's operating hours fetched so you don't need to make an additional server-side call. But you still need this validation on the back-end too, as someone could be hitting the endpoint directly rather than through the front-end.&lt;/p&gt;

&lt;p&gt;And alternative to checking the database would be to store the restaurant hours in a cache, that way you reduce the load on your database (which is going to be serving up lots of other query responses).&lt;/p&gt;

&lt;h4&gt;
  
  
  Interviewer follow-up question
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;What if you had multiple orders placed around the same time for an item that is out of stock, or had one item remaining? How would you handle that&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm going to leave this as a practice question you can work through yourself!&lt;/p&gt;

&lt;h2&gt;
  
  
  Remaining things we didn't cover
&lt;/h2&gt;

&lt;p&gt;Other things you could be asked about are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scaling/load balancing&lt;/li&gt;
&lt;li&gt;infrastructure setup&lt;/li&gt;
&lt;li&gt;security&lt;/li&gt;
&lt;li&gt;caching

&lt;ul&gt;
&lt;li&gt;what are things you would cache?&lt;/li&gt;
&lt;li&gt;what are things you wouldn't?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;These things tend to come up in interviews but are fairly standard across all architectures, so when you feel comfortable discussing approaches for handling for one particular service/company, you should feel comfortable applying it to others.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;If you have a technical interview coming up, use the scope defined in this post to practice how &lt;em&gt;you&lt;/em&gt; would go about designing it, using the patterns and approaches I discussed, and/or come up with your own design.&lt;/p&gt;

&lt;p&gt;Instead, if you are looking to learn more about architecture, use the patterns and design discussed to apply to the next project you are working on or as a springboard for learning more about any of the given topics!&lt;/p&gt;

&lt;p&gt;There isn’t much content out there that dives into &lt;strong&gt;advanced topics for Node.js developers&lt;/strong&gt; or for &lt;strong&gt;front-end developers transitioning to back-end development&lt;/strong&gt;, which is popular given that front-end devs already know JavaScript. Most resources focus on beginner-level material or generic concepts but leave a gap for those looking to advance their knowledge and/or career.&lt;/p&gt;

&lt;p&gt;This post is part of my effort to fill that gap. While it’s not yet part of a formal series, I plan to write more articles like this one in the future, because architecture concepts are part of that "advanced" knowledge.&lt;/p&gt;

&lt;p&gt;If you’ve ever felt the pain of not having enough material to guide you through these advanced topics, you’re not alone. My goal is to provide actionable insights and examples that you can apply directly to your work or interview prep.&lt;/p&gt;

&lt;p&gt;Stay tuned for more posts like this, and if you’re interested in learning about these topics as I publish them, here's that &lt;a href="https://www.coreycleary.me/about" rel="noopener noreferrer"&gt;signup link&lt;/a&gt; again!&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>interview</category>
    </item>
    <item>
      <title>Testing REST endpoints with fake HTTP statuses</title>
      <dc:creator>Corey Cleary</dc:creator>
      <pubDate>Tue, 18 Jun 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/ccleary00/testing-rest-endpoints-with-fake-http-statuses-5dp8</link>
      <guid>https://dev.to/ccleary00/testing-rest-endpoints-with-fake-http-statuses-5dp8</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Originally published at &lt;a href="https://coreycleary.me/testing-rest-endpoints-with-fake-http-statuses" rel="noopener noreferrer"&gt;coreycleary.me&lt;/a&gt;&lt;/strong&gt;. This is a cross-post from my content blog. I publish new content every week or two, and you can &lt;a href="https://www.coreycleary.me/about/" rel="noopener noreferrer"&gt;sign up to my newsletter&lt;/a&gt; if you'd like to receive my articles directly to your inbox! I also regularly send cheatsheets and other freebies.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Sometimes when developing a service that calls an external REST API or testing calling an endpoint from a front-end, you'll need to test what happens when that API returns different HTTP status codes. You can do this using tests, and mock/stub a &lt;code&gt;404&lt;/code&gt;, &lt;code&gt;500&lt;/code&gt;, etc. But often it's very helpful to &lt;em&gt;see&lt;/em&gt; what happens when a specific response status is returned.&lt;/p&gt;

&lt;p&gt;Or imagine your QA team needs to manually test what happens when a &lt;code&gt;500&lt;/code&gt; is returned. For example, they need to confirm an error page shows if the app receives an error response. In that scenario you can't (purely) rely on stubbed automated tests.&lt;/p&gt;

&lt;p&gt;Anytime you have to stub responses, that setup can be tricky and can slow you down. You could go an alternative route and setup your own server that returns &lt;code&gt;404&lt;/code&gt;, &lt;code&gt;401&lt;/code&gt;, &lt;code&gt;500&lt;/code&gt;, etc. And then you could manually swap out the endpoints that get called to point to your "stubbed" service, or using some complicated proxy setup to forward the requests. But that also takes time, adds complexity, and will slow you down. &lt;/p&gt;

&lt;p&gt;Rather than having to spin up your own server or only handle this via tests, you can use free 3rd party services that do this for you! This will make seeing how your code handles different HTTP response statuses, with scenarios like the below much easier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node (or any language) REST API -&amp;gt; external endpoint&lt;/li&gt;
&lt;li&gt;UI/Front-end -&amp;gt; REST API&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using a 3rd party service
&lt;/h2&gt;

&lt;p&gt;One of the best tools I've found for testing out different responses from a service is &lt;a href="https://httpstat.us/" rel="noopener noreferrer"&gt;httpstat.us&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This service will let you call pretty much any standard HTTP response status as an endpoint, and get that response code returned. For example, calling &lt;code&gt;https://httpstat.us/503&lt;/code&gt; (with GET/POST/etc) will return a &lt;code&gt;503 Service Unavailable&lt;/code&gt; response.&lt;/p&gt;

&lt;p&gt;I've used this for testing that a generic "hit an error" page/component gets triggered when the app hits any 500-level errors in an HTTP interceptor. This is an especially good use case for QA teams, because if they are testing this scenario, and you don't want to bring down your server in a dev/UAT environment they can just swap out your server endpoint for something like &lt;code&gt;https://httpstat.us/500&lt;/code&gt; and test that way (ideally the URL is already defined as an environment variable to avoid a code change).&lt;/p&gt;

&lt;p&gt;You can also simulate slowness/latency by adding a &lt;code&gt;sleep&lt;/code&gt; query param like &lt;code&gt;https://httpstat.us/503?sleep=5000&lt;/code&gt;. This is very useful for testing how the UI behaves when having to wait for a slow endpoint. For example, you test using this delay and realize that one component is blocking loading of data for several other components until the first (slow endpoint) response is received.&lt;/p&gt;

&lt;p&gt;Lastly, I also use this when I'm working on a frontend feature and the backend endpoint isn't ready yet, and I don't need a response body, just the status.&lt;/p&gt;

&lt;p&gt;For POST/PATCH/PUT requests you can send data in the request body, but it will just be ignored. Be careful with this, because although they claim they don't log any data it's hard to verify that and know for sure. So just don't send any sensitive customer data or anything like that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using this service on the backend
&lt;/h2&gt;

&lt;p&gt;I went over two use cases on the frontend above, now will go over a use case for using &lt;code&gt;https://httpstat.us/&lt;/code&gt; on the backend.&lt;/p&gt;

&lt;p&gt;Often your backend service will need to communicate with external third party services via REST, and this is where this mocked service can come in handy. Because while you will likely have some integration or end-to-end tests for your backend service that test how it handles receiving different HTTP responses, you will also likely want to "smoke" test this manually.&lt;/p&gt;

&lt;p&gt;So if I'm calling the Google Maps API as part of my backend service, I could swap the URL out for &lt;code&gt;https://httpstat.us/&lt;/code&gt; with whatever HTTP response code I want to test and confirm &lt;em&gt;my&lt;/em&gt; code behaves as expected, does error handling properly, etc.&lt;/p&gt;

&lt;p&gt;Similarly, let's say there is another team within the company that is working on an endpoint that is not quite ready yet, but all I need is a &lt;code&gt;200&lt;/code&gt; or &lt;code&gt;201&lt;/code&gt; response code and not the resposne body. Instead of holding up my development work waiting for them, I can swap it out with the mocked service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;There are probably several tools that do what &lt;a href="https://httpstat.us/" rel="noopener noreferrer"&gt;httpstat.us&lt;/a&gt; does, but I've found it works well and has most if not all HTTP response codes covered.&lt;/p&gt;

&lt;p&gt;To summarize, I use it for the following scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For QA testing when they need to test something that is hard to simulate (like the server being down in a dev or UAT environment)&lt;/li&gt;
&lt;li&gt;When I'm working on a FE feature and the BE endpoint isn't ready yet, or when the BE service relies on another service that isn't ready yet&lt;/li&gt;
&lt;li&gt;For testing how the FE (or BE) handles a slow endpoint&lt;/li&gt;
&lt;li&gt;When I'm working on a BE feature and want to &lt;em&gt;manually&lt;/em&gt; test how it handles different response statuses, as opposed to it being handled through integration tests&lt;/li&gt;
&lt;li&gt;When I want to test how the BE handles different response statuses from a third party service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next time you run into one of the scenarios above, consider using a tool like &lt;a href="https://httpstat.us/" rel="noopener noreferrer"&gt;httpstat.us&lt;/a&gt; to assist with your development work and/or testing!&lt;/p&gt;

&lt;p&gt;Love JavaScript but still getting tripped up by unit testing, architecture, etc? I publish articles on JavaScript and Node every 1-2 weeks, so if you want to receive all new articles directly to your inbox, &lt;a href="https://www.coreycleary.me/about/" rel="noopener noreferrer"&gt;here's that link again&lt;/a&gt; to subscribe to my newsletter!&lt;/p&gt;

</description>
      <category>testing</category>
      <category>node</category>
    </item>
    <item>
      <title>Learning a codebase by reading tests</title>
      <dc:creator>Corey Cleary</dc:creator>
      <pubDate>Mon, 15 Apr 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/ccleary00/learning-a-codebase-by-reading-tests-2mnp</link>
      <guid>https://dev.to/ccleary00/learning-a-codebase-by-reading-tests-2mnp</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Originally published at &lt;a href="https://coreycleary.me/learning-a-codebase-by-reading-tests"&gt;coreycleary.me&lt;/a&gt;&lt;/strong&gt;. This is a cross-post from my content blog. I publish new content every week or two, and you can &lt;a href="https://www.coreycleary.me/about/"&gt;sign up to my newsletter&lt;/a&gt; if you'd like to receive my articles directly to your inbox! I also regularly send cheatsheets and other freebies.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Getting up-to-speed on a new codebase - whether you're diving into an open source library or you just joined a new company and you're learning their code - is always a bit daunting... Where do you start? What file do you open first? What is the code even doing?&lt;/p&gt;

&lt;p&gt;There are lots of ways to go about this, but one of the best ways I've found and how I tackle learning any new project is by reading the tests that accompany the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advantages of learning via this approach
&lt;/h2&gt;

&lt;p&gt;Documentation is the usual place most developers will start when learning a codebase. This is definitely not a bad place to start, and it can help you to get a high-level understanding. But there are a few advantages of reading tests as "documentation".&lt;/p&gt;

&lt;p&gt;Often documentation will become out-of-date, but unit tests generally won't. Since tests are (hopefully) run as part a CI build and deployment, if a function changes and breaks the test the CI build will fail. Which means tests have to be more up-to-date than documentation, which is static.&lt;/p&gt;

&lt;p&gt;Another major advantage is that tests are about inputs and outputs, usually more written from an "outcome" perspective. Most modern test frameworks have you write a test with an expected input(s), pass those to whatever it is that's being exercised/tested, and confirm an expected output. Reading through tests like this can help you understand how data is passed around, gets modified, passed to other functions, etc.&lt;/p&gt;

&lt;p&gt;And 90% of the time when you're trying to understand a codebase, you're trying to understand what a given function expects and what it either returns or what action(s) it takes. Tests will help you understand that.&lt;/p&gt;

&lt;p&gt;Similar to how "Test-driven development" is about writing the test to write the code, this is about reading the test to &lt;em&gt;understand the code&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting with higher-level tests
&lt;/h2&gt;

&lt;p&gt;Usually the way I will do this is start with reading the end-to-end and integration tests (if they exist, ideally both do), since they are written to test things at a more "high level" and less granular than unit tests. These tests will exercise a larger amount of code paths, and are usually written in more "business requirements" or "feature requirements" language. This helps you understand how the module or REST API/service is used/consumed, and get a sense of what each feature does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading unit tests
&lt;/h2&gt;

&lt;p&gt;Next I will go a level deeper/more granular and start reading the unit tests. For these tests, a great way to go through these is to have the test file and source file open side-by-side in your IDE.&lt;/p&gt;

&lt;p&gt;Generally but not always, unit tests will be grouped by function or at least more correlated to a couple functions (in contrast to integration and end-to-end tests which will hit many functions and harder to trace back to a specific part of the code). This makes having the source file open side-by-side with the tests easier to start getting an understanding of what the code is doing. You can see expected inputs, follow those through the function, see how they're used or manipulated, then see what the expected output should be.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test files
&lt;/h2&gt;

&lt;p&gt;Usually codebases will have a test file name that corresponds to a source file, something like &lt;code&gt;books.service.js&lt;/code&gt; and &lt;code&gt;books.service.test.js&lt;/code&gt;. But in the case of integration or end-to-end tests, they most likely won't. This makes locating the unit test that goes along with a given source file easy to find and open up next to your source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Next time you need to get up to speed on a new codebase, try the method discussed above. It's always helped me learn much more quickly and given me a "roadmap" of where/what to start focusing on.&lt;/p&gt;

&lt;p&gt;Love JavaScript but still getting tripped up by unit testing, architecture, etc? I publish articles on JavaScript and Node every 1-2 weeks, so if you want to receive all new articles directly to your inbox, &lt;a href="https://www.coreycleary.me/about/"&gt;here's that link again&lt;/a&gt; to subscribe to my newsletter!&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Making test stub setup effortless using Sinon</title>
      <dc:creator>Corey Cleary</dc:creator>
      <pubDate>Fri, 03 Feb 2023 02:10:38 +0000</pubDate>
      <link>https://dev.to/ccleary00/making-test-stub-setup-effortless-using-sinon-nb1</link>
      <guid>https://dev.to/ccleary00/making-test-stub-setup-effortless-using-sinon-nb1</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Originally published at &lt;a href="https://www.coreycleary.me/making-test-stub-fake-setup-effortless-using-sinon"&gt;coreycleary.me&lt;/a&gt;&lt;/strong&gt;. This is a cross-post from my content blog. I publish new content every week or two, and you can &lt;a href="https://www.coreycleary.me/about/"&gt;sign up to my newsletter&lt;/a&gt; if you'd like to receive my articles directly to your inbox! I also regularly send cheatsheets and other freebies.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When you're writing tests, it's often the general setup like creating fake data and setting up stubs/mocks/fakes that can hold you back. Sometimes to the point where you end up skipping writing the tests altogether...&lt;/p&gt;

&lt;p&gt;This setup can feel tedious and creates more work to do on top of having to write the rest of the test, come up with the assertion, and make sure you're covering reasonable test scenarios.&lt;/p&gt;

&lt;p&gt;But by using the testing library Sinon - and having a few real-world/practical examples - test setup can become effortless.&lt;/p&gt;

&lt;p&gt;I'm going to go over one of those examples in this post to help explain how to write stubs/fakes so you can quickly write your tests and get back to writing application code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The unit under test
&lt;/h2&gt;

&lt;p&gt;The unit under test here - the &lt;code&gt;getBinarySliceFromFile()&lt;/code&gt; function - returns a slice of a binary file based on positional arguments.&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getBinarySliceFromFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileName&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;fileBinary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`./binaries/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileName&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fileBinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subarray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;readFileSync()&lt;/code&gt; returns the file in binary/Buffer format.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;start&lt;/code&gt; here is the starting point to slice the file, so you could slice from any arbitrary point&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;offset&lt;/code&gt; is sort of like the end point to slice, but rather than an explicit end, it's used along with &lt;code&gt;start&lt;/code&gt; to define the end&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The issue is that in this function it's reading from the file system via the Node &lt;code&gt;fs&lt;/code&gt; module. We want to test the output of this function, so we need to be able to control what &lt;code&gt;fileBinary&lt;/code&gt; is. And we can't hardcode the directory path + filename.&lt;/p&gt;

&lt;p&gt;To do this, we can stub out / fake the &lt;code&gt;fs&lt;/code&gt; module. This will allow us to set specifically what to return from the &lt;code&gt;fs.readFileSync()&lt;/code&gt; function, which is what we want.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getBinarySliceFromFile()&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;afterEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sinon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;restore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return a slice of the binary starting from an index point&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="c1"&gt;// fake a binary&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fakedFileBinary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// output of the below is &amp;lt;Buffer 81 0b&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;fakedFileBinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeUInt16LE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2945&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// create the Sinon fake, so that it returns the fake binary above&lt;/span&gt;
    &lt;span class="nx"&gt;sinon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;readFileSync&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sinon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fakedFileBinary&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;slice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getBinarySliceFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;offset&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;slice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;equal&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;// notice that the below is the last block in &amp;lt;Buffer 81 0b&amp;gt;, which proves the "slicing" works&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;slice&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mh"&gt;0x0b&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fake setup in the code above happens here:&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;// fake a binary&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fakedFileBinary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// output of the below is &amp;lt;Buffer 81 0b&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;fakedFileBinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeUInt16LE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2945&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// create the Sinon fake, so that it returns the fake binary above&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;readFileSyncFake&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sinon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fakedFileBinary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;sinon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;readFileSync&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;readFileSyncFake&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the first two lines, it's nothing Sinon-specific but just assigning a Buffer (which is binary data) to a variable and then writing data to that Buffer.&lt;/p&gt;

&lt;p&gt;The next part is where Sinon comes into play:&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;readFileSyncFake&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sinon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fakedFileBinary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;sinon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;readFileSync&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;readFileSyncFake&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is using the Sinon &lt;a href="https://sinonjs.org/releases/latest/fakes/"&gt;fakes&lt;/a&gt; API, which are similar to stubs and likely what you are more familiar with hearing. According to the Sinon docs, "Fakes are alternatives to the Stubs and Spies, and they can fully replace all such use cases."&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sinon.fake.returns&lt;/code&gt; lets us setup the fake using the simulation of the file binary. And the &lt;code&gt;.replace()&lt;/code&gt; function lets us specify the &lt;code&gt;fs&lt;/code&gt; method to "overwrite" - in this case &lt;code&gt;readFileSync&lt;/code&gt; - and instead return the fake.&lt;/p&gt;

&lt;p&gt;That code is pretty simple to setup but very powerful. Now we don't have to modify the function definition for &lt;code&gt;getBinarySliceFromFile()&lt;/code&gt; and can instead call it as usual. When it gets called, when it comes time to hit the &lt;code&gt;fs&lt;/code&gt; code Sinon will have swapped that out with the fake. And you can get an output from &lt;code&gt;getBinarySliceFromFile()&lt;/code&gt; to then make assertions on. A pretty effortless way of doing it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test cleanup
&lt;/h3&gt;

&lt;p&gt;You may have noticed there is an &lt;code&gt;afterEach()&lt;/code&gt; block defined at the beginning of the test suite:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;afterEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sinon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;restore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Doing this will reset Sinon and the defined fakes after each test, which is a good practice for test "cleanliness"  so you're starting from a clean slate with each test and there aren't leftover fakes from previous tests that never got reset.&lt;/p&gt;

&lt;p&gt;Note that fakes are available in Sinon with version 5 and up. If you are using a version below that I would recommend upgrading, but if you can't do that you can use the &lt;a href="https://sinonjs.org/releases/latest/stubs/"&gt;stub API&lt;/a&gt; that Sinon provides, and is very similar.&lt;/p&gt;

&lt;h2&gt;
  
  
  This vs. passing in as an argument
&lt;/h2&gt;

&lt;p&gt;One thing we could have done and avoided Sinon and fakes entirely would have been to pass the file binary in as an argument to the function, like so &lt;code&gt;getBinarySliceFromFile(start, offset, fileBinary)&lt;/code&gt; (note the last arg was changed from the file name to the file binary). If we did that it would let us pass in a simulated binary (just like how we set it up using a Buffer above) in the tests rather than using Sinon.&lt;/p&gt;

&lt;p&gt;This can be a valid way, but I don't think it's great to do that here. Any code that calls &lt;code&gt;getBinarySliceFromFile()&lt;/code&gt; will now have to call &lt;code&gt;fs.readFileSync()&lt;/code&gt; and pass the result of that to &lt;code&gt;getBinarySliceFromFile()&lt;/code&gt;, which starts to break encapsulation. Whereas if we keep &lt;code&gt;fs.readFileSync()&lt;/code&gt; in the function body, consumers of &lt;code&gt;getBinarySliceFromFile()&lt;/code&gt; only need to pass in a file name, and that function is responsible for fetching the file binary. This is better encapsulation in my opinion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The next time writing tests is holding you back because either you don't know how to setup fakes/stubs, or the setup is just too tedious, use Sinon and the example test code above to help guide you and make it very easy to get those tests written!&lt;/p&gt;

&lt;p&gt;And if you're feeling like you don't know where to even begin with real testing examples and patterns, the below posts will help you with that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.coreycleary.me/using-spies-as-a-way-to-test-side-effects-in-node"&gt;Using spies as a way to test side-effects in Node&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.coreycleary.me/know-what-to-test-using-these-recipes-node-service-that-calls-a-database"&gt;Know what to test using these recipes: Node service that calls a database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.coreycleary.me/real-world-testing-recipes-node-service-that-calls-an-external-api"&gt;Real world testing recipes: Node service that calls an external API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.coreycleary.me/real-world-testing-using-business-and-technical-requirements-to-know-what-to-test"&gt;Real world testing: Using business and technical requirements to know what to test&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Love JavaScript but still getting tripped up by unit testing, architecture, etc? I publish articles on JavaScript and Node every 1-2 weeks, so if you want to receive all new articles directly to your inbox, &lt;a href="https://www.coreycleary.me/about/"&gt;here's that link again&lt;/a&gt; to subscribe to my newsletter!&lt;/p&gt;

</description>
      <category>node</category>
      <category>testing</category>
    </item>
    <item>
      <title>In what layer does request validation go in a Node REST API?</title>
      <dc:creator>Corey Cleary</dc:creator>
      <pubDate>Thu, 12 Jan 2023 21:05:10 +0000</pubDate>
      <link>https://dev.to/ccleary00/in-what-layer-does-request-validation-go-in-a-node-rest-api-4eli</link>
      <guid>https://dev.to/ccleary00/in-what-layer-does-request-validation-go-in-a-node-rest-api-4eli</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Originally published at &lt;a href="https://www.coreycleary.me/in-what-layer-does-request-validation-go-in-a-node-rest-api" rel="noopener noreferrer"&gt;coreycleary.me&lt;/a&gt;&lt;/strong&gt;. This is a cross-post from my content blog. I publish new content every week or two, and you can &lt;a href="https://www.coreycleary.me/about/" rel="noopener noreferrer"&gt;sign up to my newsletter&lt;/a&gt; if you'd like to receive my articles directly to your inbox! I also regularly send cheatsheets and other freebies.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In a Node REST API, there are multiple different layers where you could conceivably put request validation, the code where you verify that the request is "valid" and has the correct fields, checks that those fields are the expected types, etc.&lt;/p&gt;

&lt;p&gt;In a layered API that roughly follows a web tier, service tier, and data tier structure, it makes more sense to do the request validation in the web tier, since it is the one that deals with the HTTP request. If the request is invalid, it doesn't make sense to call all the other layers of code. (&lt;em&gt;Note: typically you will have some kind of model or query validation "further down" the stack, but that is a different type of validation than what we're talking about here.&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;But the web tier is made up of middleware and controllers/router handlers. Which of these should you put your request validation in? With all the different types of logic in a REST API, knowing what code goes where can be confusing with lots of conflicting information out there.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;For a recap on the difference between middleware and controllers, &lt;a href="https://www.coreycleary.me/what-is-the-difference-between-middleware-and-controllers" rel="noopener noreferrer"&gt;check out this post.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Best place
&lt;/h2&gt;

&lt;p&gt;I think the conclusive place to put request validation is in the middleware. You usually want to bail as early as possible if you are dealing with an invalid request, as opposed to letting code seep "further down" to execute. Controllers/route handlers are closest to middleware - so it may not seem like a big deal to do the validation there - but it's still "one layer" deeper than just bailing early from the middleware.&lt;/p&gt;

&lt;p&gt;You also keep your controllers thinner and cleaner by keeping request validation out of them, and you end up having better separation of concerns. Middleware is responsible for the validation, and controllers are responsible for routing the request where it needs to go &lt;em&gt;after&lt;/em&gt; it's been validated.&lt;/p&gt;

&lt;p&gt;You can also better reuse the validation logic in other routes if it's separated into middleware rather than baked into a controller.&lt;/p&gt;

&lt;p&gt;Below is pseudo-code for what this would look like:&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// the schema to use for validating the request&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orderSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;total&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;items&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;minimum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;array&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;uniqueItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;itemId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;integer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;integer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/order&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// route name/definition&lt;/span&gt;
    &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;orderSchema&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="c1"&gt;// validation middleware&lt;/span&gt;
    &lt;span class="nx"&gt;createOrderController&lt;/span&gt; &lt;span class="c1"&gt;// controller&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, &lt;code&gt;validate()&lt;/code&gt; is a JSON Schema middleware library function, and that Schema is defined for an order. If the request that the REST API receives is invalid - for example, maybe it's missing the &lt;code&gt;total&lt;/code&gt; field - then the request will stop and return, skipping the controller.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Next time you are setting up request validation, put it into a middleware function before your controllers/route handlers. This way the code will exit as early as possible and you won't spend valuable server resource time and compute power, and will return a response to the client as quickly as possible.&lt;/p&gt;

&lt;p&gt;Love JavaScript but still getting tripped up by unit testing, architecture, etc? I publish articles on JavaScript and Node every 1-2 weeks, so if you want to receive all new articles directly to your inbox, &lt;a href="https://www.coreycleary.me/about/" rel="noopener noreferrer"&gt;here's that link again&lt;/a&gt; to subscribe to my newsletter!&lt;/p&gt;

</description>
      <category>node</category>
    </item>
    <item>
      <title>What is the difference between Middleware and Controllers in Node REST APIs?</title>
      <dc:creator>Corey Cleary</dc:creator>
      <pubDate>Thu, 15 Dec 2022 02:04:15 +0000</pubDate>
      <link>https://dev.to/ccleary00/what-is-the-difference-between-middleware-and-controllers-in-node-rest-apis-3ii8</link>
      <guid>https://dev.to/ccleary00/what-is-the-difference-between-middleware-and-controllers-in-node-rest-apis-3ii8</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Originally published at &lt;a href="https://www.coreycleary.me/what-is-the-difference-between-middleware-and-controllers"&gt;coreycleary.me&lt;/a&gt;&lt;/strong&gt;. This is a cross-post from my content blog. I publish new content every week or two, and you can &lt;a href="https://www.coreycleary.me/about/"&gt;sign up to my newsletter&lt;/a&gt; if you'd like to receive my articles directly to your inbox! I also regularly send cheatsheets and other freebies.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When you're writing code dealing with the request lifecycle, what is the difference between middleware and controllers? What code goes in each? They both touch the "request object" and do things with it, so it seems like there might be overlap or controllers might be redundant to have.&lt;/p&gt;

&lt;p&gt;But they are distinctly different, and understanding the differences in the use cases for each logic type is important in structuring maintainable and scalable applications. Let's examine the distinctions between the two and what code goes where, so the next time you are working on an API you can build it sustainably.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: this post focuses on middleware and controllers from a NodeJS perspective but is applicable to other languages and frameworks.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Middleware vs. Controllers
&lt;/h2&gt;

&lt;p&gt;What's immediately confusing is that a controller can be considered a type of middleware. (And to add to the confusion, "controllers" are sometimes referred to as "route handlers".) But middleware - compared to controllers - handles HTTP-specific concerns as well as concerns that are common to every (or most) request. "HTTP concerns" are things like handling CORS, parsing the body, cookies, etc. "Common concerns" are things like request validation, sessions, request logging using something like &lt;code&gt;morgan&lt;/code&gt;, and authentication and authorization. Middleware is basically agnostic of application logic.&lt;/p&gt;

&lt;p&gt;All of the above you don't want in your controllers. Why? Because controllers are responsible for routing a given request where it needs to go for "processing". They don't directly deal with application logic, but send the request to code that does. Controllers should be fairly "thin" and not do a whole lot. So, a "book order" controller would take the request object after it's already been "pre-processed" by middleware and "successfully passed" it, pull out what data it needs from either the query string or body and send it to the &lt;a href="https://www.coreycleary.me/what-is-the-difference-between-controllers-and-services-in-node-rest-apis"&gt;service layer/domain logic layer to execute the business logic&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Middleware functions are typically (although not always) passed through by all HTTP requests and do not fulfill the request, unless it's a case of returning an error response early. It's the controller that ultimately &lt;em&gt;fulfills&lt;/em&gt; the request with a successful response. They are usually are specific to a single endpoint (like &lt;code&gt;/items&lt;/code&gt;, &lt;code&gt;/orders&lt;/code&gt;, etc).&lt;/p&gt;

&lt;h3&gt;
  
  
  Example code
&lt;/h3&gt;

&lt;p&gt;Controller code will look something 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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// Controller&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createOrder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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;// grab what we need from the request...&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orderTotal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orderItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paymentDetails&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...then route it to the appropriate business-logic-processing functions...&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customerData&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;getCustomerData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;processOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orderTotal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orderItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paymentDetails&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;customerData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sendConfirmationEmailToCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orderItems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// ...then fulfill the request with the response object&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Route definition&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/order&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createOrder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above you will notice that the controller grabs what we need from the request, routes it to the appropriate business-logic-processing function(s), and finally fulfills the request via the response (&lt;code&gt;res&lt;/code&gt;) object.&lt;/p&gt;

&lt;p&gt;Then the chain of middleware will look similar to this, usually called either &lt;code&gt;app.js&lt;/code&gt; or &lt;code&gt;server.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;// middleware library imports&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bodyParser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body-parser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cookieParser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cookie-parser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;passport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;passport&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// for auth&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./routes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// this is a .js file that contains all your route definitions mapped to their respective controllers&lt;/span&gt;

&lt;span class="c1"&gt;// here is the middleware being chained together&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bodyParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bodyParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urlencoded&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;extended&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cookieParser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;passport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c1"&gt;// routes defined last in the chain of middleware&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above, we chain the middleware together, using &lt;code&gt;app.use()&lt;/code&gt;, before we have the routes (which map to controllers) at the end of the chain, so that the request has to pass through the middleware in order before even hitting the controllers.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: if I need to build custom middleware I will generally add a directory called &lt;code&gt;middleware/&lt;/code&gt; to be at the same level as the &lt;code&gt;controllers/&lt;/code&gt; directory, and put my custom middleware functions there.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;While it may seem confusing at first, the difference between these two "layers" / logic types are generally fairly clear, so it should make sense now as to what code goes where. Occasionally, you may run into a scenario where it's less black and white, but then you just have to decide what makes sense for where to put the code based on you specific application requirements.&lt;/p&gt;

&lt;p&gt;Why does this matter? If it's not obvious, this separation of concerns is necessary for developing a maintainable codebase and API. You will save yourself a lot of painful refactoring in the future if you can achieve this separation, because as your code grows and you add more developers to the team things can start to get out of hand quickly.&lt;/p&gt;

&lt;p&gt;Love JavaScript but still getting tripped up by unit testing, architecture, etc? I publish articles on JavaScript and Node every 1-2 weeks, so if you want to receive all new articles directly to your inbox, &lt;a href="https://www.coreycleary.me/about/"&gt;here's that link again&lt;/a&gt; to subscribe to my newsletter!&lt;/p&gt;

</description>
      <category>node</category>
    </item>
    <item>
      <title>Multiple assertions per test may actually be OK</title>
      <dc:creator>Corey Cleary</dc:creator>
      <pubDate>Wed, 30 Nov 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/ccleary00/multiple-assertions-per-test-may-actually-be-ok-1i44</link>
      <guid>https://dev.to/ccleary00/multiple-assertions-per-test-may-actually-be-ok-1i44</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Originally published at &lt;a href="https://www.coreycleary.me/multiple-assertions-per-test-are-actually-ok" rel="noopener noreferrer"&gt;coreycleary.me&lt;/a&gt;&lt;/strong&gt;. This is a cross-post from my content blog. I publish new content every week or two, and you can &lt;a href="https://www.coreycleary.me/about/" rel="noopener noreferrer"&gt;sign up to my newsletter&lt;/a&gt; if you'd like to receive my articles directly to your inbox! I also regularly send cheatsheets and other freebies.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this post I want to examine the claim that "you should only ever have one assertion per test". This is oft-repeated to the point it is basically considered a rule or best practice at this point. But is it actually a good practice that should be followed universally?&lt;/p&gt;

&lt;p&gt;Knowing or learning how to write good tests for your code can be confusing enough - you have to know when and how to use test doubles, write meaningful test cases and descriptions, when to stub a function vs use a real database/API, etc.&lt;/p&gt;

&lt;p&gt;Having a bunch of so-called rules people have made up on top of that makes it all even more confusing. And there are a lot of different interpretations on what this "rule" even means. So I want to look at that statement further and discuss some instances when it might not make sense, and what the original rule should was (likely) meant to intend in the first place since it seems to be repeated often in developer-land.&lt;/p&gt;

&lt;h2&gt;
  
  
  A bad test
&lt;/h2&gt;

&lt;p&gt;First let's look at an example of a test that has a lot of assertions for a single test case. The code we want to test is:&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;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A username must be provided&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;userName&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;getFormattedUserName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// a simplified example just for demonstration purposes, you likely wouldn't be appending a string to a username&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;identifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;usr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userName&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="nx"&gt;identifier&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="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the bad test is:&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;mocha&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mocha&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return a correctly formatted username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;throw&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;testcaseuser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&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;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TESTCASE-USR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an example where multiple assertions &lt;em&gt;are&lt;/em&gt; bad, because it's essentially two tests in one: 1) that the class instantiated without a user name string should throw an error and 2) that the formatting works as expected. But the test case - "should return a correctly formatted username" - means only the second assertion is irrelevant.&lt;/p&gt;

&lt;p&gt;So having multiple assertions here is bad because of an irrelevant assertion.&lt;/p&gt;

&lt;h3&gt;
  
  
  The fix
&lt;/h3&gt;

&lt;p&gt;It can be helpful to think about what the &lt;em&gt;output&lt;/em&gt; for your function is that you are testing. The output of &lt;code&gt;getFormattedUserName()&lt;/code&gt; is not a thrown &lt;code&gt;Error&lt;/code&gt; if the username is not passed in when instantiating the &lt;code&gt;User&lt;/code&gt; class. Instead, it is a formatted username. With that in mind, here is an example of the refactored tests. The obvious thing to do here is to limit the assertions to just testing the formatting, like so:&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return a capitalized username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;testcase456&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&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;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/TESTCASEUSER456/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return a username with an appended identifier&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;testcase456&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&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;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/-USR/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The original test was just one test, but in this case we broke it out into two test cases in order to test more expected output.&lt;/p&gt;

&lt;p&gt;So now we have one assertion per test, but those tests are fairly closely related. What if we did 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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return a correctly formatted username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;testcase456&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should capitalize username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/TESTCASE456/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should append username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/-USR/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can add an assertion description as the second argument to &lt;code&gt;expect()&lt;/code&gt;, which allows us to have multiple assertions per test and which makes it clearer what the issue is if the assertion fails. (NOTE: using Mocha and Chai here, but most test frameworks should also have this ability to add an assertion description)&lt;/p&gt;

&lt;p&gt;This code is helpful to discuss a certain point - that "no multiple assertions per test" makes more sense / may be clearer as "no multiple aspects/concepts per test". I would argue that this example above, where we have one test case - a discrete aspect/concept - with multiple assertions is actually OK. Those assertions are both testing closely related formatting concepts, which can be tested with two "sub-assertions".&lt;/p&gt;

&lt;p&gt;Also, one of the reasons "no multiple assertions per test" has been touted as a rule is because if the assertion fails, you won't know why necessarily, but here we get around that issue by having assertion descriptions.&lt;/p&gt;

&lt;p&gt;In the second example (multiple assertions) we only have to instantiate &lt;code&gt;User&lt;/code&gt; once, wherease with two tests we have to do it twice. &lt;code&gt;User&lt;/code&gt; here is so simple it probably doesn't matter, but you can imagine a scenario where the test setup is more complex. More than one assertion per test may make sense in a case like this where otherwise you’d have to copy most of test A again to make test B&lt;/p&gt;

&lt;p&gt;Having this test as-is above vs. breaking it out into two tests - honestly I think both are OK. At a certain point it gets into splitting hairs, or more of a readability thing. But I wanted to include the test above to show that multiple assertions per test is not always bad.&lt;/p&gt;

&lt;p&gt;Note: it should be pointed out that some test runners will skip the rest of the assertions if one fails and go to the next test, and we mitigate this a bit by having clear assertion descriptions like above, but it's worth pointing out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Single responsibility principle
&lt;/h2&gt;

&lt;p&gt;I'll jump into some other examples of where multiple assertions may make sense in a bit, but want to discuss one more thing related to when this rule does make sense.&lt;/p&gt;

&lt;p&gt;If you find yourself having to continually add assertions to your test case, it might be a sign your "unit under test" is doing too much and is violating the "Single responsibility principle" (SRP), that states "every class, module, or function in a program should have one responsibility/purpose in a program." This is a key sign that you might need to refactor your function, and is a good example of too many assertions being a code smell that you can use to reevaluate the code you're testing.&lt;/p&gt;

&lt;p&gt;This code smell is a sign you should break that function down into multiple ones. You can keep the same test, but treat it more as an "integration" test, and include more "scoped" smaller unit tests after you've broken that into separate tests.&lt;/p&gt;

&lt;p&gt;Now that we've covered the general intent of the rule/principle in question and shown some examples of bad tests that likely led to this becoming a rule, let's look at some other types of examples where it's more nuanced and where multiple assertions may make more sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  REST API testing
&lt;/h2&gt;

&lt;p&gt;One use case where you probably should use multiple assertions for your test case is if you are testing a REST API. Imagine you are testing an API endpoint and you want to test a successfull response is returned with expected 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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET /v1/accounts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return all the accounts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;api&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/v1/accounts&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="nf"&gt;expect&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;id&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John&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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Jack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the case we are testing is that the endpoint returns all expected accounts. As part of that, it has assertions on a couple of the response headers, in addition to an assertion for the actual response body/accounts.&lt;/p&gt;

&lt;p&gt;At first glance this appears to be in violation of the "one assertion per test" principle, so you might be inclined to rewrite the tests like so:&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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET /v1/accounts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return 200 OK&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;api&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/v1/accounts&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="nf"&gt;expect&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="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return Content-Type application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;api&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/v1/accounts&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return accounts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;api&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/v1/accounts&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;id&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John&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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Jack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gets us to one assertion per test, but does it make sense?&lt;/p&gt;

&lt;p&gt;Firstly, you now have to execute the GET call multiple times, which adds additional test execution time / latency to your tests. Additionally, API tests by nature are closer to &lt;em&gt;integration tests&lt;/em&gt; since their execution ends up hitting multiple parts of the code, so it's not implausible in these scenarios to need more than one assertion to test the output / result of what you are testing.&lt;br&gt;
Also, those headers could be considered to be part of the response body, and thus part of a "singular" test case.&lt;br&gt;
Lastly, test headers by themselves don't exactly constitute a "unit" to be tested - I'd rather test all the headers together with a case that tests for appropriate header responses. Sure, if one of those headers changes and it breaks the test, depending on your test runner the rest of the assertions may not run, but you will know right away which header change broke the test.&lt;/p&gt;

&lt;p&gt;So in this scenario I think the first example test is acceptable rather than testing each individual header individually.&lt;/p&gt;

&lt;p&gt;An alternative - which would still include multiple assertions per test - would be to test the response body in one test case and the headers (multiple assertions) in another test case, like so:&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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET /v1/accounts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return the appropriate headers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;api&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/v1/accounts&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="nf"&gt;expect&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return all accounts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;api&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/v1/accounts&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;id&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John&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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Jack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is more of a "middle ground" and something you may want to consider when writing endpoint tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Object and property testing
&lt;/h2&gt;

&lt;p&gt;This could be a blogpost by itself, but I want to briefly cover it because this is a scenario where you might need multiple asserts per test in order to verify the test case.&lt;/p&gt;

&lt;p&gt;Imagine a simplified function that returns an object like:&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;getUserPreferences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// the below is hardcoded just for demonstration purposes&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;preferences&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;emailCommunication&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;phoneCommunication&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;smsCommunication&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can imagine this is some object related to notification preferences from an online service I signed up for.&lt;/p&gt;

&lt;p&gt;The test for this could look like:&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return notification preferences&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;preferences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getUserPreferences&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;preferences&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;preferences&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;emailCommunication&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;phoneCommunication&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;smsCommunication&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That test will pass, with only one assertion, but what if we want to add another property to the returned object later on? Like &lt;code&gt;physicalMailCommunication&lt;/code&gt; for example.&lt;/p&gt;

&lt;p&gt;We could update the test like so:&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return notification preferences&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;preferences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getUserPreferences&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;preferences&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;preferences&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;emailCommunication&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;phoneCommunication&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;smsCommunication&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;physicalMailCommunication&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Or&lt;/em&gt; we could do something 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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return notification preferences&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;emailCommunication&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;phoneCommunication&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;smsCommunication&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;physicalMailCommunication&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getUserPreferences&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emailCommunication&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;phoneCommunication&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;smsCommunication&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;physicalMailCommunication&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a case where having multiple assertions per test is valid, because you are testing properties of the object. Now, you might not need to check each property like this, and instead could just do a deep object euality check like the first test. If the test fails Mocha (and most test runners) will show you the properties of the object that don't match the expected values. But testing each property one by one may be valid if you have a function that returns an object where you don't care about testing all properties and only want/need to test a few. And in that case asserting on deep object equality won't work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multiple assertions in an integration test
&lt;/h2&gt;

&lt;p&gt;Lastly, with integration tests you're likely to run into scenarios where you need to have multiple assertions for a given test case in order to test it properly.&lt;/p&gt;

&lt;p&gt;An example of this is in some tests I wrote for a Redis Streams library, &lt;a href="https://github.com/coreyc/redis-streams-client/blob/168910e3039c4cc44bae6c909ed26a12290d857a/tests/client.test.js#L145" rel="noopener noreferrer"&gt;you can see the test linked here&lt;/a&gt; and a slightly simplified version below:&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should subscribe to a consumer group and process the event&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;workerFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;streamClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;groupName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;streamName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;readTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;workerFunction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eventPayload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&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;this is a subscribe 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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;streamClient2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EventStreamClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;streamClient2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;streamName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;eventMeta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;defaultEventMeta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;eventPayload&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// we can't spy on private functions (workerFunction is private w/in the closure in subscribe&lt;/span&gt;
  &lt;span class="c1"&gt;// but testing there are items, and they've been acked (not on PEL) works just as well&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eventFromStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;streamName&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formatted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getFormatted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventFromStream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formatted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include&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="nx"&gt;PAYLOAD_PREFIX&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;this is a subscribe test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pending&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getPending&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;streamName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;groupName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;subscribe()&lt;/code&gt; function above subscribes to events from a stream, processes the received event and - if processing happens successfully - finally ACK's (acknowledges) it. This test case is testing a single concept - that it processes the event (and in order to do that, subscribes to the event stream) - but has two assertions.&lt;/p&gt;

&lt;p&gt;This is because we need to first check the event, as part of test setup, is published to the stream. If that part fails, then anything related to the &lt;code&gt;subscribe()&lt;/code&gt; function will fail and we want that to happen early because we need to fix that. This first assertion is also needed, because as noted in the code comment we can't spy on the &lt;code&gt;workerFunction&lt;/code&gt; passed in to make sure it was called. So instead of having a separate assertion for that, like &lt;code&gt;expect(workerFunction)to.have.been.called&lt;/code&gt;, we can make sure those events are there and then they are no longer in pending status, because only successfully processed items - the &lt;code&gt;workerFunction&lt;/code&gt; does the processing - are ACK'd and removed from the pending status.&lt;/p&gt;

&lt;p&gt;And that's what the second assertion checks.&lt;/p&gt;

&lt;p&gt;As you work from &lt;em&gt;unit&lt;/em&gt; to &lt;em&gt;integration&lt;/em&gt; to &lt;em&gt;end-to-end&lt;/em&gt; tests, this can become more common. That's because generally with integration tests you're testing a larger scope, and with end-to-end tests you're testing a larger scope than that. "Scope" meaning number of touchpoints. So it follows that you'll likely need more assertions to verify the behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Hopefully you have a more nuanced understanding of the "multiple assertions rule" now, and can make a more informed decision on how you should write any given test for your application. I didn't want to provide anything prescriptive, because each application and context is different and rules/best practices become dogmas when this is not taken into account. Every single production application you've ever worked on for a company has done things slightly (if not totally) differently. That doesn't mean that just because it's different that its correct and you can ignore bad practices, but it's important to take into consideration.&lt;/p&gt;

&lt;p&gt;The point is not that you &lt;em&gt;have to&lt;/em&gt; or &lt;em&gt;should&lt;/em&gt; use multiple assertions per test - you may not have &lt;em&gt;any&lt;/em&gt;. But moreso that it's not a hard rule you should strictly adhere to. Context is everything.&lt;/p&gt;

&lt;p&gt;If you find yourself having multiple assertions per test, consider the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does having to add more assertions mean that the code being tested is doing too much? Does it need to be refactored to better handle a single responsibility?&lt;/li&gt;
&lt;li&gt;Are you focusing on what the "output" of the unit under test is? Or are you testing extraneous and irrelevant things? This especially applies to "pure functions" that don't call a database, API, etc.&lt;/li&gt;
&lt;li&gt;Are there multiple assertions because you are testing integration or end-to-end tests, that would be difficult to only assert on one output or action being performed?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you consider the above and decide that multiple assertions makes sense, then buck the "rule" and go for it. And if you want to learn how to better structure your tests using Mocha, &lt;a href="https://www.coreycleary.me/better-test-structuring-using-mochas-context" rel="noopener noreferrer"&gt;check out this other post on doing that&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Love JavaScript but still getting tripped up by unit testing, architecture, etc? I publish articles on JavaScript and Node every 1-2 weeks, so if you want to receive all new articles directly to your inbox, &lt;a href="https://www.coreycleary.me/about/" rel="noopener noreferrer"&gt;here's that link again&lt;/a&gt; to subscribe to my newsletter!&lt;/p&gt;

</description>
      <category>testing</category>
      <category>node</category>
    </item>
    <item>
      <title>Better test structuring using Mocha's context()</title>
      <dc:creator>Corey Cleary</dc:creator>
      <pubDate>Thu, 03 Nov 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/ccleary00/better-test-structuring-using-mochas-context-5fhj</link>
      <guid>https://dev.to/ccleary00/better-test-structuring-using-mochas-context-5fhj</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Originally published at &lt;a href="https://www.coreycleary.me/better-test-structuring-using-mochas-context"&gt;coreycleary.me&lt;/a&gt;&lt;/strong&gt;. This is a cross-post from my content blog. I publish new content every week or two, and you can &lt;a href="https://www.coreycleary.me/about/"&gt;sign up to my newsletter&lt;/a&gt; if you'd like to receive my articles directly to your inbox! I also regularly send cheatsheets and other freebies.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Sometimes when working on adding or modifying unit tests within a test suite, you may not be sure what the best way to structure the tests within that suite is.&lt;/p&gt;

&lt;p&gt;The tests may be getting cluttered, out of control, and you may have repetition.&lt;/p&gt;

&lt;p&gt;But better organization will lead to making writing tests less painful (and even enjoyable) because they're easier to maintain.&lt;br&gt;
And since you're usually working on code within a team, it will make it easier for everyone on that team to maintain, understand what the tests are testing, and to add new tests as well.&lt;/p&gt;

&lt;p&gt;If you're using  for your tests, one way to do this is by using the &lt;code&gt;context()&lt;/code&gt; function. You're almost definitely already using &lt;code&gt;describe()&lt;/code&gt; to group your test cases, but &lt;code&gt;context()&lt;/code&gt; will provide for more explicit grouping of tests within your suite.&lt;/p&gt;
&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;Take a look at the example below, that is only using &lt;code&gt;describe()&lt;/code&gt; for grouping tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Array&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;indexOf()&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should not throw an error when the array item is not present&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="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="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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nx"&gt;should&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="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return -1 when the array item is not present&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;should&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return the index where the element first appears in the array&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;should&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll notice that the phrase "when the array item is not present" is repeated twice, in the 1st and 2nd tests.&lt;/p&gt;

&lt;p&gt;It was kind of annoying to type that twice, and from reading these tests you don't immediately pick up on the fact that those tests are related. If you have business requirements for a feature you're building, you might even have requirements like "when X is not present, do Y" and "when X is not present and A, do B".&lt;/p&gt;

&lt;p&gt;Instead it would be great if we could group these together. Now, we could use another &lt;code&gt;describe()&lt;/code&gt; to do this, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;indexOf()&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;when not present&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should not throw an error when the array item is not present&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="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="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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nx"&gt;should&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="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;

      &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return -1 when the array item is not present&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;should&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return the index where the element first appears in the array&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;should&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But then it sort of begs the question... what is the difference between the nested &lt;code&gt;describe()&lt;/code&gt; block and the other individual &lt;code&gt;it()&lt;/code&gt; blocks within the same "parent" &lt;code&gt;describe('indexOf()', () =&amp;gt; {})&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;This is where the &lt;code&gt;context()&lt;/code&gt; function comes in. We can instead use it to group the contextually similar test cases, and have them still be within our "parent" unit test &lt;code&gt;describe()&lt;/code&gt; block.&lt;/p&gt;

&lt;p&gt;Take a look at the example above, rewritten below using it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Array&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;indexOf()&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;when not present&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should not throw an error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="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="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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nx"&gt;should&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="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;

      &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return -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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;should&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;when present&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return the index where the element first appears in the array&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;should&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;context()&lt;/code&gt; is just an alias for &lt;code&gt;describe()&lt;/code&gt;, so it works exactly the same and is the same function.&lt;br&gt;
From a &lt;em&gt;test runner&lt;/em&gt; perspective this doesn't do anything - it doesn't change the way the tests are run, the order they're run in, etc.&lt;/p&gt;

&lt;p&gt;But BDD-style tests are designed to be readable to even non-developers (like QA and business analysts/product managers).&lt;br&gt;
So having an "extra" function we can use to make it clearer how the tests are grouped and what a given "test block" is testing is a huge boost for readability.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use context?
&lt;/h2&gt;

&lt;p&gt;A good heuristic for when to use &lt;code&gt;context()&lt;/code&gt; over &lt;code&gt;describe()&lt;/code&gt; is when you repeat yourself across tests.&lt;br&gt;
That's a key indicator that what you're dealing with is, in fact, a contextual set of tests, and that they can likely be grouped together.&lt;/p&gt;

&lt;p&gt;Not &lt;em&gt;everything&lt;/em&gt; needs to be grouped using it, I certainly don't do that. But when you have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you're repeating the same language in your test cases&lt;/li&gt;
&lt;li&gt;you're starting to have &lt;em&gt;many&lt;/em&gt; test cases within a single "parent" unit, and that starts getting out of control, and can group some into contextually similar sub-groups&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...&lt;code&gt;context()&lt;/code&gt; can come in very handy.&lt;/p&gt;

&lt;p&gt;It will make your test suites easier to read, your teammates will understand what the intent of the tests are, and it can help the tests better serve as documentation of requirements/functionality.&lt;/p&gt;

&lt;p&gt;Love JavaScript but still getting tripped up by unit testing, architecture, etc? I publish articles on JavaScript and Node every 1-2 weeks, so if you want to receive all new articles directly to your inbox, &lt;a href="https://www.coreycleary.me/about/"&gt;here's that link again&lt;/a&gt; to subscribe to my newsletter!&lt;/p&gt;

</description>
      <category>node</category>
      <category>testing</category>
    </item>
    <item>
      <title>What is the difference between a job queue and worker threads?</title>
      <dc:creator>Corey Cleary</dc:creator>
      <pubDate>Thu, 20 Oct 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/ccleary00/what-is-the-difference-between-a-job-queue-and-worker-threads-1oc6</link>
      <guid>https://dev.to/ccleary00/what-is-the-difference-between-a-job-queue-and-worker-threads-1oc6</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Originally published at &lt;a href="https://www.coreycleary.me/what-is-the-difference-between-a-job-queue-and-worker-threads"&gt;coreycleary.me&lt;/a&gt;&lt;/strong&gt;. This is a cross-post from my content blog. I publish new content every week or two, and you can &lt;a href="https://www.coreycleary.me/about/"&gt;sign up to my newsletter&lt;/a&gt; if you'd like to receive my articles directly to your inbox! I also regularly send cheatsheets and other freebies.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are several different architectural patterns and solutions for handling asynchronous code. Queues, promises, worker threads, child processes, the &lt;code&gt;cluster&lt;/code&gt; module, scaling horizontally with multiple containers, etc.&lt;br&gt;
Whether you're dealing with scaling up or computationally-heavy tasks, certain patterns/solutions will be the right choice over others.&lt;/p&gt;

&lt;p&gt;Two of these patterns/methods that might be confusing as to when you would use which one due to perceived similarities are job/task queues and worker threads.&lt;br&gt;
Both are options if you want to handle computationally heavy work and not block the event loop.&lt;br&gt;
With both you are "outsourcing" work to be executed elsewhere besides the event loop.&lt;/p&gt;

&lt;p&gt;Getting a better grasp on these will help you know when to use which solution, and help you implement your designs faster and in a more robust way.&lt;br&gt;
With architecture choices I've found that once you decide on it, it can be more difficult to change compared to changes at the "code-level", so best to pick the right solution for your problem/use case off the bat.&lt;/p&gt;

&lt;p&gt;So what are the differences and when would you use which one?&lt;/p&gt;

&lt;h2&gt;
  
  
  Job queue
&lt;/h2&gt;

&lt;p&gt;Compared to worker threads, a "job queue" (or "task queue") is more of an architectural design pattern.&lt;br&gt;
It's not a part of the NodeJS API, but is a pattern you implement (or a third party library implements for you), &lt;br&gt;
with a set of common features.&lt;/p&gt;

&lt;p&gt;Job queues generally provide features like scheduling, coordinating, and retrying jobs, which helps with scalability as well as "fault-tolerance".&lt;br&gt;
Instead, if you handle all of that in application memory then it will be ephemeral, and less able to recover upon failure.&lt;br&gt;
If the main process dies, it will take down all threads with it.&lt;/p&gt;

&lt;p&gt;With a queue, the jobs are usually run in a separate component/service from the application &lt;em&gt;producing&lt;/em&gt; the jobs, &lt;br&gt;
and can run in parallel with it. The service that produces/schedules/creates the jobs is the producer, &lt;br&gt;
and the service(s) that do the work are consumers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Worker threads
&lt;/h2&gt;

&lt;p&gt;"Worker threads" are more of a primitive as opposed to an architectural or design pattern.&lt;br&gt;
This primitive is a building block used to constructu a larger abstraction, and you could even use worker threads to implement the job queue pattern.&lt;br&gt;
Of course, you would need a lot of other code and features to implement that pattern - simply "dropping in" worker threads &lt;br&gt;
doesn't automatically mean you have a job queue - but it can be done.&lt;/p&gt;

&lt;p&gt;Each worker thread has its own V8 instance and event loop, compared to single-threaded NodeJS where there is one event loop that all code uses.&lt;br&gt;
This is an important distinction, because having multiple event loops is a big part of what allows worker threads to work as semi-isolated.&lt;br&gt;
I say "semi-isolated" because they still run as part of the same &lt;em&gt;process&lt;/em&gt; as the "main" NodeJS thread.&lt;/p&gt;

&lt;p&gt;To use queue terminology, in this case worker threads are generally the "consumers", and the "main" NodeJS thread is the "producer".&lt;/p&gt;

&lt;p&gt;In contrast to a job queue (via the library you're likely using), you need to implement retry logic, scheduling, and priority levels for the worker threads yourself.&lt;br&gt;
There might be some libraries out there that do this that I'm not aware of, but the general idea is that retry-ability &lt;br&gt;
is not native to worker threads. And since the "main" Node thread is the orchestrator of the worker threads, it would be something you'd have to implement at that level, including &lt;br&gt;
tracking of which ones failed, initiating retries, etc.&lt;/p&gt;

&lt;p&gt;Instead, if you have a CPU-intensive task that only has a single step - where failure may mean less than if you have a multistep process, in which case you'd likely have to rollback certain things at certain steps - &lt;br&gt;
and you can afford to let it fail, a worker thread might be the right choice for your use case.&lt;/p&gt;

&lt;p&gt;Another important thing to note - an &lt;em&gt;uncaught&lt;/em&gt; exception will kill the whole process, taking all threads including the "main" Node thread down with it. If that happens in a job consumer/processor, you still have all the others running &lt;br&gt;
assuming you have more than one consumer. Obviously you can mitigate this by having a "global" error even listener and handle the exception there,&lt;br&gt;
 but even in case of a single worker thread failure another worker thread would have to be spun up and would not automatically recover.&lt;br&gt;
That's logic you would have to implement yourself in the "main" thread.&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions for use case
&lt;/h2&gt;

&lt;p&gt;Some things to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do you already have queue infrastructure setup, a library selected, pattern for calling it, etc.?

&lt;ul&gt;
&lt;li&gt;If you do, and you're on the fence about which solution to choose given your use case, it may make sense to use queues if you already have them setup.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Do you need schedulability? (then use a job queue)

&lt;ul&gt;
&lt;li&gt;It would be very difficult to build this feature using a purely worker threads solution - the main Node thread would have to act as an orchestrator polling some database where you have the schedules stored, and use some lookup to determine what code to run for the job, etc.
What you would end up with would likely resemble a job queue anyways.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Do you need retryability? (then &lt;em&gt;probably&lt;/em&gt; use a job queue)

&lt;ul&gt;
&lt;li&gt;You can build this feature with worker threads but what if the main node thread goes down or the container is restarted?
If you were tracking retries in memory you would lose the number of attempts and when the process/container restarts it wouldn't be retried.
Because threads are more "ephemeral" you have to factor in fault tolerance.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;How many threads do you have available vs. how many "nodes" can you distribute for workers, and what is the processing power of each of those?

&lt;ul&gt;
&lt;li&gt;Depending on how many threads you have available in your service, and how many individual job consumer processes you can sping up will likely factor into your decision.
If it's too expensive to be running that many separate job consumers, which will likely be a server/container per consumer, and you've determined you need that many consumers given your workload,
it may make sense to look at if worker threads would be more cost-effective. Of course, if you have such a high workload you need lots of consumers then you might need lots of threads too, which may
come back around to being too expensive, but it's something to consider.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Examples of when you would use which
&lt;/h2&gt;

&lt;p&gt;At a certain point if you have enough requirements like being able to set job priority, scheduling, etc. it may make more sense to have a separate architectural piece for this, via a job queue.&lt;/p&gt;

&lt;p&gt;A job queue is useful if you have lots of things in your "order of operations" to be done rather than a single thing&lt;br&gt;
As briefly mentioned before infrastructure/setup cost is a factor you'll have to consider in setting up a queue.&lt;/p&gt;

&lt;p&gt;But looking at some more specific examples...&lt;/p&gt;

&lt;h3&gt;
  
  
  Lots of sensors / "Internet of Things" (IoT)
&lt;/h3&gt;

&lt;p&gt;In this use case you have many sensors communicating frequently over the network with your server and database, likely calling multiple services and inserting/updating several different tables with sensor data.&lt;br&gt;
It's a more involved, multi-step process to process the data sent in by the sensor. It's not just a single task that happens.&lt;br&gt;
And you need to return a successful response from the server to your sensors as quickly as possible since sensors usually can't wait a long time for a response, and the server has lots to process.&lt;/p&gt;

&lt;p&gt;So, you recognize you need to use an asynchronous pattern for this.&lt;/p&gt;

&lt;p&gt;Worker threads are good for CPU-intensive work but don't help much with I/O-intensive work, &lt;br&gt;
and in an IoT use case where you have potentially hundreds of thousands of devices sending data every &lt;em&gt;X&lt;/em&gt; seconds &lt;br&gt;
its very I/O-intensive, at the network and database layers as opposed to CPU-intensive processes like machine learning or image processing&lt;br&gt;
(generally speaking).&lt;/p&gt;

&lt;p&gt;It's also possible that you have too many requests from sensors at once and don't have enough threads to delegate all that work in a timely fashion to the worker threads.&lt;/p&gt;

&lt;p&gt;This is a use case where a queue is almost always going to be the better option.&lt;/p&gt;

&lt;h3&gt;
  
  
  Image processing
&lt;/h3&gt;

&lt;p&gt;Imagine a use case where you have an auction site and a user can upload images of the items they're selling. &lt;br&gt;
These images may need to be scaled down to a reasonable resolution so as to not eat a ton of storage space,&lt;br&gt;
as well as be able to be edited by the user (rotated, brightened, etc).&lt;/p&gt;

&lt;p&gt;While this &lt;em&gt;can&lt;/em&gt; happen in the browser, generally image processing, resizing, etc is computationally itensive and so happens on the server using something like &lt;code&gt;ImageMagick&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This "task" is not complex in that there are multiple steps that need to happen. Instead it's a single step but is compute-heavy.&lt;/p&gt;

&lt;p&gt;But it's also a task that if it fails, likely isn't a big deal. You can show an error message and the user can simply retry.&lt;br&gt;
Could you setup a queue for this? Sure, but the infrastructure setup if you're solely using it for this use case may not be worth the extra setup overhead and cost.&lt;/p&gt;

&lt;p&gt;Because you probably don't need retries, you definitely don't need schedulability, and this is a one-off task, using worker threads is a good solution for this use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Hopefully this examination of differences and tradeoffs between worker threads and a job/task queue gives you a better idea &lt;br&gt;
of which one to choose for you given use case. There are lots of asynchronous patterns in NodeJS and software architecture in general, &lt;br&gt;
so having a clearer understanding of some of these patterns will make choosing the right solution from the start easier, and remove headaches later on.&lt;/p&gt;

&lt;p&gt;Love JavaScript but still getting tripped up by local dev, architecture, testing, etc? I publish articles on JavaScript and Node every 1-2 weeks, so if you want to receive all new articles directly to your inbox, &lt;a href="https://www.coreycleary.me/about/"&gt;here's that link again&lt;/a&gt; to subscribe to my newsletter!&lt;/p&gt;

</description>
      <category>node</category>
    </item>
    <item>
      <title>Using a task queue vs. just not waiting for Promise to resolve</title>
      <dc:creator>Corey Cleary</dc:creator>
      <pubDate>Thu, 04 Aug 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/ccleary00/using-a-task-queue-vs-just-not-waiting-for-promise-to-resolve-1m7m</link>
      <guid>https://dev.to/ccleary00/using-a-task-queue-vs-just-not-waiting-for-promise-to-resolve-1m7m</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Originally published at &lt;a href="https://www.coreycleary.me/using-a-task-queue-vs-just-not-waiting-for-promise-to-resolve"&gt;coreycleary.me&lt;/a&gt;&lt;/strong&gt;. This is a cross-post from my content blog. I publish new content every week or two, and you can &lt;a href="https://www.coreycleary.me/about/"&gt;sign up to my newsletter&lt;/a&gt; if you'd like to receive my articles directly to your inbox! I also regularly send cheatsheets and other freebies.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When working with Node and JavaScript one of the benefits is that we can make code asynchronous, whether via callbacks or Promises. Instead of having to wait for a line of code to finish executing we can continue on if we don't &lt;code&gt;await&lt;/code&gt; or &lt;code&gt;.then()&lt;/code&gt; the Promise, or don't nest the callbacks if using those.&lt;/p&gt;

&lt;p&gt;You are also likely aware of task queues, where instead of executing the code in your "main" service you create a job/task in a queue and a consumer watches the queue and &lt;em&gt;it&lt;/em&gt; does the work rather than the "main" service. Rather than being a native asynchronous Node/JS thing, this is an asynchronous pattern at the architecture level.&lt;/p&gt;

&lt;p&gt;Usually a task queue is used when you want to offload a longer running block of code and you don't need the results of that code in the rest of your code.&lt;br&gt;
But if we can skip waiting for asynchronous JavaScript code to finish, and keep the code "fast" that way, doesn't that accomplish the same thing?&lt;br&gt;
Why would you need a queue to begin with?&lt;/p&gt;

&lt;p&gt;This is an important concept to understand especially as you become more "senior" and are making architecture decisions. So let's explore both and understand what the difference is / why you would want to use one option over the other.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code processing
&lt;/h2&gt;

&lt;p&gt;When you don't wait for the Promise to resolve, the most important thing to remember is that Node is still processing that Promise from the event loop. It's not like it disappeared, or was sent to some magic factory that does the work for free.&lt;br&gt;
So even if you don't wait for resolution, your server is still executing that code. This is important to point out because you may have a scenario where that execution is computationally expensive (using lots of CPU and/or Memory).&lt;br&gt;
So even if you don't wait for it to complete, server performance will be something you need to factor in.&lt;/p&gt;

&lt;p&gt;Imagine you have a computationally intensive task like image processing, where when that is executed in the Node event loop it bogs down your server.&lt;br&gt;
This is a prime candidate for something that should be pushed to a task queue. You're offloading that computationally expensive somewher else, again you can't avoid it. But that work is no longer in the main service bogging it down, and instead you can more immediately return the response to the user. &lt;em&gt;And&lt;/em&gt; you can now scale up or down consumers (the "services" executing the code) to essentially load balance the work.&lt;/p&gt;
&lt;h2&gt;
  
  
  Error handling when not waiting for Promise resolution
&lt;/h2&gt;

&lt;p&gt;This is probably a good time to discuss another important consideration when not waiting for Promise resolution.&lt;br&gt;
If the Promise rejects, you &lt;em&gt;still need to catch it&lt;/em&gt;. If you don't you'll get an &lt;code&gt;Unhandled promise rejection&lt;/code&gt; error.&lt;/p&gt;

&lt;p&gt;The most "local" way to do that is to use &lt;code&gt;.catch()&lt;/code&gt;, like so:&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// artificial rejection just to demonstrate&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;this is a rejection&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="c1"&gt;// notice, NO .then() or await&lt;/span&gt;
&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// handle Promise rejection here&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that you can't use try/catch here like so:&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="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a &lt;code&gt;try/catch&lt;/code&gt; even without &lt;code&gt;await&lt;/code&gt; it will result in an uncaught Promise error. There's not another way of doing this with &lt;code&gt;try/catch&lt;/code&gt; that I'm aware of.&lt;/p&gt;

&lt;p&gt;You could also use a "top-level" as opposed to "local" error handler, something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unhandledRejection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unhandled Rejection at:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reason:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// Application specific logging, throwing an error, or other logic here&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But regardless, it needs to be handled. Especially if you're using newer version of Node. Depending on the version, newer versions won't just throw a warning, they will kill the server. And if you go the "top-level" route you may lose out on supplementing the error with other variables or information that are within the function's scope.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retrying failed Promises
&lt;/h2&gt;

&lt;p&gt;Another thing to consider if you are thinking about not waiting for Promise resolution is that if it does fail/reject, you need to add code to handle retrying the Promise (if you in fact want to retry it). Something like:&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;retry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;retrying...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;someFnThatReturnsPromise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course if you don't care about the function/Promise rejecting, and can live with that, then you don't have to do this. But usually you're probably going to want that code to execute successfully.&lt;/p&gt;

&lt;p&gt;The code above gets us Promise function retries, but what if the &lt;code&gt;someFnThatReturnsPromise&lt;/code&gt; above keeps failing? Maybe there is a logic error or TypeError somewhere within the function definition. No number of retries are going to get it to successfully complete.&lt;/p&gt;

&lt;p&gt;We can implement a &lt;code&gt;maxNumberRetries&lt;/code&gt; in the &lt;code&gt;retry()&lt;/code&gt; function, and that will stop the retries after X number of times. But we're still back to the issue that the code isn't completing successfully.&lt;br&gt;
And those retries that happen are still in the event loop, using server processing power (back to point #1). What if you absolutely need those functions to complete and it's mission critical to your app?&lt;/p&gt;

&lt;p&gt;Retrying those "permanent" failures becomes more difficult.&lt;/p&gt;

&lt;p&gt;Also, in order to monitor these failures, we have to instrument the code to log out retries, number of attempts, etc. Again, that's doable, but it means more code to implement.&lt;br&gt;
And unless you have something custom setup like a custom counter using &lt;code&gt;statsd&lt;/code&gt;, Splunk, etc. to instrument and monitor the failures in some dashboard, you're probably going to just be logging the failures. And that means coming through logs to find the failures, or maybe setting up a CloudWatch query to watch for these failures.&lt;/p&gt;

&lt;p&gt;Maybe a queue would make some of this simpler though? With less custom work you have to do on your end?&lt;/p&gt;

&lt;p&gt;Depending on which queue solution you use, you usually get the following out of the box:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;configurable retries&lt;/li&gt;
&lt;li&gt;Dead letter queue ("DLQ")&lt;/li&gt;
&lt;li&gt;queue monitoring/observability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of adding custom retry code you usually get configurable "automatic" retries out of the box with a task queue solution.&lt;br&gt;
In a scenario in which you get continual failures, that task can be automatically moved to a DLQ, where it will sit until you act on it. But will help you avoid an infinite retry loop.&lt;/p&gt;

&lt;p&gt;Imagine you have some asynchronous code where a user signs up to your app, your code sends a welcome email out, creates credentials for them, and kicks off some marketing sequence. Maybe not super processing-intensive, but something you decide you don't wait to wait for (maybe your email provider is a bit slow, for example).&lt;br&gt;
What if you pushed some bad processing code (i.e. your email-send code had a bug in it)? With a queue solutoin, you could make a fix, and then retry all these with the fixed code using the items from the DLQ.&lt;/p&gt;

&lt;p&gt;And you'll also get observability into not just the DLQ - you want to know when code just won't successfully execute - but generally your others tasks too. Things like how many are currently in the queue, how many are processing, completed, etc.&lt;/p&gt;

&lt;p&gt;The main point here is that you get these things out of the box (again most solutions should have these features but always make sure to check).&lt;/p&gt;

&lt;h2&gt;
  
  
  Infrastructure setup required for queue if not already setup
&lt;/h2&gt;

&lt;p&gt;If you don't have the infrastructure already setup for a task queue, that is "overhead" work you or someone on your team will have to take care of. And obviously with more infrastructure comes more cost, so that's something to factor when you're looking at pricing/billing.&lt;/p&gt;

&lt;p&gt;If you're building out a MVP, or can live with some code execution failures and less observability into the execution of that code, maybe the infrastructure setup is not worth it for you.&lt;br&gt;
If you go with just not waiting for Promise resolution, the good thing is that solution is just application code. No queue setup, worker setup, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  A note on Lambdas
&lt;/h2&gt;

&lt;p&gt;It's worth pointing out that if you're using AWS Lambdas and you don't &lt;code&gt;await&lt;/code&gt; or &lt;code&gt;.then()&lt;/code&gt; the Promise, you run the risk of that code "hijacking" and finishing its resolution within another Lambda request. I'm not an expert on Lambdas but I've personally seen this happen. A single Lambda was executing two different requests, with the part of one request that wasn't &lt;code&gt;await&lt;/code&gt;'ed finishing in that Lambda run.&lt;br&gt;
So the above discussion on Promises needs to be weighed against Lambda nuances.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;I've gone through every consideration I can think of when determining if you should use a task queue or just skip Promise resolution and continue code execution.&lt;br&gt;
But to end with a pseudo decision matrix for when you'd likely use which:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If processing (like image processing) is going to take several seconds or minutes, you should probably use a queue. It's likely too processing intensive for the server and you might end up with ancillary performance issues even though you're skipping resolution and continuing to the next bit of code.&lt;/li&gt;
&lt;li&gt;If the task is not mission-critical and not processing intensive, and you can deal with some failures here and there, not waiting for Promise resolution is probably someFnThatReturnsPromise

&lt;ul&gt;
&lt;li&gt;The same goes for if you can live with continual failures (in the case of a programming bug related to the task)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;If the task &lt;em&gt;is&lt;/em&gt; mission-critical, even if it's not processing intensive, you should probably use a queue so you get observability, retries, and a DLQ (which again is really useful in case you had a programming bug)&lt;/li&gt;
&lt;li&gt;If infrastructure setup is too much work for you, even given the above considerations, just don't wait for Promise resolution and don't use a queue

&lt;ul&gt;
&lt;li&gt;This might seem obvious but if you either can't setup the queue infrastructure or it's too much work, you're not going to have a queue anyways so can't use that solution.&lt;/li&gt;
&lt;li&gt;If given your non-functional requirements and technical considerations you determine a task queue is right for your application though, I'd recommend biting the bullet and setting up the infrastructure.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The ability to work with asynchronous code in Node and JavaScript is great and obviously a core part of the language, but it can bring up some confusions too. Hopefully this discussion and explanation of the differences give you more of a nuanced understanding of the differences between the two approaches and helps you decide when to use which.&lt;/p&gt;

&lt;p&gt;Love JavaScript but still getting tripped up by local dev, architecture, testing, etc? I publish articles on JavaScript and Node every 1-2 weeks, so if you want to receive all new articles directly to your inbox, &lt;a href="https://www.coreycleary.me/about/"&gt;here's that link again&lt;/a&gt; to subscribe to my newsletter!&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
    </item>
    <item>
      <title>ExpressJS antipattern: making everything middleware</title>
      <dc:creator>Corey Cleary</dc:creator>
      <pubDate>Wed, 20 Jul 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/ccleary00/expressjs-antipattern-making-everything-middleware-1i1g</link>
      <guid>https://dev.to/ccleary00/expressjs-antipattern-making-everything-middleware-1i1g</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Originally published at &lt;a href="https://coreycleary.me/expressjs-antipattern-making-everything-middleware"&gt;coreycleary.me&lt;/a&gt;&lt;/strong&gt;. This is a cross-post from my content blog. I publish new content every week or two, and you can &lt;a href="https://www.coreycleary.me/about/"&gt;sign up to my newsletter&lt;/a&gt; if you'd like to receive my articles directly to your inbox! I also regularly send cheatsheets and other freebies.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Something I see in many ExpressJS APIs is the overuse / incorrect use of middleware. Sometimes to the extent that almost &lt;em&gt;everything&lt;/em&gt; is middleware.&lt;/p&gt;

&lt;p&gt;What this usually ends up looking like is 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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Router&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;getCustomerData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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;try&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;customerId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customerDetails&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;fetchUserDetails&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transactionHistory&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;fetchCustomerTransactionHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerDetails&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processOrder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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;try&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;customerDiscount&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;calculateDiscountFromCustomerTransactionHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transactionHistory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;recalculatedOrderTotal&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;customerDiscount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;recalculatedOrderTotal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orderTotal&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orderTotal&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;customerDiscount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;itemsAreInStock&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;doubleCheckStock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orderItems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;itemsAreInStock&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;res&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Item(s) out of stock&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;await&lt;/span&gt; &lt;span class="nx"&gt;insertOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recalculatedOrderTotal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chargeCustomerPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recalculatedOrderTotal&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;orderTotal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentDetails&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sendConfirmationEmailToCustomer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dispatchConfirmationEmailJob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orderItems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Order complete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/order&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCustomerData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;processOrder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sendConfirmationEmailToCustomer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "middleware" here is anything that depends on the ExpressJS &lt;code&gt;req&lt;/code&gt;/&lt;code&gt;res&lt;/code&gt;/&lt;code&gt;next&lt;/code&gt; context. You can see that they are also chained where the route is defined:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/order&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCustomerData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;processOrder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sendConfirmationEmailToCustomer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: your controller will also usually depend on the Express context but it won't act like middleware in that it's chained from one call to the next in the route definition. &lt;br&gt;
The controller will usually have a single entrypoint - so one controller function per route. This isn't a hard-set rule but is generally a best practice.&lt;/p&gt;

&lt;p&gt;You usually see middleware in ExpressJS using &lt;code&gt;app.use(someMiddleware)&lt;/code&gt; to register the chain of middleware in order. And while this is not an example of that, I'd argue it's still coded essentially as middleware because of the hard dependency on the ExpressJS context.&lt;br&gt;
It's just in a different place in the code - in the route definition instead of the &lt;code&gt;index.js&lt;/code&gt; or &lt;code&gt;app.js&lt;/code&gt; part of your code where you see the &lt;code&gt;app.use(someMiddleware)&lt;/code&gt; setup.&lt;/p&gt;

&lt;p&gt;What is this code doing? A few things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;getCustomerData()&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;fetches user details (likely from a database)&lt;/li&gt;
&lt;li&gt;fetches the customer transaction history (also likely from a database)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;processOrder()&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;calculates any potential discount for the user&lt;/li&gt;
&lt;li&gt;checks the item(s) are in stock&lt;/li&gt;
&lt;li&gt;inserts the item order into the database&lt;/li&gt;
&lt;li&gt;charges the customer's credit card or other form of payment&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sendConfirmationEmailToCustomer()&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;send the user a confirmation email with their order details&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  What makes this a problem?
&lt;/h2&gt;

&lt;p&gt;The problem is not really what the code is doing but how, for the following reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;These three functions now depend on the request context. If you want to reuse them / use them in multiple places, every function that calls this &lt;em&gt;must&lt;/em&gt; have &lt;code&gt;req&lt;/code&gt;, &lt;code&gt;res&lt;/code&gt;, and &lt;code&gt;next&lt;/code&gt; (the Express "context").

&lt;ul&gt;
&lt;li&gt;You also have to assume sequence of calls and &lt;code&gt;next()&lt;/code&gt;, so even though they may be individual functions, they aren't reusable.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If you have to pass one value from one middleware function to the next, you have to use &lt;code&gt;res.locals&lt;/code&gt; to (when we could just return it and pass it via a function argument).&lt;/li&gt;
&lt;li&gt;It makes writing automated tests more difficult.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Request context dependency
&lt;/h2&gt;

&lt;p&gt;One of the biggest issues in my opinion is that these functions are not reusable. Because the function definition is now coupled via its arguments to &lt;code&gt;req&lt;/code&gt;, &lt;code&gt;res&lt;/code&gt;, and &lt;code&gt;next&lt;/code&gt;, and those are coupled to ExpressJS, you can't call them anywhere else in your code.&lt;br&gt;
Unless it's somewhere you have the ExpressJS context (more on this a bit further down).&lt;/p&gt;

&lt;p&gt;If these were just "regular" functions, the context would not matter. That is, if you could just pass in "agnostic" values/objects/arrays etc, then you could reuse them elsewhere in your code.&lt;br&gt;
Sure, the expected types and expected arguments matter but you can reuse a functions in ways that make sense for your application.&lt;br&gt;
You can call your utility functions in your service layer code or your database code, for example.&lt;br&gt;
And obviously the business logic still matters, i.e. you're not going to arbitrarily call functions.&lt;br&gt;
Similarly, you're not going to &lt;a href="https://coreycleary.me/should-one-express-controller-call-another"&gt;call controller functions from within another controller either&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But by not being totally coupled to the core Express objects/functions this gets us a long ways towards reusability. We should always strive for loose coupling when designing our software.&lt;/p&gt;

&lt;p&gt;You might be able to "reuse" that middleware elsewhere but &lt;em&gt;only&lt;/em&gt; as middleware and even then it might not be reusable.&lt;br&gt;
Consider a function that is supposed to end the request by calling &lt;code&gt;res.send(response)&lt;/code&gt;. You can't really reuse that (without changing the function definition), because it ends the request so you couldn't call it in the middle of your chain.&lt;br&gt;
And if you need to pass value(s) from one middleware function to the next, this pesudo-middleware reusability becomes even more difficult, as explained in the next section.&lt;/p&gt;
&lt;h2&gt;
  
  
  Passing values from one function to the next
&lt;/h2&gt;

&lt;p&gt;In our code above, &lt;code&gt;getCustomerData()&lt;/code&gt; calls &lt;code&gt;fetchCustomerTransactionHistory()&lt;/code&gt; and then needs to pass it to the next middleware function, &lt;code&gt;processOrder()&lt;/code&gt;. Because these functions get called in a chain, we need some way of passing that value to &lt;code&gt;processOrder()&lt;/code&gt;, since we have no intermediary variable to store the result in.&lt;/p&gt;

&lt;p&gt;You can do that thru &lt;code&gt;res.locals.transactionHistory = transactionHistory&lt;/code&gt; or by attaching a new property to the &lt;code&gt;res&lt;/code&gt; object arbitrarily, like &lt;code&gt;res.transactionHistory = transactionHistory&lt;/code&gt;.&lt;br&gt;
Any property added to &lt;code&gt;res.locals&lt;/code&gt; is only available for the lifecycle of the request, so when the request completes you can't access it again.&lt;/p&gt;

&lt;p&gt;This is much messier than if we could just call &lt;code&gt;getCustomerData()&lt;/code&gt;, store the result in a variable &lt;code&gt;customerData&lt;/code&gt; or whatever and then pass that to &lt;code&gt;processOrder()&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Also, this further reinforces that the order of middleware function calls matters when going about it this way. Because one function willy rely on previous &lt;code&gt;res.locals&lt;/code&gt; being set, the order of calls must stay the same.&lt;br&gt;
And if you want to change the value that gets passed, you inevitably have to change the implementation of more than one function, you can't just change the one function.&lt;/p&gt;

&lt;p&gt;While &lt;code&gt;res.locals&lt;/code&gt; is supported by ExpressJS, and you can of course set new properties on objects if you go the custom property on &lt;code&gt;res&lt;/code&gt; route, I don't recommend this unless it's something you absoluetly need to do as it can make troubleshooting mroe difficult.&lt;br&gt;
But anyways, it's best to avoid this altogether and have your utility/business/DB logic in non-middleware code.&lt;/p&gt;
&lt;h2&gt;
  
  
  Makes writing automated tests more difficult
&lt;/h2&gt;

&lt;p&gt;In order to write tests for this type of code, we now either need to stub &lt;code&gt;req&lt;/code&gt; and &lt;code&gt;res&lt;/code&gt; or we need to test this end-to-end using &lt;a href="https://coreycleary.me/using-supertest-to-avoid-manually-testing-your-endpoints"&gt;something like supertest&lt;/a&gt;.&lt;br&gt;
Endpoint/end-to-end tests are good to have, but these functions we want to test are individual/modular (or at least, should be modular/resuable) and should be able to be tested more as units.&lt;br&gt;
We shouldn't have to test them by spinning up a mock server, or by manually stubbing &lt;code&gt;req&lt;/code&gt; and &lt;code&gt;res&lt;/code&gt; - that's unnecessary complexity and work.&lt;br&gt;
And stubs for request and response objects can require more maintenance, tight coupling, etc.&lt;br&gt;
Not that stubs are bad - quite the opposite - and in the case of the functions above we'd likely want to stub some of the database and async calls.&lt;br&gt;
But in this case we don't want to have to write them for &lt;code&gt;req&lt;/code&gt;/&lt;code&gt;res&lt;/code&gt;. They would have to be more like mocks, where we define the &lt;code&gt;next()&lt;/code&gt; function and make assertions that it was called, stub the &lt;code&gt;res.send()&lt;/code&gt; function, which is an implementation we don't care about, etc.&lt;/p&gt;

&lt;p&gt;Instead if we could just break these pesudo-middlewares into resuable funtions without the ExpressJS context, we could test them by passing expected parameters to the functions which makes the test setup much easier.&lt;/p&gt;
&lt;h2&gt;
  
  
  What middleware is really for
&lt;/h2&gt;

&lt;p&gt;This topic could be a few blogposts by itself, but to get the general idea across middleware should be used for things that are common to all HTTP requests but don't contain business logic, and that need to be processed before everything else.&lt;/p&gt;

&lt;p&gt;Things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authorization/authentication&lt;/li&gt;
&lt;li&gt;Caching&lt;/li&gt;
&lt;li&gt;Session data&lt;/li&gt;
&lt;li&gt;CORS&lt;/li&gt;
&lt;li&gt;HTTP request logging (like &lt;code&gt;morgan&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of the above are their own category of API concern, separate conceptually from code that is concerned with fetching data from the database, sending out a user registration email, etc.&lt;br&gt;
Authorization and authentication need to happen before a user or client application access a service. That's something that is common to every (or most) requests.&lt;br&gt;
Caching, that is generally common to most requests, and is a utility that is a separate concern from business or view logic.&lt;br&gt;
Same with session data, same with CORS, same with request logging.&lt;/p&gt;

&lt;p&gt;While there are always exceptions to any rule, middleware almost always should not contain the core of your code that handles business logic, that handles code specific to your REST API, that is "further down" the chain of function calls.&lt;/p&gt;

&lt;p&gt;I like to think of business logic as the more "pure" form of logic. It is logic that shouldn't care about validating the request or handling anything framework-specific. It just handles algorithms/rules for processing data, storing of data, fetching data, formatting that data, etc. These rules are usually determined by business requirements.&lt;/p&gt;

&lt;p&gt;For example, if you had an API that returned how many users had been registered on your platform within the last X amount of days, the business logic here would be querying the database and doing any formatting of that data before it returned it to the controller, which returns the HTTP response.&lt;br&gt;
That logic won't handle caching or auth or session data. The middleware takes care of that.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to fix it
&lt;/h2&gt;

&lt;p&gt;If we make these "normal" functions rather than "middleware" functions coupled to ExpressJS, this is what they could look like. Of course you could refactor it further but this is the general idea:&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;getCustomerData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerId&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;customerDetails&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;fetchUserDetails&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerId&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;fetchCustomerTransactionHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerDetails&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processOrder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orderTotal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orderItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paymentDetails&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transactionHistory&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;customerDiscount&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;calculateDiscountFromCustomerTransactionHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transactionHistory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;recalculatedOrderTotal&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;customerDiscount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;recalculatedOrderTotal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;orderTotal&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orderTotal&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;customerDiscount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;itemsAreInStock&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;doubleCheckStock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orderItems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;itemsAreInStock&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="kc"&gt;null&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;insertOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orderTotal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orderItems&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;chargeCustomerPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recalculatedOrderTotal&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;orderTotal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paymentDetails&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sendConfirmationEmailToCustomer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orderItems&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;return&lt;/span&gt; &lt;span class="nx"&gt;dispatchConfirmationEmailJob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orderItems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: &lt;code&gt;sendConfirmationEmailToCustomer()&lt;/code&gt; is just a wrapper function basically. We could just call &lt;code&gt;dispatchConfirmationEmailJob()&lt;/code&gt; directly now, but I'm leaving it in to demonstrate the before and after.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now we have functions that are more reusable, not coupled to ExpressJS, and require less test setup to write tests for.&lt;/p&gt;

&lt;p&gt;You might call these functions in your controller like so:&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;// Controller&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createOrder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orderTotal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orderItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paymentDetails&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;

  &lt;span class="k"&gt;try&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;customerData&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;getCustomerData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;processOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orderTotal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orderItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paymentDetails&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;customerData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sendConfirmationEmailToCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orderItems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// or however you want to handle it&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Route&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/order&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createOrder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could of course use these individual functions elsewhere in your code though, now that they are reusable!&lt;/p&gt;

&lt;p&gt;Love JavaScript but still getting tripped up by local dev, architecture, testing, etc? I publish articles on JavaScript and Node every 1-2 weeks, so if you want to receive all new articles directly to your inbox, &lt;a href="https://www.coreycleary.me/about/"&gt;here's that link again&lt;/a&gt; to subscribe to my newsletter!&lt;/p&gt;

</description>
      <category>node</category>
      <category>express</category>
    </item>
    <item>
      <title>Simplifying local dev setup with Docker Compose</title>
      <dc:creator>Corey Cleary</dc:creator>
      <pubDate>Tue, 23 Jun 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/ccleary00/simplifying-local-dev-setup-with-docker-compose-m51</link>
      <guid>https://dev.to/ccleary00/simplifying-local-dev-setup-with-docker-compose-m51</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Originally published at &lt;a href="https://www.coreycleary.me/simplifying-local-dev-setup-with-docker-compose"&gt;coreycleary.me&lt;/a&gt;&lt;/strong&gt;. This is a cross-post from my content blog. I publish new content every week or two, and you can &lt;a href="https://www.coreycleary.me/about/"&gt;sign up to my newsletter&lt;/a&gt; if you'd like to receive my articles directly to your inbox! I also regularly send cheatsheets and other freebies.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you've ever had to deal with setting up a Node.js project in which you had to install a bunch of things - like MySQL/Postgres, Redis, etc. - and then run some setup scripts &lt;em&gt;just&lt;/em&gt; to be able to get the project running locally on your machine...&lt;/p&gt;

&lt;p&gt;...then you've likely experienced the pain of losing half a day - at least - to solely just getting set up.&lt;/p&gt;

&lt;p&gt;This is especially frustrating and anxiety-inducing if you're new to the team and want to start contributing right away, not waste time in the maze of steps you have to run, or waste time having to ask the team every 5 minutes how to get over the next install hurdle.&lt;/p&gt;

&lt;p&gt;What's worse is, as the project evolves, you might need to install more things, you might have more complex setup scripts, and (worst of all IMO) documentation for that setup might become out of date.&lt;/p&gt;

&lt;p&gt;Rather than having to install a bunch of things - or figure out what you need to install in the first place in the case of bad documentation - there's a much easier way that can get you up and running in as little as one or two commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Docker Compose
&lt;/h2&gt;

&lt;p&gt;Docker Compose gives us the ability to define install dependencies - like databases and other software - and run them within containers that your "main" code can interact with.&lt;/p&gt;

&lt;p&gt;In order to best explain how to use Compose - and how to convert an existing project with local install steps, scripts, etc - I'll use an example of a demo repo I wrote awhile back (that accompanied &lt;a href="https://www.coreycleary.me/understand-how-to-approach-designing-queues-in-node"&gt;this post on designing reliable queues&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;When I originally built that project, it was using "the old way", without Compose.&lt;/p&gt;

&lt;p&gt;But I recently re-wrote it to use Compose for creating Redis and Postgres containers, and to be able to run the tests against those containers (using Compose is also really good for having local test databases).&lt;/p&gt;

&lt;h2&gt;
  
  
  New world and old world
&lt;/h2&gt;

&lt;p&gt;First, let's look at &lt;a href="https://github.com/coreyc/queue-example/tree/without-docker-compose#queue-example"&gt;how the project was setup&lt;/a&gt; using "the old way":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;first install Homebrew&lt;/li&gt;
&lt;li&gt;then install Postgres&lt;/li&gt;
&lt;li&gt;then create a "root" database&lt;/li&gt;
&lt;li&gt;then define the schema&lt;/li&gt;
&lt;li&gt;then run a script to &lt;strong&gt;install&lt;/strong&gt; Redis&lt;/li&gt;
&lt;li&gt;then run a script to &lt;strong&gt;start&lt;/strong&gt; Postgres&lt;/li&gt;
&lt;li&gt;then run a script to &lt;strong&gt;start&lt;/strong&gt; Redis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's a lot of steps...&lt;/p&gt;

&lt;p&gt;Now, let's take a look at the steps involved using Docker Compose: &lt;code&gt;docker-compose up&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;...and that's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How were we able to accomplish this?
&lt;/h2&gt;

&lt;p&gt;Let's look at how I converted this project over to using Compose.&lt;/p&gt;

&lt;h4&gt;
  
  
  Postgres
&lt;/h4&gt;

&lt;p&gt;Instead of having to &lt;strong&gt;install Postgres&lt;/strong&gt; (and Homebrew, if you didn't already have it installed), and then &lt;strong&gt;define our database and schema&lt;/strong&gt;, using Compose that becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.7'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db_queue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:9.6.17&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db_queue&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;library&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;password&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./init.sql:/docker-entrypoint-initdb.d/init.sql&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db-data:/var/lib/postgresql/data&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5432:5432&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note that the above is contained in the docker-compose.yml file in the root of our project.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Second note: you'll need to have Docker installed on your machine in order to use Docker and Docker Compose&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We define our "install dependencies" within the &lt;code&gt;services&lt;/code&gt; section, in this case, Postgres.&lt;/p&gt;

&lt;p&gt;Then we define the basic environment variables that Postgres needs to startup the database. In the old world, where we were creating the database from the command line via psql, here we just define it under &lt;code&gt;POSTGRES_DB&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The service's &lt;code&gt;volumes&lt;/code&gt; section uses an initialize script (more on this in a second) and defines a database volume that gets "mounted" alongside the container. And we define that volume name using the "root" &lt;code&gt;volumes&lt;/code&gt; section, in this case using the name &lt;code&gt;db-data&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The reason we do that is so that if we bring down the "stack" using &lt;code&gt;docker-compose down&lt;/code&gt;, it won't clear the schema definitions + data stored in the database. Note, if we &lt;em&gt;want&lt;/em&gt; to delete that information and bring it &lt;em&gt;totally&lt;/em&gt; down, we can use the command &lt;code&gt;docker-compose down -v&lt;/code&gt;, using the &lt;code&gt;-v&lt;/code&gt; flag for "volume".&lt;/p&gt;

&lt;p&gt;The init.sql (used to create the table schema as the container boots up) still needs to be created, but instead of you having to manually define the schema, the SQL script just gets leveraged by Compose instead. In other words, its automatic rather than manual, and removes a step for us.&lt;/p&gt;

&lt;p&gt;And here's what that init.sql script looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;books&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book_number&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbn&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Lastly, we map the container port to the host machine port (the host machine being your machine itself), so that you can access the container &lt;em&gt;from&lt;/em&gt; your machine. That's done in the service's &lt;code&gt;ports&lt;/code&gt; section.&lt;/p&gt;

&lt;h4&gt;
  
  
  Redis
&lt;/h4&gt;

&lt;p&gt;For Redis, it's even simpler. In that same &lt;code&gt;services&lt;/code&gt; section, we do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;redis_queue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:5.0.6&lt;/span&gt;
  &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis_queue&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;6379:6379&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Define the Docker Redis image to use, give the container a name, and map the ports. Simple.&lt;/p&gt;

&lt;p&gt;Compared to the old world, where we had to &lt;a href="https://github.com/coreyc/queue-example/blob/without-docker-compose/setup-redis.sh"&gt;run a script&lt;/a&gt; to &lt;code&gt;wget&lt;/code&gt; to install Redis and build that code using &lt;code&gt;make&lt;/code&gt;, then start Redis using a &lt;strong&gt;separate&lt;/strong&gt; &lt;a href="https://github.com/coreyc/queue-example/blob/without-docker-compose/start-redis.sh"&gt;script&lt;/a&gt;, the Compose way is much easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leveraging the Compose containers
&lt;/h2&gt;

&lt;p&gt;Real quick, here's the entire &lt;code&gt;docker-compose.yml&lt;/code&gt; file in its entirety:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.7'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;redis_queue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:5.0.6&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis_queue&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;6379:6379&lt;/span&gt;
  &lt;span class="na"&gt;db_queue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:9.6.17&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db_queue&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;library&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;password&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./init.sql:/docker-entrypoint-initdb.d/init.sql&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db-data:/var/lib/postgresql/data&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5432:5432&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Like I mentioned before, all we need to do to start the "stack" is to run &lt;code&gt;docker-compose up&lt;/code&gt;, and Docker will use the Compose file and services defined therein to spin up the containers.&lt;/p&gt;

&lt;p&gt;Because we have the container ports mapped to the local machine, we can run the unit/integration tests using &lt;code&gt;npm test&lt;/code&gt; - nothing different we need to do.&lt;/p&gt;

&lt;p&gt;You can also run the code against the containers, not just the tests. Simple.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;If you're continuously bumping up against problems running your project locally, strongly consider using Docker Compose for this instead.&lt;/p&gt;

&lt;p&gt;It makes defining a local "stack" for local development a lot simpler and more headache-free then installing a bunch of stuff on your machine. And in this post we've really only scratched the surface of what you can do. It can make your developer life SO much easier.&lt;/p&gt;

&lt;p&gt;Love JavaScript but still getting tripped up by local dev, architecture, testing, etc? I publish articles on JavaScript and Node every 1-2 weeks, so if you want to receive all new articles directly to your inbox, &lt;a href="https://www.coreycleary.me/about/"&gt;here's that link again&lt;/a&gt; to subscribe to my newsletter!&lt;/p&gt;

</description>
      <category>node</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
