<?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: Ilya Pirozhenko</title>
    <description>The latest articles on DEV Community by Ilya Pirozhenko (@sochix).</description>
    <link>https://dev.to/sochix</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%2F149588%2F0253a4fb-2b69-451f-9324-7f956ab1d625.jpeg</url>
      <title>DEV Community: Ilya Pirozhenko</title>
      <link>https://dev.to/sochix</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sochix"/>
    <language>en</language>
    <item>
      <title>✨ The new way of testing REST API's</title>
      <dc:creator>Ilya Pirozhenko</dc:creator>
      <pubDate>Thu, 06 Jun 2019 16:08:52 +0000</pubDate>
      <link>https://dev.to/sochix/the-new-way-of-testing-rest-api-s-5gd8</link>
      <guid>https://dev.to/sochix/the-new-way-of-testing-rest-api-s-5gd8</guid>
      <description>&lt;p&gt;I’m a software engineer in a small company. In short, this means I do everything related to the software used in the business, from dev-ops and CI setup, to quality assurance. Most of my time is spent developing REST API’s for our company’s clients. The only problem I have is this: &lt;strong&gt;I don’t feel confident in the quality of my REST API’s&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I guess some people would recommend using TDD, make a test-coverage of 100% and you’re sorted. I’ve done all of that, but it will not rescue you in production environment. &lt;/p&gt;

&lt;p&gt;In my experience, unit tests and test-driven development work pretty well for small encapsulated segments of code. It works if you’re developing some library or part of code that performs math, for example. Generally speaking, the code that doesn’t depends on some external services. If you’re developing REST API’s, you’ll mainly rely on database or queue or cache services, e.g. an external service.&lt;/p&gt;

&lt;p&gt;Maybe smoke tests? Yes, they’ll alert you if a known problem occurs, but they won’t solve the problem completely.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fslao.io%2Fblog%2Fposts%2Fnew-way-of-testing%2Fimages%2Funit-vs-integration.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fslao.io%2Fblog%2Fposts%2Fnew-way-of-testing%2Fimages%2Funit-vs-integration.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What I actually need is some type of integration test. This is the only one you can trust as it works in the production environment and covers the whole process, from the client request to the client response. &lt;/p&gt;

&lt;h3&gt;
  
  
  What is the best way to carry out the integration test?
&lt;/h3&gt;

&lt;p&gt;The standard way is to run a script that will imitate the client’s behaviour and call the API’s methods in a specific order. This script runs on the “stage” or “production” environment under a special test account. After each run, you’ll also need to delete all the records created by the test account or recreate the whole database. Sounds difficult. You’ll also need to come up with test scenarios and describe them in code, as well as supporting these scenarios after each small change in the API’s interface. I think this approach is good for an enterprise level company with a dedicated QA team, but not for an indie developer. Indie developers need a more simple and efficient approach.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fslao.io%2Fblog%2Fposts%2Fnew-way-of-testing%2Fimages%2Fwork-smarter_hufd5bf8348c15ad5dff1ecdfbc57e388f_83784_600x0_resize_q75_box.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fslao.io%2Fblog%2Fposts%2Fnew-way-of-testing%2Fimages%2Fwork-smarter_hufd5bf8348c15ad5dff1ecdfbc57e388f_83784_600x0_resize_q75_box.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Maybe monitoring?
&lt;/h3&gt;

&lt;p&gt;After looking around, my first idea was to use some monitoring service to solve my problem. Actually, my teammate wrote a whole article about &lt;a href="https://slao.io/blog/posts/tools-for-monitoring/" rel="noopener noreferrer"&gt;Monitoring in 2019&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;For a time, we tried APMs (like NewRelic and ELK stack) and “pinging” services (like PingDom, Assertible or Checkly).&lt;/p&gt;

&lt;p&gt;The APM solutions have an agent that captures all the required data, but lacks the ability to quickly and easily define assertions. As proof, try to set up alerting in ELK stack for some unobvious case. I also found that NewRelic’s pricing policy is a bit too high for adding a batch of REST APIs.&lt;/p&gt;

&lt;p&gt;Alternatively, there are several “pinging” services that have absolutely wonderful ways of defining assertions for each and every method of REST API, but lack access to the client’s requests and responses.&lt;/p&gt;

&lt;p&gt;That’s why I came up with an idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What if I could capture each and every client request and corresponding response to my API with the help of an agent library (like an APM service)? But then let the developer define a set of assertions on each of the REST API methods (like a “pinging” service).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I call it the “indie hacker way” of doing an integration test for the whole API in just two or three clicks. &lt;/p&gt;

&lt;h3&gt;
  
  
  What are the pros?
&lt;/h3&gt;

&lt;p&gt;You don’t need to create test scenarios and then describe them in code, as the actions of your clients are recorded for you. And it would always be up to date because the client is always right :) I mean if a client can’t call your API in the proper order, then it probably means that your documentation sucks. &lt;/p&gt;

&lt;p&gt;Secondly, your test coverage will be around 100% because every request will go through assertions. You could argue that there would be methods that clients do not call and these would not be tested. Ok, that’s true, but do you need such methods? &lt;/p&gt;

&lt;p&gt;Another feature of "the indie hacker way" is the possibility of rate testing. For example, you can define an assertion for the minimum or maximum number of requests per second for each and every method of your API. If something goes over the threshold, you’ll be notified. &lt;/p&gt;

&lt;p&gt;Last but not least, you can assert response times for the customer and be confident that all requests are running under 100ms, for example.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are the cons?
&lt;/h3&gt;

&lt;p&gt;Yes, it’s not perfect. As I said above, if you have dedicated QA team then it’s better to go with integration tests in the usual sense, or configure a proper APM solution.&lt;/p&gt;

&lt;p&gt;First of all, you’re adding some computational load to your app as a result of capturing and sending each and every request. It’s not a big deal, but for some apps it may be a problem. &lt;/p&gt;

&lt;p&gt;Secondly, you can’t define any custom logic or dependency between methods in the test.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;Inspired by this idea, I quickly developed a web service to define assertions and receive notifications. It’s called SLAO. I use it on a daily basis and have added it to all my REST APIs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How does it work?&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install agent to capture requests (now only node.js/express is supported)&lt;/li&gt;
&lt;li&gt;Define assertions in simple web interface
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fslao.io%2Fblog%2Fposts%2Fnew-way-of-testing%2Fimages%2Fdefine-assertions_hub70c2bb6c93fe8ae4833ffbf3380fd42_69960_1800x0_resize_box_2.png"&gt;
&lt;/li&gt;
&lt;li&gt;Receive notification if something goes wrong
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fslao.io%2Fblog%2Fposts%2Fnew-way-of-testing%2Fimages%2Fnotification-example_hu90200949a7e0a98a1422322554b649eb_41925_1800x0_resize_box_2.png"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you like the idea, try it now on &lt;a href="https://slao.io?utm_campaign=new-way-of-testing" rel="noopener noreferrer"&gt;📊 SLAO&lt;/a&gt; and provide your feedback via chat or via email &lt;a href="//mailto:hello@slao.io"&gt;hello@slao.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally posted: &lt;a href="https://slao.io/blog/posts/new-way-of-testing/" rel="noopener noreferrer"&gt;https://slao.io/blog/posts/new-way-of-testing/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>restapi</category>
      <category>testing</category>
    </item>
    <item>
      <title>💯 Counting requests in your Node.js + Express application</title>
      <dc:creator>Ilya Pirozhenko</dc:creator>
      <pubDate>Thu, 18 Apr 2019 10:59:37 +0000</pubDate>
      <link>https://dev.to/sochix/counting-requests-in-your-node-js-express-application-4g8g</link>
      <guid>https://dev.to/sochix/counting-requests-in-your-node-js-express-application-4g8g</guid>
      <description>&lt;h2&gt;
  
  
  Foreword
&lt;/h2&gt;

&lt;p&gt;What is the purpose of counting requests to your web application?&lt;/p&gt;

&lt;p&gt;As I wrote in &lt;a href="%7B%%20link%20https://dev.to/sochix/four-key-metrics-of-rest-api-every-developer-should-worry-about-12ie%20%%7D"&gt;previous post&lt;/a&gt;, knowing the number of requests helps you answer next important business questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is anyone using my API? (if requests count is zero then it’s probably nobody)&lt;/li&gt;
&lt;li&gt;Is my API working? (if requests count is zero than it’s probably broken)&lt;/li&gt;
&lt;li&gt;Is my API under a DDoS attack ? (if requests count during the last hour is much higher than average than probably it is)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my case it was a business need - every request with status code “200” to a specific method of my REST API was adding a few cents to our company’s bank account. That’s why we decided to go into the matter.&lt;/p&gt;

&lt;p&gt;First of all, we explored all existing paid and free monitoring tools to make a long story short, none of them was a perfect fit.&lt;/p&gt;

&lt;p&gt;Secondly, I googled for npm libraries that count requests. I found that in 90% of cases developers count requests for requests rate limiting purposes. Rate limiting is another subject not related to my task in this case.&lt;/p&gt;

&lt;p&gt;Roughly speaking, my task was to count all the requests grouped by methods and status codes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing a middleware
&lt;/h2&gt;

&lt;p&gt;My web app is a REST API written on Node.js + Express. To simplify things here is the boilerplate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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="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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/&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;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="o"&gt;=&amp;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;200&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="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&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;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;Server started&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;The only one &lt;strong&gt;legit&lt;/strong&gt; method to capture all the requests in Express framework is to implement a middleware function and load it before any other handlers.&lt;/p&gt;

&lt;p&gt;Quote from the &lt;a href="https://expressjs.com/en/guide/writing-middleware.html"&gt;official Express.js docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Middleware functions are functions that have access to the request object (req),  the response object (res), and the next function in the application’s request-response cycle. The next function is a function in the Express router which, when  invoked, executes the middleware succeeding the current middleware. Middleware functions can perform the following tasks: Execute any code. Make changes to the request and the response objects. End the request-response cycle. Call the next middleware in the stack. If the current middleware function does not end the request-response cycle, it must call next() to pass control to the next middleware function. Otherwise, the request will be left hanging.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Just to understand what was happening in my app, I wrote this middleware function (see below) and made several requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&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;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="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="s2"&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;method&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;originalUrl&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;next&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 results are&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl http://localhost:3000/api
GET /api

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl http://localhost:3000/api/
GET /api/

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl http://localhost:3000/api?q&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;test
&lt;/span&gt;GET /api?q&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Ok, it’s working. Let’s add an ability to capture the response status code. Node.js has a default event that is fired when the response has been sent. More specifically, this event is emitted when the last segment of the response headers and body have been handed off to the operating system for transmission over the network. This hook is &lt;code&gt;res.on("finish")&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I should notice that not every request comes to the “finish” state, in real life client can close the connection before the response is sent. In this case Node.js emits only &lt;code&gt;res.on("close")&lt;/code&gt; event. To keep this post as simple as it can be I decided to ignore these types of requests.&lt;/p&gt;

&lt;p&gt;I modified my middleware to add the info about the response status code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&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;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="nx"&gt;res&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="s2"&gt;finish&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;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="s2"&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;method&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;originalUrl&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
   &lt;span class="p"&gt;})&lt;/span&gt;
   &lt;span class="nx"&gt;next&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 results are&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl http://localhost:3000/api
GET /api 200

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl http://localhost:3000/api/
GET /api/ 200

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl http://localhost:3000/api/?q&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;test
&lt;/span&gt;GET /api?q&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;test &lt;/span&gt;200
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We captured the http verb, the status code and the original url. As you can see the &lt;code&gt;originalUrl&lt;/code&gt; is different for each request but the handler path is always the same, it’s &lt;code&gt;api.get("/api")&lt;/code&gt;. Let’s capture the handler path instead of the &lt;code&gt;originalUrl&lt;/code&gt;. It’s a bit tricky.&lt;/p&gt;

&lt;p&gt;Express stores the data about the handler path in &lt;code&gt;req.route&lt;/code&gt; object. The object is filled with data only after the handler processed the request. As mentioned above the hook &lt;code&gt;res.on("finish")&lt;/code&gt; is called after all the handlers have been executed and the response has been sent. So we should inject a capturing code right in &lt;code&gt;res.on("finish")&lt;/code&gt;. Also we should keep in mind that there may be requests without a handler and we also should process them somehow.&lt;/p&gt;

&lt;p&gt;I wrote a small helper function to get the correct handler path&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;getRoute&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="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;route&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;route&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;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="c1"&gt;// check if the handler exist&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseUrl&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;baseUrl&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;baseUrl&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="c1"&gt;// adding the base url if the handler is a child of another handler&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;route&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;baseUrl&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&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="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;route&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unknown route&lt;/span&gt;&lt;span class="dl"&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 modified the middleware&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&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;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="nx"&gt;res&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="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;finish&lt;/span&gt;&lt;span class="err"&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;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="s2"&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;method&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;getRoute&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="s2"&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;statusCode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
   &lt;span class="p"&gt;})&lt;/span&gt;
   &lt;span class="nx"&gt;next&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;Now the results are consistent&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl http://localhost:3000/api
GET /api 200

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl http://localhost:3000/api/
GET /api 200

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl http://localhost:3000/api?q&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;test
&lt;/span&gt;GET /api 200

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl http://localhost:3000/
GET unknown route 404

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/
POST unknown route 404
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Data persistance
&lt;/h2&gt;

&lt;p&gt;The last but not least step is storing the captured data. I decided to store the data in next format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET /stats/ 200&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// "route name": "number of requests"&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET /api/ 200&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET unknown route 404&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST unknown route 404&lt;/span&gt;&lt;span class="dl"&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;For demo purposes we will store the statistics in a JSON file. Let’s add two helper methods to read and dump the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;fs&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;fs&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;FILE_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stats.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// read json object from file&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;readStats&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&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="nx"&gt;result&lt;/span&gt; &lt;span class="o"&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;parse&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="nx"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FILE_PATH&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;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// dump json object to file&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dumpStats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stats&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="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FILE_PATH&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;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;w+&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;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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Also, I modified the middleware to add persistance to the statistics&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&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;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="nx"&gt;res&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;finish&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;stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;readStats&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;event&lt;/span&gt; &lt;span class="o"&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&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;getRoute&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="s2"&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;statusCode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
        &lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;event&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="mi"&gt;1&lt;/span&gt;
        &lt;span class="nx"&gt;dumpStats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stats&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="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And created the &lt;code&gt;/stats&lt;/code&gt; method which returns the statistics.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/stats/&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;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="o"&gt;=&amp;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;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;readStats&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’re done, let’s make a few requests and check the stats.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET  http://localhost:3000/api/
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/api/
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl &lt;span class="nt"&gt;-X&lt;/span&gt; PUT http://localhost:3000/api/
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl http://localhost:3000/stats/
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"GET /api/ 200"&lt;/span&gt;: 1,
    &lt;span class="s2"&gt;"POST unknown route 404"&lt;/span&gt;: 1,
    &lt;span class="s2"&gt;"PUT unknown route 404"&lt;/span&gt;: 1
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As you can see, we have number of request for every route in our app. The whole code of this sample app can be found on &lt;a href="https://gist.github.com/sochix/475c9d6b32780c56db909c72bde0f150"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion and next steps
&lt;/h2&gt;

&lt;p&gt;In this post I described the basics of request counting. Keeping all your data in a file might not work properly in production, you should persist it in somewhere less volatile, such as a database. It could be Redis, InfluxDB, ElasticSearch, MongoDB, etc. Personally, our Node.js + Express monitoring service SLAO uses an InfluxDB cluster.&lt;/p&gt;

&lt;p&gt;Also, in the real world you’d like to have more detailed stats like requests per day/minute/hour along with an ability to view your data in a more convenient way than a JSON returned by your API. What’s more, a plain dashboard with statistics is useless unless you have an alerting attached to it. We’ll cover all these topics later on. &lt;/p&gt;




&lt;p&gt;I’m building 📊&lt;a href="https://slao.io"&gt;SLAO: Node.js + Express monitoring&lt;/a&gt;. Sign up for a free trial! &lt;br&gt;
Not sure yet? Just press 🧡 for this post.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://slao.io/blog/posts/counting-requests/"&gt;here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>📍 Four key metrics of REST API every developer should worry about</title>
      <dc:creator>Ilya Pirozhenko</dc:creator>
      <pubDate>Wed, 17 Apr 2019 17:42:39 +0000</pubDate>
      <link>https://dev.to/sochix/four-key-metrics-of-rest-api-every-developer-should-worry-about-12ie</link>
      <guid>https://dev.to/sochix/four-key-metrics-of-rest-api-every-developer-should-worry-about-12ie</guid>
      <description>&lt;p&gt;Hi, software engineer, did you think what happens with your app when it goes into the wild? Have you ever faced the situation when requests from your clients differ a lot from your test ones? Do you recall all your complaining clients who were sending requests with wrong &lt;em&gt;“Content-Type”&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fslao.io%2Fblog%2Fposts%2Fapi-key-metrics%2Fimages%2Fworked-fine-in-dev_hucfc2638715a3b651071091caacbc6b57_48138_550x0_resize_q75_box.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fslao.io%2Fblog%2Fposts%2Fapi-key-metrics%2Fimages%2Fworked-fine-in-dev_hucfc2638715a3b651071091caacbc6b57_48138_550x0_resize_q75_box.jpg" alt="worked-fine-in-dev"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First time I connected an Application Performance Monitoring (APM) to my REST API was like: &lt;em&gt;“Oooh what should I do with this volume of new data?”&lt;/em&gt;. What is bad and what is good? Should I worry about CPU load? Or maybe requests count?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fslao.io%2Fblog%2Fposts%2Fapi-key-metrics%2Fimages%2Ftoo-much-data_hu3830a4da48fe7384e77eb54d109e52ca_37416_450x0_resize_q75_box.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fslao.io%2Fblog%2Fposts%2Fapi-key-metrics%2Fimages%2Ftoo-much-data_hu3830a4da48fe7384e77eb54d109e52ca_37416_450x0_resize_q75_box.jpg" alt="too-much-data"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To answer these questions you should think about your REST API from the client’s side. Your client doesn’t care about the CPU load when your API runs smoothly, but he will definitely panic if your API responds slowly, this can happen even when the CPU usage is low. So let’s agree on that not all metrics are valuable. After my 10+ years of experience in developing REST API’s I came up with a rule: &lt;strong&gt;Every metric should be an answer to a simple question related to your business and your clients’ experience.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Good metrics are related to your business and affect clients, for example: “What amount of time a client should wait till he gets the response?” (e.g. request duration), “How many clients is my app serving right now?” (e.g. count of requests by clients), “Has a client received the response?” (e.g. connection state).&lt;/p&gt;

&lt;p&gt;On the other hand, here is the list of metrics that are pretty interesting for a developer, but don’t have a direct impact on your client: “What amount of memory does my application use?” (e.g. memory consumption), “What number of i/o ops does my app performs?” (e.g. i/o ops per minute). All these metrics are not strictly connected to your business, because who cares If your app consumes all the memory but still responds in 10 ms.&lt;/p&gt;

&lt;p&gt;Now you known the philosophy behind my key metrics, based on it I made a compilation of critical REST API metrics you should worry about as a developer:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Requests count
&lt;/h2&gt;

&lt;p&gt;Requests count is a simple but super useful metric. With this metric you can answer next important business questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is anyone using my API? (if requests count is zero then it’s probably nobody)&lt;/li&gt;
&lt;li&gt;Is my API working? (if requests count is zero than it’s probably broken)&lt;/li&gt;
&lt;li&gt;Is my API under a DDoS attack ? (if requests count during the last hour is much higher than average than probably it is)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Response codes
&lt;/h2&gt;

&lt;p&gt;When I was a beginner software developer I was wondering why we just can’t send code 200 for each request? Response status code is the easiest way to understand what happened with the request without reading and decoding the response body. So it’s important always to respond with a proper code in your REST API. With response status code you can answer next questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are clients properly calling my REST API methods? (e.g. no 4xx codes)&lt;/li&gt;
&lt;li&gt;Has my API ever crashed? (e.g. 5xx codes)&lt;/li&gt;
&lt;li&gt;Is my API running properly? (e.g. only 2xx codes)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Requests duration
&lt;/h2&gt;

&lt;p&gt;Gold standard for request duration is 200 ms, but let’s be honest it’s not true for the real world. In my experience some methods’ calls could take up to 60 seconds and it was still OK for the client. So you should negotiate the upper threshold of requests’ duration with your client, and fix its value in the SLA (Service Level Agreement). Armed with this metric you can answer next valuable business question:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is my API responding not slower than it should? (e.g. all responses are sent faster than the threshold time)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Is request finished
&lt;/h2&gt;

&lt;p&gt;This metric describes the connection state. It’s highly related to the requests duration metric. Having unfinished requests means that your client had closed the connection before the response arrived. Next question comes to my mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do my clients always receive the response? (If there are a lot of unfinished requests, then it’s definitely a reason to check this out)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fslao.io%2Fblog%2Fposts%2Fapi-key-metrics%2Fimages%2FIm-watching-you-meme_hud8fb541f266692141b55cea8c8c40c68_60239_200x0_resize_box.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fslao.io%2Fblog%2Fposts%2Fapi-key-metrics%2Fimages%2FIm-watching-you-meme_hud8fb541f266692141b55cea8c8c40c68_60239_200x0_resize_box.gif" alt="watching-you"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sum up
&lt;/h2&gt;

&lt;p&gt;Requests count, response time, status codes and connection states are the four simple metrics you should keep an eye on. I personally have been using these metrics for the last 3 years and have no plans to stop doing it.&lt;/p&gt;




&lt;p&gt;I’m building 📊&lt;a href="https://slao.io" rel="noopener noreferrer"&gt;SLAO: Node.js + Express monitoring&lt;/a&gt;. Sign up for a free trial! &lt;br&gt;
Not sure yet? Just press 🧡 for this post.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://slao.io/blog/posts/api-key-metrics/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>restapi</category>
      <category>monitoring</category>
      <category>metrics</category>
      <category>webdev</category>
    </item>
    <item>
      <title>📊 SLAO: Node.js + express monitoring tool</title>
      <dc:creator>Ilya Pirozhenko</dc:creator>
      <pubDate>Tue, 26 Mar 2019 14:08:45 +0000</pubDate>
      <link>https://dev.to/sochix/-slao-nodejs--express-monitoring-tool-48de</link>
      <guid>https://dev.to/sochix/-slao-nodejs--express-monitoring-tool-48de</guid>
      <description>&lt;p&gt;I've created a tool to monitor the performance and correctness of your REST API from a single dashboard. Unlike other overloaded APMs, SLAO is about the essentials. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fv8hmh6z7z0g942j9o5l5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fv8hmh6z7z0g942j9o5l5.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give it a try here 👉 &lt;a href="https://slao.io" rel="noopener noreferrer"&gt;https://slao.io&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>express</category>
      <category>monitoring</category>
    </item>
  </channel>
</rss>
