<?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>Forem: moesif</title>
    <description>The latest articles on Forem by moesif (@moesif).</description>
    <link>https://forem.com/moesif</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%2Forganization%2Fprofile_image%2F558%2F6c89d8d4-9200-4e10-b5f1-8ea02d68332c.png</url>
      <title>Forem: moesif</title>
      <link>https://forem.com/moesif</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/moesif"/>
    <language>en</language>
    <item>
      <title>11 Most Popular Tools for Logging and Monitoring API Calls</title>
      <dc:creator>Moesif Staff</dc:creator>
      <pubDate>Thu, 27 Mar 2025 17:58:44 +0000</pubDate>
      <link>https://forem.com/moesif/11-most-popular-tools-for-logging-and-monitoring-api-calls-5f06</link>
      <guid>https://forem.com/moesif/11-most-popular-tools-for-logging-and-monitoring-api-calls-5f06</guid>
      <description>&lt;p&gt;Monitoring and API logging, facilitated by various API monitoring tools, is no longer a nice-to-have – for many API providers, it has become a key part of ensuring growth and success in the ever-evolving API landscape. There are as many API monitoring tool providers as there are approaches to this topic, but we’ve gathered together a list of the top 11 in the market today. But first, let's take a look at a few things to consider.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is API Monitoring?
&lt;/h2&gt;

&lt;p&gt;API monitoring is the process of continuously tracking and analyzing the performance, functionality, and reliability of Application Programming Interfaces (APIs). It involves monitoring API calls, requests, and responses to ensure they are functioning correctly and efficiently. A well-structured API log includes timestamps, HTTP methods, endpoints accessed, request and response details, client information, and latency. By keeping a close eye on these interactions, developers can proactively identify and address issues, minimizing downtime and optimizing overall efficiency. Real-time insights into API performance and reliability enable quick troubleshooting and data-driven decision-making, ensuring that APIs deliver a seamless user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key API Metrics to Monitor
&lt;/h2&gt;

&lt;p&gt;Monitoring key API metrics is crucial for ensuring the performance, reliability, and security of APIs. Some of the essential metrics to keep an eye on include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Response Time&lt;/strong&gt;: The time taken by an API to process a request and return a response. Lower response times indicate better performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Error Rate&lt;/strong&gt;: The number of API calls resulting in non-200 status codes. A high error rate can signal underlying issues that need immediate attention. Error logs provide detailed information about exceptions and errors encountered during API operations, including error messages and stack traces.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Uptime&lt;/strong&gt;: The percentage of time a service is available. High uptime is critical for maintaining user trust and satisfaction.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CPU Usage&lt;/strong&gt;: The percentage of CPU resources used by an application. Monitoring CPU usage helps in identifying performance bottlenecks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Memory Usage&lt;/strong&gt;: The amount of memory used by an application. Efficient memory usage is vital for optimal performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Endpoint Performance&lt;/strong&gt;: The performance of specific API endpoints. Monitoring individual endpoints helps in pinpointing issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;API Calls per Minute&lt;/strong&gt;: The number of API calls made per minute. This metric helps in understanding the load on the API.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Log Data&lt;/strong&gt;: The data generated by API logs, including request and response data. Analyzing log data provides deep insights into API interactions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  API Logging and Monitoring Best Practices
&lt;/h2&gt;

&lt;p&gt;Effective API logging and monitoring are critical for maintaining robust API performance. Here are some best practices to follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Log All API Requests and Responses&lt;/strong&gt;: Ensure that every API request and response is logged for comprehensive tracking.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use a Centralized Logging System&lt;/strong&gt;: Collect and manage log data in a centralized system for easier analysis and troubleshooting.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monitor API Performance and Reliability in Real-Time&lt;/strong&gt;: Real-time monitoring helps in quickly identifying and addressing issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Set Up Alerts and Notifications for Critical Issues&lt;/strong&gt;: Configure alerts for critical issues to ensure prompt response and resolution.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use Log Data to Troubleshoot Issues and Optimize Performance&lt;/strong&gt;: Analyze log data to identify and fix performance bottlenecks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Implement Secure Logging Practices&lt;/strong&gt;: Protect sensitive data by following secure logging practices.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Regularly Review and Refine Logging and Monitoring Practices&lt;/strong&gt;: Continuously improve your logging and monitoring strategies to keep up with evolving needs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use Structured Logs&lt;/strong&gt;: Structured logs use a uniform format across all entries, facilitating easier querying and analysis.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-Time Monitoring and API Observability
&lt;/h2&gt;

&lt;p&gt;Real-time monitoring is essential for achieving API observability, providing instant insights into API performance and reliability. By analyzing log data and metrics in real-time, developers can quickly detect and respond to issues, optimizing API performance and reducing downtime. Real-time monitoring enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quick Issue Detection and Response&lt;/strong&gt;: Immediate identification of problems allows for prompt resolution.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Optimized API Performance and Reliability&lt;/strong&gt;: Continuous monitoring helps in maintaining high performance and reliability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Improved User Experience&lt;/strong&gt;: Minimizing downtime and performance issues leads to a better user experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data-Driven Decision Making&lt;/strong&gt;: Real-time insights support informed decision-making.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integration with Other Observability Tools&lt;/strong&gt;: Seamless integration with other tools enhances overall observability.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Choosing the Right API Monitoring Tool
&lt;/h2&gt;

&lt;p&gt;Selecting the right API monitoring tool is crucial for effective API monitoring. Consider the following factors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Comprehensive Visibility&lt;/strong&gt;: The tool should offer complete visibility into API performance and reliability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real-Time Monitoring&lt;/strong&gt;: Ensure the tool provides real-time monitoring and analysis of log data and metrics.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Customizable Alerts&lt;/strong&gt;: Look for tools that offer customizable alerts and notifications for critical issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integration&lt;/strong&gt;: The tool should integrate well with other observability tools and processes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt;: Choose a tool that can scale to meet growing API demands.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Security&lt;/strong&gt;: The tool should implement secure logging practices to protect sensitive data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User Experience&lt;/strong&gt;: A user-friendly interface is essential for easy monitoring and analysis.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By considering these factors, developers can choose the right API monitoring tool for their needs, ensuring effective API monitoring and observability.&lt;/p&gt;

&lt;h2&gt;
  
  
  1 - Moesif
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.moesif.com/features/api-logs?utm_campaign=Int-site&amp;amp;utm_source=blog&amp;amp;utm_medium=body-cta&amp;amp;utm_content=most-popular-tools-for-logging" rel="noopener noreferrer"&gt;Moesif&lt;/a&gt; is a powerful, feature-rich solution for API monitoring and tracking. Moesif also provides detailed insights into API transactions, capturing information about API requests and responses to ensure quality and performance. Rather than synthetic monitoring, Moesif provides real user monitoring, giving businesses the ability to dive into their API performance and API call data. By combining real-time event logging with product analytics (including API endpoint analysis, API testing, and API gateway integration), monetization systems (including quotas, governance, and metered billing), and alerts (both real-time and behavioral, as well as API security), Moesif promises to unlock better revenue performance, improve user retention, and generate unprecedented insight and awareness across your API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Provides powerful context and deep introspection through unlocked application-layer visibility&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easy to set up across a variety of systems and implementations&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Governance Rules only available on enterprise tier&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2 - Prometheus
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://prometheus.io/docs/prometheus/latest/management_api/" rel="noopener noreferrer"&gt;Prometheus&lt;/a&gt; is a widely-adopted open source monitoring solution. Prometheus also excels in infrastructure monitoring, helping to ensure system reliability and performance. It has generated quite a following due to its developer outreach backing, with ample documentation, blogs, video content, etc. That being said, Prometheus was designed to generate insights based on metrics, and thus is often considered more an “insights” solution than a “logging” one. Logs are stored locally and are then pointed to by Prometheus when an issue is detected per the metrics – for this reason, users typically use Prometheus in addition to an API logging solution for remote hosting, which makes Prometheus – at least for a subset of users – a half solution for monitoring API products.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Well-documented and easy to implement&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ample insights generated from small amounts of data&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Lack of native cloud logging support&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;May not be a complete solve for many users&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3 - Sematext
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://sematext.com/" rel="noopener noreferrer"&gt;Sematext&lt;/a&gt; offers a very popular solution in Sematext Logs. Sematext Logs supports the creation of detailed log entries, capturing essential data for troubleshooting and performance monitoring. Sematext positions their offering as a Log Management-as-a-Service model, supporting the exfiltration of API logs from containers, infrastructure, applications, etc. It facilitates this by supporting Syslog or the Elasticsearch API, which opens it to a wide range of implementations. Because it supports these solutions, you can use Sematext natively, bypassing more expensive options which require third party solutions for performance monitoring.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Wide support for systems and applications&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cheaper to use than many other solutions&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Requires using either Syslog or Elasticsearch, which can be limiting in some situations&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Requires buying into the “as-a-Service” dynamic&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4 - Papertrail
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.papertrail.com/" rel="noopener noreferrer"&gt;Papertrail&lt;/a&gt; is a logging solution from SolarWinds Cloud, a Software-as-a-Service solution. Each log entry in Papertrail captures critical elements like timestamps and request details, aiding in diagnosing issues. Papertrail allows for a large amount of customizability, and because it’s part of what is typically a holistic solution, it offers substantial cross-service and cross-database API logging that is often much slower and more expensive in other solutions. The drawback, of course, is that Papertrail works best as part of the SolarWinds Cloud service – and if you’re not using that service, your experience may not be as good as it could be with other open-source or free software solutions. If you currently are a SolarWinds customer, the best solution for your API monitor may be Papertrail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Highly customizable&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cross-service and cross-database&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Part of a suite of offerings that do better as part of SolarWinds Cloud&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Not robustly open-source&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5 - Checkly
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.checklyhq.com/" rel="noopener noreferrer"&gt;Checkly&lt;/a&gt; is a very new player in the logging software scene, but it brings a lot to the table. Checkly provides insights into key metrics such as response times and error rates, crucial for evaluating API performance. Beyond its powerful synthetic monitoring and API logging, it also allows for deep contextual inspection, diving into payload, responses, headers, and more. Latency, which refers to the time delay between API request and response time, is another critical metric Checkly helps to monitor. This amount of logging and API analytics provide a great amount of context and can improve your visibility quite significantly through application performance monitoring. Pricing is relatively simple and follows a freemium model – that being said, more complex environments might quickly run above the relatively modest 10,000 API calls in the free plan, and the custom plan may have higher costs compared to something that is self-hosted.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Relatively inexpensive&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Provides high context&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Very new player, so not as proven&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Limited freemium offering&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6 - Datadog
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.datadoghq.com/" rel="noopener noreferrer"&gt;Datadog&lt;/a&gt; bills itself as an observability platform, offering a relatively feature-complete solution for logging, website monitoring, application monitoring, and reporting for REST API-based products and beyond. Datadog's suite of monitoring tools provides real-time insights into API performance and reliability. It allows users to set specific metric-based rules to generate alerts across channels like Slack, SMS, email, etc. Datadog is relatively cheap, which is a big reason it has seen good adoption across the board – that being said, it doesn’t provide as much detail as some other solutions in the marketplace, making it a good generalist middle-of-the-road API management solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Feature-complete compared to others on this list&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Relatively cheap to deploy&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Limited detail compared to other solutions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7 - Sauce Labs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://saucelabs.com/" rel="noopener noreferrer"&gt;Sauce Labs&lt;/a&gt; used to be called API Fortress, and under that name, it generated a bit of a reputation as a cloud-based REST API monitoring solution. Setting up Sauce Labs for monitoring involves establishing secure connections to ensure data integrity and security. Sauce Labs continues this success by providing testing, monitoring, and reporting, but for those looking principally for API log tooling, Sauce Labs can seem a bit too feature-complete. Ultimately the use case for Sauce Labs is a bit of column A and column B – if you want to collect API logs for the purpose of generating reports and insights, Sauce Labs is a great solution. If you want to log to just log, this might be another in the column of “too good for what you need”.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Very mature in the market&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Widely adopted giving a huge user community&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Perhaps too mature for those looking only for logging&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A bit too general for specific use cases&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  8 - Amazon CloudWatch
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/cloudwatch/" rel="noopener noreferrer"&gt;Amazon CloudWatch&lt;/a&gt; brings the proven logging and monitoring approaches on offer from Amazon. Amazon CloudWatch is one of the most flexible API monitoring tools available, offering robust logging and monitoring capabilities. This solution is relatively robust, but the main selling point is in the pricing model – CloudWatch is very flexible, offering pay-as-you-go, which allows for easy scaling. That being said, the solution is tooled specifically for the AWS API gateway, which makes it a hard sell to anyone not utilizing AWS systems. Because of the usage based nature of CloudWatch’s payment structure understanding the performance metric that eat your budget can help guide the decision to use CloudWatch for uptime monitoring or synthetic API monitoring.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Relatively efficient and feature rich&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pay-as-you-go pricing makes it flexible&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Tooled for AWS specifically, which may exclude it from some stacks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  9 - AppDynamics
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.appdynamics.com/" rel="noopener noreferrer"&gt;AppDynamics&lt;/a&gt; is particularly powerful and is backed by the Cisco product offering. It offers real-time data visualization and analysis, and provides relatively robust solutions like resource waterfalls to show the impact of each element of the API service in the overall efficiency and user experience. AppDynamics provides comprehensive monitoring for any application programming interface, ensuring optimal performance and reliability. That being said, this is another offering that is run by a corporate entity, and its quite expensive – for many developers, this might be a huge negative that is difficult to overcome no matter how beautiful the visualizations are in practice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Backed by Cisco and its mature product offering&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Real-time data visualization&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Not open source and corporate in nature&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Quite expensive&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  10 - Uptrends
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.uptrends.com/" rel="noopener noreferrer"&gt;Uptrends&lt;/a&gt; offers a robust multi-step logging and API monitoring tool, selling itself as a logging and alerting system first and foremost. Uptrends offers “Private Checkpoints”, a solution that unlocks testing behind private networks and firewalls. Uptrends is very clearly leaning on its solution as a “problem resolution” one, with one area of the site boasting “Yesterday we detected 332k errors. How's your site doing?”. The main drawback here is that Uptrends is very clearly selling itself as an analysis tool rather than just an API log tool. For developers looking for logging – and just logging – Uptrends is too far down the monitoring path.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Robust logging and monitoring&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Works on private systems and networks&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Monitoring-heavy makes logging a second thought&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Provides ample analysis which may just be a cost increase vs. a utility increase for some stacks&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  11 - Graphite
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://graphiteapp.org/" rel="noopener noreferrer"&gt;Graphite&lt;/a&gt; is an open source monitoring and logging system that utilizes a push-based design architecture. What this means is that Graphite allows services to push their API logs into a component called Graphite Carbon, which is then stored in a database for later deep introspection and transformation. Prometheus, another open-source monitoring toolkit designed for cloud-native applications, is often used alongside Graphite for enhanced monitoring capabilities. Graphite's main selling point is its easy deployment through its native Synthesize product, an automated installation and configuration system which promises to get users up and running with minimal headache.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Open source and feature-rich&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easy deployment&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Depends on its own solutions - may be stack limiting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We hope this list has helped you see the offerings in the current marketplace. Please note that this list is not exhaustive – nonetheless, it provides a good overview of the most common solutions available today! Feel free to let us know if there are more solutions you'd love included in this list.&lt;/p&gt;

</description>
      <category>api</category>
      <category>monitoring</category>
      <category>tooling</category>
      <category>developers</category>
    </item>
    <item>
      <title>11 Most Popular Tools for Logging and Monitoring API Calls</title>
      <dc:creator>Moesif Staff</dc:creator>
      <pubDate>Thu, 27 Mar 2025 17:58:44 +0000</pubDate>
      <link>https://forem.com/moesif/11-most-popular-tools-for-logging-and-monitoring-api-calls-561e</link>
      <guid>https://forem.com/moesif/11-most-popular-tools-for-logging-and-monitoring-api-calls-561e</guid>
      <description>&lt;p&gt;Monitoring and API logging, facilitated by various API monitoring tools, is no longer a nice-to-have – for many API providers, it has become a key part of ensuring growth and success in the ever-evolving API landscape. There are as many API monitoring tool providers as there are approaches to this topic, but we’ve gathered together a list of the top 11 in the market today. But first, let's take a look at a few things to consider.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is API Monitoring?
&lt;/h2&gt;

&lt;p&gt;API monitoring is the process of continuously tracking and analyzing the performance, functionality, and reliability of Application Programming Interfaces (APIs). It involves monitoring API calls, requests, and responses to ensure they are functioning correctly and efficiently. A well-structured API log includes timestamps, HTTP methods, endpoints accessed, request and response details, client information, and latency. By keeping a close eye on these interactions, developers can proactively identify and address issues, minimizing downtime and optimizing overall efficiency. Real-time insights into API performance and reliability enable quick troubleshooting and data-driven decision-making, ensuring that APIs deliver a seamless user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key API Metrics to Monitor
&lt;/h2&gt;

&lt;p&gt;Monitoring key API metrics is crucial for ensuring the performance, reliability, and security of APIs. Some of the essential metrics to keep an eye on include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Response Time&lt;/strong&gt;: The time taken by an API to process a request and return a response. Lower response times indicate better performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Error Rate&lt;/strong&gt;: The number of API calls resulting in non-200 status codes. A high error rate can signal underlying issues that need immediate attention. Error logs provide detailed information about exceptions and errors encountered during API operations, including error messages and stack traces.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Uptime&lt;/strong&gt;: The percentage of time a service is available. High uptime is critical for maintaining user trust and satisfaction.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CPU Usage&lt;/strong&gt;: The percentage of CPU resources used by an application. Monitoring CPU usage helps in identifying performance bottlenecks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Memory Usage&lt;/strong&gt;: The amount of memory used by an application. Efficient memory usage is vital for optimal performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Endpoint Performance&lt;/strong&gt;: The performance of specific API endpoints. Monitoring individual endpoints helps in pinpointing issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;API Calls per Minute&lt;/strong&gt;: The number of API calls made per minute. This metric helps in understanding the load on the API.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Log Data&lt;/strong&gt;: The data generated by API logs, including request and response data. Analyzing log data provides deep insights into API interactions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  API Logging and Monitoring Best Practices
&lt;/h2&gt;

&lt;p&gt;Effective API logging and monitoring are critical for maintaining robust API performance. Here are some best practices to follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Log All API Requests and Responses&lt;/strong&gt;: Ensure that every API request and response is logged for comprehensive tracking.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use a Centralized Logging System&lt;/strong&gt;: Collect and manage log data in a centralized system for easier analysis and troubleshooting.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monitor API Performance and Reliability in Real-Time&lt;/strong&gt;: Real-time monitoring helps in quickly identifying and addressing issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Set Up Alerts and Notifications for Critical Issues&lt;/strong&gt;: Configure alerts for critical issues to ensure prompt response and resolution.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use Log Data to Troubleshoot Issues and Optimize Performance&lt;/strong&gt;: Analyze log data to identify and fix performance bottlenecks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Implement Secure Logging Practices&lt;/strong&gt;: Protect sensitive data by following secure logging practices.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Regularly Review and Refine Logging and Monitoring Practices&lt;/strong&gt;: Continuously improve your logging and monitoring strategies to keep up with evolving needs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use Structured Logs&lt;/strong&gt;: Structured logs use a uniform format across all entries, facilitating easier querying and analysis.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-Time Monitoring and API Observability
&lt;/h2&gt;

&lt;p&gt;Real-time monitoring is essential for achieving API observability, providing instant insights into API performance and reliability. By analyzing log data and metrics in real-time, developers can quickly detect and respond to issues, optimizing API performance and reducing downtime. Real-time monitoring enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quick Issue Detection and Response&lt;/strong&gt;: Immediate identification of problems allows for prompt resolution.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Optimized API Performance and Reliability&lt;/strong&gt;: Continuous monitoring helps in maintaining high performance and reliability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Improved User Experience&lt;/strong&gt;: Minimizing downtime and performance issues leads to a better user experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data-Driven Decision Making&lt;/strong&gt;: Real-time insights support informed decision-making.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integration with Other Observability Tools&lt;/strong&gt;: Seamless integration with other tools enhances overall observability.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Choosing the Right API Monitoring Tool
&lt;/h2&gt;

&lt;p&gt;Selecting the right API monitoring tool is crucial for effective API monitoring. Consider the following factors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Comprehensive Visibility&lt;/strong&gt;: The tool should offer complete visibility into API performance and reliability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real-Time Monitoring&lt;/strong&gt;: Ensure the tool provides real-time monitoring and analysis of log data and metrics.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Customizable Alerts&lt;/strong&gt;: Look for tools that offer customizable alerts and notifications for critical issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integration&lt;/strong&gt;: The tool should integrate well with other observability tools and processes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt;: Choose a tool that can scale to meet growing API demands.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Security&lt;/strong&gt;: The tool should implement secure logging practices to protect sensitive data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User Experience&lt;/strong&gt;: A user-friendly interface is essential for easy monitoring and analysis.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By considering these factors, developers can choose the right API monitoring tool for their needs, ensuring effective API monitoring and observability.&lt;/p&gt;

&lt;h2&gt;
  
  
  1 - Moesif
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.moesif.com/features/api-logs?utm_campaign=Int-site&amp;amp;utm_source=blog&amp;amp;utm_medium=body-cta&amp;amp;utm_content=most-popular-tools-for-logging" rel="noopener noreferrer"&gt;Moesif&lt;/a&gt; is a powerful, feature-rich solution for API monitoring and tracking. Moesif also provides detailed insights into API transactions, capturing information about API requests and responses to ensure quality and performance. Rather than synthetic monitoring, Moesif provides real user monitoring, giving businesses the ability to dive into their API performance and API call data. By combining real-time event logging with product analytics (including API endpoint analysis, API testing, and API gateway integration), monetization systems (including quotas, governance, and metered billing), and alerts (both real-time and behavioral, as well as API security), Moesif promises to unlock better revenue performance, improve user retention, and generate unprecedented insight and awareness across your API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Provides powerful context and deep introspection through unlocked application-layer visibility&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easy to set up across a variety of systems and implementations&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Governance Rules only available on enterprise tier&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2 - Prometheus
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://prometheus.io/docs/prometheus/latest/management_api/" rel="noopener noreferrer"&gt;Prometheus&lt;/a&gt; is a widely-adopted open source monitoring solution. Prometheus also excels in infrastructure monitoring, helping to ensure system reliability and performance. It has generated quite a following due to its developer outreach backing, with ample documentation, blogs, video content, etc. That being said, Prometheus was designed to generate insights based on metrics, and thus is often considered more an “insights” solution than a “logging” one. Logs are stored locally and are then pointed to by Prometheus when an issue is detected per the metrics – for this reason, users typically use Prometheus in addition to an API logging solution for remote hosting, which makes Prometheus – at least for a subset of users – a half solution for monitoring API products.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Well-documented and easy to implement&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ample insights generated from small amounts of data&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Lack of native cloud logging support&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;May not be a complete solve for many users&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3 - Sematext
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://sematext.com/" rel="noopener noreferrer"&gt;Sematext&lt;/a&gt; offers a very popular solution in Sematext Logs. Sematext Logs supports the creation of detailed log entries, capturing essential data for troubleshooting and performance monitoring. Sematext positions their offering as a Log Management-as-a-Service model, supporting the exfiltration of API logs from containers, infrastructure, applications, etc. It facilitates this by supporting Syslog or the Elasticsearch API, which opens it to a wide range of implementations. Because it supports these solutions, you can use Sematext natively, bypassing more expensive options which require third party solutions for performance monitoring.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Wide support for systems and applications&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cheaper to use than many other solutions&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Requires using either Syslog or Elasticsearch, which can be limiting in some situations&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Requires buying into the “as-a-Service” dynamic&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4 - Papertrail
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.papertrail.com/" rel="noopener noreferrer"&gt;Papertrail&lt;/a&gt; is a logging solution from SolarWinds Cloud, a Software-as-a-Service solution. Each log entry in Papertrail captures critical elements like timestamps and request details, aiding in diagnosing issues. Papertrail allows for a large amount of customizability, and because it’s part of what is typically a holistic solution, it offers substantial cross-service and cross-database API logging that is often much slower and more expensive in other solutions. The drawback, of course, is that Papertrail works best as part of the SolarWinds Cloud service – and if you’re not using that service, your experience may not be as good as it could be with other open-source or free software solutions. If you currently are a SolarWinds customer, the best solution for your API monitor may be Papertrail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Highly customizable&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cross-service and cross-database&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Part of a suite of offerings that do better as part of SolarWinds Cloud&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Not robustly open-source&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5 - Checkly
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.checklyhq.com/" rel="noopener noreferrer"&gt;Checkly&lt;/a&gt; is a very new player in the logging software scene, but it brings a lot to the table. Checkly provides insights into key metrics such as response times and error rates, crucial for evaluating API performance. Beyond its powerful synthetic monitoring and API logging, it also allows for deep contextual inspection, diving into payload, responses, headers, and more. Latency, which refers to the time delay between API request and response time, is another critical metric Checkly helps to monitor. This amount of logging and API analytics provide a great amount of context and can improve your visibility quite significantly through application performance monitoring. Pricing is relatively simple and follows a freemium model – that being said, more complex environments might quickly run above the relatively modest 10,000 API calls in the free plan, and the custom plan may have higher costs compared to something that is self-hosted.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Relatively inexpensive&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Provides high context&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Very new player, so not as proven&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Limited freemium offering&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6 - Datadog
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.datadoghq.com/" rel="noopener noreferrer"&gt;Datadog&lt;/a&gt; bills itself as an observability platform, offering a relatively feature-complete solution for logging, website monitoring, application monitoring, and reporting for REST API-based products and beyond. Datadog's suite of monitoring tools provides real-time insights into API performance and reliability. It allows users to set specific metric-based rules to generate alerts across channels like Slack, SMS, email, etc. Datadog is relatively cheap, which is a big reason it has seen good adoption across the board – that being said, it doesn’t provide as much detail as some other solutions in the marketplace, making it a good generalist middle-of-the-road API management solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Feature-complete compared to others on this list&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Relatively cheap to deploy&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Limited detail compared to other solutions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7 - Sauce Labs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://saucelabs.com/" rel="noopener noreferrer"&gt;Sauce Labs&lt;/a&gt; used to be called API Fortress, and under that name, it generated a bit of a reputation as a cloud-based REST API monitoring solution. Setting up Sauce Labs for monitoring involves establishing secure connections to ensure data integrity and security. Sauce Labs continues this success by providing testing, monitoring, and reporting, but for those looking principally for API log tooling, Sauce Labs can seem a bit too feature-complete. Ultimately the use case for Sauce Labs is a bit of column A and column B – if you want to collect API logs for the purpose of generating reports and insights, Sauce Labs is a great solution. If you want to log to just log, this might be another in the column of “too good for what you need”.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Very mature in the market&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Widely adopted giving a huge user community&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Perhaps too mature for those looking only for logging&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A bit too general for specific use cases&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  8 - Amazon CloudWatch
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/cloudwatch/" rel="noopener noreferrer"&gt;Amazon CloudWatch&lt;/a&gt; brings the proven logging and monitoring approaches on offer from Amazon. Amazon CloudWatch is one of the most flexible API monitoring tools available, offering robust logging and monitoring capabilities. This solution is relatively robust, but the main selling point is in the pricing model – CloudWatch is very flexible, offering pay-as-you-go, which allows for easy scaling. That being said, the solution is tooled specifically for the AWS API gateway, which makes it a hard sell to anyone not utilizing AWS systems. Because of the usage based nature of CloudWatch’s payment structure understanding the performance metric that eat your budget can help guide the decision to use CloudWatch for uptime monitoring or synthetic API monitoring.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Relatively efficient and feature rich&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pay-as-you-go pricing makes it flexible&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Tooled for AWS specifically, which may exclude it from some stacks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  9 - AppDynamics
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.appdynamics.com/" rel="noopener noreferrer"&gt;AppDynamics&lt;/a&gt; is particularly powerful and is backed by the Cisco product offering. It offers real-time data visualization and analysis, and provides relatively robust solutions like resource waterfalls to show the impact of each element of the API service in the overall efficiency and user experience. AppDynamics provides comprehensive monitoring for any application programming interface, ensuring optimal performance and reliability. That being said, this is another offering that is run by a corporate entity, and its quite expensive – for many developers, this might be a huge negative that is difficult to overcome no matter how beautiful the visualizations are in practice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Backed by Cisco and its mature product offering&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Real-time data visualization&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Not open source and corporate in nature&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Quite expensive&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  10 - Uptrends
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.uptrends.com/" rel="noopener noreferrer"&gt;Uptrends&lt;/a&gt; offers a robust multi-step logging and API monitoring tool, selling itself as a logging and alerting system first and foremost. Uptrends offers “Private Checkpoints”, a solution that unlocks testing behind private networks and firewalls. Uptrends is very clearly leaning on its solution as a “problem resolution” one, with one area of the site boasting “Yesterday we detected 332k errors. How's your site doing?”. The main drawback here is that Uptrends is very clearly selling itself as an analysis tool rather than just an API log tool. For developers looking for logging – and just logging – Uptrends is too far down the monitoring path.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Robust logging and monitoring&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Works on private systems and networks&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Monitoring-heavy makes logging a second thought&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Provides ample analysis which may just be a cost increase vs. a utility increase for some stacks&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  11 - Graphite
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://graphiteapp.org/" rel="noopener noreferrer"&gt;Graphite&lt;/a&gt; is an open source monitoring and logging system that utilizes a push-based design architecture. What this means is that Graphite allows services to push their API logs into a component called Graphite Carbon, which is then stored in a database for later deep introspection and transformation. Prometheus, another open-source monitoring toolkit designed for cloud-native applications, is often used alongside Graphite for enhanced monitoring capabilities. Graphite's main selling point is its easy deployment through its native Synthesize product, an automated installation and configuration system which promises to get users up and running with minimal headache.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Open source and feature-rich&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easy deployment&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Depends on its own solutions - may be stack limiting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We hope this list has helped you see the offerings in the current marketplace. Please note that this list is not exhaustive – nonetheless, it provides a good overview of the most common solutions available today! Feel free to let us know if there are more solutions you'd love included in this list.&lt;/p&gt;

</description>
      <category>api</category>
      <category>monitoring</category>
      <category>tooling</category>
      <category>developers</category>
    </item>
    <item>
      <title>How we built a Node.js Middleware to Log HTTP API Requests and Responses</title>
      <dc:creator>Moesif Staff</dc:creator>
      <pubDate>Thu, 27 Mar 2025 17:37:21 +0000</pubDate>
      <link>https://forem.com/moesif/how-we-built-a-nodejs-middleware-to-log-http-api-requests-and-responses-bh6</link>
      <guid>https://forem.com/moesif/how-we-built-a-nodejs-middleware-to-log-http-api-requests-and-responses-bh6</guid>
      <description>&lt;p&gt;There are many different runtimes and eco-systems used to build APIs and here at Moesif we try to make integration with them as simple as possible. We build many libraries that help with this integration, one of them is the &lt;a href="https://github.com/Moesif/moesif-nodejs" rel="noopener noreferrer"&gt;Moesif Node.js Middleware Library&lt;/a&gt;, or short moesif-nodejs. Middleware in Node.js is a function that has access to request and response objects and the next middleware function in the request-response cycle.&lt;/p&gt;

&lt;p&gt;Node.js handles requests asynchronously which can sometimes lead to problems, especially when we want to debug our systems or log what they are doing. API logs capture the intricate details of requests and responses exchanged between applications and servers, making them essential for understanding and troubleshooting such asynchronous behavior.&lt;/p&gt;

&lt;p&gt;In this article, we will go through the steps that went into building the moesif-nodejs library, the places where the relevant logging data is and how to hook into Node.js' http module to handle the data-gathering at the right time in the pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to API Logging
&lt;/h2&gt;

&lt;p&gt;API logging is the process of capturing and recording API interactions, including requests, responses, and errors. API logs provide valuable insights into API performance, security, and usage, enabling developers to troubleshoot issues, optimize performance, and ensure compliance with regulatory standards. In this section, we will explore the importance of API logging, its benefits, and the different types of API logs.&lt;/p&gt;

&lt;p&gt;API logs are essential for monitoring and maintaining the health, security, and efficiency of APIs. They provide a comprehensive view of API interactions, allowing developers to identify trends and patterns in API usage, detect security threats, and optimize performance. API logs can be categorized into different types, including access logs, error logs, security logs, and performance logs.&lt;/p&gt;

&lt;p&gt;Access logs record details about each API request, such as the HTTP method, URL, and response status code. Error logs capture information about any errors that occur during API calls, helping developers identify and fix issues. Security logs track failed authentication attempts and access control violations, providing insights into potential security threats. Performance logs measure the efficiency and response speed of APIs, recording metrics such as response time and throughput.&lt;/p&gt;

&lt;p&gt;By leveraging API logs, developers can gain a deeper understanding of how their APIs are being used, identify and resolve issues more quickly, and ensure that their APIs are secure and performant.&lt;/p&gt;

&lt;p&gt;For developers looking for an effortless way to monitor and analyze API traffic, integrating Moesif can significantly streamline this process. Moesif’s API analytics platform provides deep insights into API usage, identifying trends and performance bottlenecks while ensuring compliance with security policies. By leveraging tools like the Moesif Node.js Middleware, developers can log API requests and responses efficiently without modifying their core application logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Middleware in Node.js
&lt;/h2&gt;

&lt;p&gt;In Node.js, middleware is a function that can access the request object (req), the Middleware functions can perform tasks such as authentication, caching, and compression, and can be used to handle HTTP requests and responses. In this section, we will explore the concept of middleware in Node.js, its benefits, and how to create and use middleware functions.&lt;/p&gt;

&lt;p&gt;Middleware functions are essential in Node.js applications, as they enable developers to perform tasks that are common to multiple routes or endpoints. Middleware functions can be used to authenticate users, cache frequently accessed data, and compress responses to improve performance. Node.js provides several built-in middleware functions, including express.static, express.json, and express.urlencoded.&lt;/p&gt;

&lt;p&gt;The express.static middleware function serves static files, such as images, CSS files, and JavaScript files, from a specified directory. The express.json middleware function parses incoming requests with JSON payloads, making it easier to work with JSON data. The express.urlencoded middleware function parses incoming requests with URL-encoded payloads, which is useful for handling form submissions.&lt;/p&gt;

&lt;p&gt;By using middleware functions, developers can create modular and maintainable code, as each middleware function can be responsible for a specific task. This modularity makes it easier to manage and update the application, as changes to one middleware function do not affect others.&lt;/p&gt;

&lt;p&gt;For instance, Moesif’s middleware for Node.js seamlessly integrates into Express applications, allowing developers to capture request-response data for logging, debugging, and analytics. This approach not only simplifies tracking API performance but also enables real-time monitoring, anomaly detection, and automated alerts—essential for ensuring API reliability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Node.js' HTTP Module
&lt;/h2&gt;

&lt;p&gt;Node.js comes with an HTTP-server implementation out-of-the-box, while it isn’t used directly in most applications, it’s a good start to understand the basics of requests and responses.&lt;/p&gt;

&lt;p&gt;In a middleware setup, the current middleware function plays a crucial role in managing the request-response cycle. If the current middleware function does not complete the cycle, it must invoke the next middleware function to prevent requests from being left hanging.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing the Middleware Function
&lt;/h2&gt;

&lt;p&gt;Designing a middleware function involves several steps, including defining the function signature, accessing the request and response objects, and calling the next middleware function. In this section, we will explore the best practices for designing middleware functions, including how to handle errors, how to access and modify the request and response objects, and how to call the next middleware function.&lt;/p&gt;

&lt;p&gt;When designing a middleware function, it is essential to consider the function signature, which includes the request object (req), the response object (res), and the next middleware function (next). The middleware function should access and modify the request and response objects as needed, and call the next middleware function to pass control to the next function in the chain.&lt;/p&gt;

&lt;p&gt;To handle errors effectively, the middleware function should include error-handling logic. This can be done by checking for errors in the request and response objects and logging any errors that occur. Additionally, the middleware function should call the next middleware function with an error argument if an error is detected, allowing the error to be handled by error-handling middleware functions.&lt;/p&gt;

&lt;p&gt;Accessing and modifying the request and response objects is a common task in middleware functions. For example, a middleware function might add a custom header to the response object or modify the request object to include additional data. It is important to ensure that any modifications to the request and response objects do not interfere with other middleware functions or the final response.&lt;/p&gt;

&lt;p&gt;Calling the next middleware function is crucial for ensuring that the request-response cycle continues. The middleware function should call the next middleware function after completing its task, passing control to the next function in the chain. This allows multiple middleware functions to work together to handle a single request.&lt;/p&gt;

&lt;p&gt;By following these best practices, developers can design effective and efficient middleware functions that enhance the functionality and maintainability of their Node.js applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Logging of a GET Request
&lt;/h2&gt;

&lt;p&gt;The idea behind logging is that we write some kind of data in some persistent data store so we can review it later. To keep things simple, we will first write to stdout with console.log() Sometimes logging to stdout isn’t an option and we have to send the logging data to some other place. Like when running in a serverless environment, where the functions have no persistent storage. Let’s try a simple server that does nothing but sending empty responses for every request to illustrate how to get request data that can be logged. const http = require("http");&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;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8888&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we send a request to &lt;a href="http://localhost:8888/" rel="noopener noreferrer"&gt;http://localhost:8888&lt;/a&gt; we see a giant object being logged to stdout, it is full of implementation details and finding the important parts isn’t easy. These log details include various components such as timestamps, client information, request and response details, and security events, which are essential for troubleshooting issues, monitoring performance, and recognizing potential security threats.&lt;/p&gt;

&lt;p&gt;Let’s look at the Node.js documentation for &lt;a href="https://nodejs.org/dist/latest-v10.x/docs/api/http.html#http_class_http_incomingmessage" rel="noopener noreferrer"&gt;IncomingMessage&lt;/a&gt;, the class of which our requests is an object of.&lt;/p&gt;

&lt;p&gt;What information can we find here?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;headers&lt;/code&gt; and &lt;code&gt;rawHeaders&lt;/code&gt; (for invalid/duplicate headers)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;httpVersion&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;method&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;url&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;socket.remoteAddress&lt;/code&gt; (for the client IP)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This should be enough for GET requests since they usually don't have a body. Let's update our implementation.&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;rawHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;httpVersion&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="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;remoteFamily&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nx"&gt;rawHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;httpVersion&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="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;remoteFamily&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;url&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&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 output should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1562331336922&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rawHeaders"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"cache-control"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"no-cache"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Postman-Token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"dcd81e98-4f98-42a3-9e13-10c8401892b3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"User-Agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"PostmanRuntime/7.6.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Accept"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"*/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"localhost:8888"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"accept-encoding"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"gzip, deflate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Connection"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"keep alive"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"httpVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"remoteAddress"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"::1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"remoteFamily"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"IPv6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We only use specific parts of the request for our logging. It uses JSON as format so it’s a structured logging approach and has a timestamp so know we not only know what was requested by whom but also when the request started. Structured logging uses a uniform format across all log entries to facilitate easier querying and analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logging Processing Time
&lt;/h2&gt;

&lt;p&gt;If we wanted to add data about how long the request took to process, we need a way to check when it's finished. Performance logs measure the efficiency and response speed of APIs, recording metrics such as response time and throughput, which can be useful for such analysis.&lt;/p&gt;

&lt;p&gt;The request is done when we finished sending our response, so we have to check when a response.end() was called. In our example, this is rather simple, but sometimes these end-calls are done by other modules.&lt;/p&gt;

&lt;p&gt;For this, we can look at the docs of the &lt;a href="https://nodejs.org/dist/latest-v10.x/docs/api/http.html#http_class_http_serverresponse" rel="noopener noreferrer"&gt;ServerResponse&lt;/a&gt; class. It mentions a finish event that is fired when all the sever finished sending it's response. This doesn't mean the client received everything, but it's an indicator that our work is done.&lt;/p&gt;

&lt;p&gt;Let's update our code!&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;requestStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rawHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;httpVersion&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="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;remoteFamily&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;processingTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;requestStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;rawHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;httpVersion&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="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;remoteFamily&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;url&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="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;100&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 passed the processing of our request to a separate function to simulate an &lt;em&gt;other module&lt;/em&gt; that takes care of it. The processing takes place asynchronously, because of the &lt;code&gt;setTimeout&lt;/code&gt;, so synchronous logging wouldn't get the desired result, but the &lt;code&gt;finish&lt;/code&gt; event takes care of this by firing &lt;em&gt;after&lt;/em&gt; &lt;code&gt;response.end()&lt;/code&gt; was called.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logging the Body
&lt;/h2&gt;

&lt;p&gt;The request body is still not logged, which means POST, PUT and PATCH requests aren't 100% covered.&lt;/p&gt;

&lt;p&gt;To get the body into the logs too, we need a way to extract it from our request object.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://nodejs.org/dist/latest-v10.x/docs/api/http.html#http_class_http_incomingmessage" rel="noopener noreferrer"&gt;IncomingMessage&lt;/a&gt; class implements the &lt;a href="https://nodejs.org/dist/latest-v10.x/docs/api/stream.html#stream_class_stream_readable" rel="noopener noreferrer"&gt;ReadableStream&lt;/a&gt; interface. It uses the events of that interface to signal when body data from the client arrives.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;data&lt;/code&gt; event is fired when the server received new chunks of data from the client&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;end&lt;/code&gt; event is called when all data has been sent&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;error&lt;/code&gt; event is called when something goes wrong&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's update our code:&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;requestStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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;errorMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chunk&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;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;end&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;body&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="nf"&gt;concat&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;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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;errorMessage&lt;/span&gt; &lt;span class="o"&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;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rawHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;httpVersion&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="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;remoteFamily&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;processingTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;requestStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;rawHeaders&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;errorMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;httpVersion&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="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;remoteFamily&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;url&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="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way we log an additional error message when something goes wrong and add the body content to the logging.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Caution: The body can be very big and/or binary, so validation checks are needed, otherwise the amount of data or the encoding can mess up our logs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Logging Response Data
&lt;/h2&gt;

&lt;p&gt;Now that we got the requests down, the next step is the logging of our responses.&lt;/p&gt;

&lt;p&gt;We already listen to the &lt;code&gt;finish&lt;/code&gt; event of our response, so we have a pretty safe way to get all the data. We just have to extract what the response object holds.&lt;/p&gt;

&lt;p&gt;Let's look at the docs for the &lt;a href="https://nodejs.org/dist/latest-v10.x/docs/api/http.html#http_class_http_serverresponse" rel="noopener noreferrer"&gt;ServerResponse&lt;/a&gt; class to find out what it offers us.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;statusCode&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;statusMessage&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getHeaders()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's add it to our code.&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;requestStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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;errorMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chunk&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;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;end&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;body&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="nf"&gt;concat&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="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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;errorMessage&lt;/span&gt; &lt;span class="o"&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;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rawHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;httpVersion&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="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;remoteFamily&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;socket&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;statusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;statusMessage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&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;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHeaders&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;processingTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;requestStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;rawHeaders&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;errorMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;httpVersion&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="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;remoteFamily&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;statusMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;headers&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="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Handling Response Errors and Client Aborts
&lt;/h2&gt;

&lt;p&gt;At the moment we only log when the response &lt;code&gt;finish&lt;/code&gt; event is fired, this isn't the case if something goes wrong in response or if the client aborts the request.&lt;/p&gt;

&lt;p&gt;For these two cases, we need to create additional handlers.&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;requestStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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;body&lt;/span&gt; &lt;span class="o"&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;requestErrorMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getChunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&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;assembleBody&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="nx"&gt;body&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="nf"&gt;concat&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="nf"&gt;toString&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;getError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&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;requestErrorMessage&lt;/span&gt; &lt;span class="o"&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;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getChunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;assembleBody&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getError&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;logClose&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="nf"&gt;removeHandlers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Client aborted.&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;logError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&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;removeHandlers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&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;logFinish&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="nf"&gt;removeHandlers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestErrorMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;close&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logClose&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="nx"&gt;logFinish&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;removeHandlers&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="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getChunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;assembleBody&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logClose&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&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="nx"&gt;logFinish&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;errorMessage&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;rawHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;httpVersion&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="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;remoteFamily&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;socket&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;statusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;statusMessage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&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;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHeaders&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;processingTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;requestStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;rawHeaders&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;errorMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;httpVersion&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="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;remoteFamily&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;statusMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;headers&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;Now, we also log errors and aborts.&lt;/p&gt;

&lt;p&gt;The logging handlers are also removed when the response finished and all the logging is moved to an extra function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logging to an external API
&lt;/h2&gt;

&lt;p&gt;At the moment the script only writes its logs to the console and in many cases, this is enough because operating systems allow other programs to capture the stdout and do their thing with it, like writing into a file or sending it to a third-party API like Moesif, for example. API logs are vital tools for monitoring the health of APIs, optimizing performance, and ensuring compliance with regulatory standards.&lt;/p&gt;

&lt;p&gt;In some environments, this isn't possible, but since we gathered all information into one place, we can replace the call to console.log with a third-party function.&lt;/p&gt;

&lt;p&gt;Let's refactor the code so it resembles a library and logs to some external service.&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;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;loggingLibrary&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;XYZ&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loggingLibray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&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;loggingApiHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bearer &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiKey&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;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;errorMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestStart&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;rawHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;httpVersion&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="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;remoteFamily&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;socket&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;statusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;statusMessage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&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;responseHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHeaders&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.org/logging-endpoint&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;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;loggingApiHeaders&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;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;requestStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;processingTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;requestStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;rawHeaders&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;errorMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;httpVersion&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="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;remoteFamily&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;statusMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;responseHeaders&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;requestStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// ========== REQUEST HANLDING ==========&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&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;requestErrorMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getChunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&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;assembleBody&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="nx"&gt;body&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="nf"&gt;concat&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="nf"&gt;toString&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;getError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&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;requestErrorMessage&lt;/span&gt; &lt;span class="o"&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;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getChunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;assembleBody&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// ========== RESPONSE HANLDING ==========&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logClose&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="nf"&gt;removeHandlers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Client aborted.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestStart&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;logError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&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;removeHandlers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestStart&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;logFinish&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="nf"&gt;removeHandlers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestErrorMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestStart&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;close&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logClose&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="nx"&gt;logFinish&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// ========== CLEANUP ==========&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;removeHandlers&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="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getChunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;assembleBody&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logClose&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&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="nx"&gt;logFinish&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;With these changes, we can now use our logging implementation as we would use &lt;a href="https://github.com/Moesif/moesif-nodejs" rel="noopener noreferrer"&gt;moesif-nodejs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;loggingLibrary&lt;/code&gt; function takes an API-key as configuration and returns the actual logging-function that will send the log-data to a logging service via HTTP. The logging-function itself takes a &lt;code&gt;request&lt;/code&gt; and &lt;code&gt;response&lt;/code&gt; object.&lt;/p&gt;

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

&lt;p&gt;With these changes, we can now use our logging implementation as we would use &lt;a href="https://github.com/Moesif/moesif-nodejs" rel="noopener noreferrer"&gt;moesif-nodejs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The loggingLibrary function takes an API-key as configuration and returns the actual logging-function that will send the log-data to a logging service via HTTP. The logging-function itself takes a requestresponse object.&lt;/p&gt;

&lt;p&gt;Here are a few:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/trentm/node-bunyan" rel="noopener noreferrer"&gt;Bunyan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/visionmedia/debug" rel="noopener noreferrer"&gt;Debug&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/log4js-node/log4js-node" rel="noopener noreferrer"&gt;Log4js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/expressjs/morgan" rel="noopener noreferrer"&gt;Morgan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/npm/npmlog" rel="noopener noreferrer"&gt;Npmlog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/winstonjs/winston" rel="noopener noreferrer"&gt;Winston&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For developers who want deeper visibility into API traffic with minimal setup, &lt;a href="https://www.moesif.com/wrap?onboard=true&amp;amp;utm_source=devto&amp;amp;utm_medium=bottom-cta&amp;amp;utm_content=how-we-built-nodejs-middleware" rel="noopener noreferrer"&gt;Moesif&lt;/a&gt; offers a seamless solution. Unlike traditional logging libraries, Moesif provides real-time API analytics, retention tracking, and security monitoring, making it an essential tool for scaling and optimizing API-driven applications.&lt;/p&gt;

</description>
      <category>node</category>
      <category>middleware</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How we built a Node.js Middleware to Log HTTP API Requests and Responses</title>
      <dc:creator>Moesif Staff</dc:creator>
      <pubDate>Thu, 27 Mar 2025 17:37:21 +0000</pubDate>
      <link>https://forem.com/moesif/how-we-built-a-nodejs-middleware-to-log-http-api-requests-and-responses-356f</link>
      <guid>https://forem.com/moesif/how-we-built-a-nodejs-middleware-to-log-http-api-requests-and-responses-356f</guid>
      <description>&lt;p&gt;There are many different runtimes and eco-systems used to build APIs and here at Moesif we try to make integration with them as simple as possible. We build many libraries that help with this integration, one of them is the &lt;a href="https://github.com/Moesif/moesif-nodejs" rel="noopener noreferrer"&gt;Moesif Node.js Middleware Library&lt;/a&gt;, or short moesif-nodejs. Middleware in Node.js is a function that has access to request and response objects and the next middleware function in the request-response cycle.&lt;/p&gt;

&lt;p&gt;Node.js handles requests asynchronously which can sometimes lead to problems, especially when we want to debug our systems or log what they are doing. API logs capture the intricate details of requests and responses exchanged between applications and servers, making them essential for understanding and troubleshooting such asynchronous behavior.&lt;/p&gt;

&lt;p&gt;In this article, we will go through the steps that went into building the moesif-nodejs library, the places where the relevant logging data is and how to hook into Node.js' http module to handle the data-gathering at the right time in the pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to API Logging
&lt;/h2&gt;

&lt;p&gt;API logging is the process of capturing and recording API interactions, including requests, responses, and errors. API logs provide valuable insights into API performance, security, and usage, enabling developers to troubleshoot issues, optimize performance, and ensure compliance with regulatory standards. In this section, we will explore the importance of API logging, its benefits, and the different types of API logs.&lt;/p&gt;

&lt;p&gt;API logs are essential for monitoring and maintaining the health, security, and efficiency of APIs. They provide a comprehensive view of API interactions, allowing developers to identify trends and patterns in API usage, detect security threats, and optimize performance. API logs can be categorized into different types, including access logs, error logs, security logs, and performance logs.&lt;/p&gt;

&lt;p&gt;Access logs record details about each API request, such as the HTTP method, URL, and response status code. Error logs capture information about any errors that occur during API calls, helping developers identify and fix issues. Security logs track failed authentication attempts and access control violations, providing insights into potential security threats. Performance logs measure the efficiency and response speed of APIs, recording metrics such as response time and throughput.&lt;/p&gt;

&lt;p&gt;By leveraging API logs, developers can gain a deeper understanding of how their APIs are being used, identify and resolve issues more quickly, and ensure that their APIs are secure and performant.&lt;/p&gt;

&lt;p&gt;For developers looking for an effortless way to monitor and analyze API traffic, integrating Moesif can significantly streamline this process. Moesif’s API analytics platform provides deep insights into API usage, identifying trends and performance bottlenecks while ensuring compliance with security policies. By leveraging tools like the Moesif Node.js Middleware, developers can log API requests and responses efficiently without modifying their core application logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Middleware in Node.js
&lt;/h2&gt;

&lt;p&gt;In Node.js, middleware is a function that can access the request object (req), the Middleware functions can perform tasks such as authentication, caching, and compression, and can be used to handle HTTP requests and responses. In this section, we will explore the concept of middleware in Node.js, its benefits, and how to create and use middleware functions.&lt;/p&gt;

&lt;p&gt;Middleware functions are essential in Node.js applications, as they enable developers to perform tasks that are common to multiple routes or endpoints. Middleware functions can be used to authenticate users, cache frequently accessed data, and compress responses to improve performance. Node.js provides several built-in middleware functions, including express.static, express.json, and express.urlencoded.&lt;/p&gt;

&lt;p&gt;The express.static middleware function serves static files, such as images, CSS files, and JavaScript files, from a specified directory. The express.json middleware function parses incoming requests with JSON payloads, making it easier to work with JSON data. The express.urlencoded middleware function parses incoming requests with URL-encoded payloads, which is useful for handling form submissions.&lt;/p&gt;

&lt;p&gt;By using middleware functions, developers can create modular and maintainable code, as each middleware function can be responsible for a specific task. This modularity makes it easier to manage and update the application, as changes to one middleware function do not affect others.&lt;/p&gt;

&lt;p&gt;For instance, Moesif’s middleware for Node.js seamlessly integrates into Express applications, allowing developers to capture request-response data for logging, debugging, and analytics. This approach not only simplifies tracking API performance but also enables real-time monitoring, anomaly detection, and automated alerts—essential for ensuring API reliability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Node.js' HTTP Module
&lt;/h2&gt;

&lt;p&gt;Node.js comes with an HTTP-server implementation out-of-the-box, while it isn’t used directly in most applications, it’s a good start to understand the basics of requests and responses.&lt;/p&gt;

&lt;p&gt;In a middleware setup, the current middleware function plays a crucial role in managing the request-response cycle. If the current middleware function does not complete the cycle, it must invoke the next middleware function to prevent requests from being left hanging.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing the Middleware Function
&lt;/h2&gt;

&lt;p&gt;Designing a middleware function involves several steps, including defining the function signature, accessing the request and response objects, and calling the next middleware function. In this section, we will explore the best practices for designing middleware functions, including how to handle errors, how to access and modify the request and response objects, and how to call the next middleware function.&lt;/p&gt;

&lt;p&gt;When designing a middleware function, it is essential to consider the function signature, which includes the request object (req), the response object (res), and the next middleware function (next). The middleware function should access and modify the request and response objects as needed, and call the next middleware function to pass control to the next function in the chain.&lt;/p&gt;

&lt;p&gt;To handle errors effectively, the middleware function should include error-handling logic. This can be done by checking for errors in the request and response objects and logging any errors that occur. Additionally, the middleware function should call the next middleware function with an error argument if an error is detected, allowing the error to be handled by error-handling middleware functions.&lt;/p&gt;

&lt;p&gt;Accessing and modifying the request and response objects is a common task in middleware functions. For example, a middleware function might add a custom header to the response object or modify the request object to include additional data. It is important to ensure that any modifications to the request and response objects do not interfere with other middleware functions or the final response.&lt;/p&gt;

&lt;p&gt;Calling the next middleware function is crucial for ensuring that the request-response cycle continues. The middleware function should call the next middleware function after completing its task, passing control to the next function in the chain. This allows multiple middleware functions to work together to handle a single request.&lt;/p&gt;

&lt;p&gt;By following these best practices, developers can design effective and efficient middleware functions that enhance the functionality and maintainability of their Node.js applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Logging of a GET Request
&lt;/h2&gt;

&lt;p&gt;The idea behind logging is that we write some kind of data in some persistent data store so we can review it later. To keep things simple, we will first write to stdout with console.log() Sometimes logging to stdout isn’t an option and we have to send the logging data to some other place. Like when running in a serverless environment, where the functions have no persistent storage. Let’s try a simple server that does nothing but sending empty responses for every request to illustrate how to get request data that can be logged. const http = require("http");&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;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8888&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we send a request to &lt;a href="http://localhost:8888/" rel="noopener noreferrer"&gt;http://localhost:8888&lt;/a&gt; we see a giant object being logged to stdout, it is full of implementation details and finding the important parts isn’t easy. These log details include various components such as timestamps, client information, request and response details, and security events, which are essential for troubleshooting issues, monitoring performance, and recognizing potential security threats.&lt;/p&gt;

&lt;p&gt;Let’s look at the Node.js documentation for &lt;a href="https://nodejs.org/dist/latest-v10.x/docs/api/http.html#http_class_http_incomingmessage" rel="noopener noreferrer"&gt;IncomingMessage&lt;/a&gt;, the class of which our requests is an object of.&lt;/p&gt;

&lt;p&gt;What information can we find here?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;headers&lt;/code&gt; and &lt;code&gt;rawHeaders&lt;/code&gt; (for invalid/duplicate headers)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;httpVersion&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;method&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;url&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;socket.remoteAddress&lt;/code&gt; (for the client IP)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This should be enough for GET requests since they usually don't have a body. Let's update our implementation.&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;rawHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;httpVersion&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="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;remoteFamily&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nx"&gt;rawHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;httpVersion&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="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;remoteFamily&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;url&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&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 output should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1562331336922&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rawHeaders"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"cache-control"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"no-cache"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Postman-Token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"dcd81e98-4f98-42a3-9e13-10c8401892b3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"User-Agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"PostmanRuntime/7.6.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Accept"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"*/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"localhost:8888"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"accept-encoding"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"gzip, deflate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Connection"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"keep alive"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"httpVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"remoteAddress"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"::1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"remoteFamily"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"IPv6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We only use specific parts of the request for our logging. It uses JSON as format so it’s a structured logging approach and has a timestamp so know we not only know what was requested by whom but also when the request started. Structured logging uses a uniform format across all log entries to facilitate easier querying and analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logging Processing Time
&lt;/h2&gt;

&lt;p&gt;If we wanted to add data about how long the request took to process, we need a way to check when it's finished. Performance logs measure the efficiency and response speed of APIs, recording metrics such as response time and throughput, which can be useful for such analysis.&lt;/p&gt;

&lt;p&gt;The request is done when we finished sending our response, so we have to check when a response.end() was called. In our example, this is rather simple, but sometimes these end-calls are done by other modules.&lt;/p&gt;

&lt;p&gt;For this, we can look at the docs of the &lt;a href="https://nodejs.org/dist/latest-v10.x/docs/api/http.html#http_class_http_serverresponse" rel="noopener noreferrer"&gt;ServerResponse&lt;/a&gt; class. It mentions a finish event that is fired when all the sever finished sending it's response. This doesn't mean the client received everything, but it's an indicator that our work is done.&lt;/p&gt;

&lt;p&gt;Let's update our code!&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;requestStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rawHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;httpVersion&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="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;remoteFamily&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;processingTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;requestStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;rawHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;httpVersion&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="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;remoteFamily&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;url&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="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;100&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 passed the processing of our request to a separate function to simulate an &lt;em&gt;other module&lt;/em&gt; that takes care of it. The processing takes place asynchronously, because of the &lt;code&gt;setTimeout&lt;/code&gt;, so synchronous logging wouldn't get the desired result, but the &lt;code&gt;finish&lt;/code&gt; event takes care of this by firing &lt;em&gt;after&lt;/em&gt; &lt;code&gt;response.end()&lt;/code&gt; was called.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logging the Body
&lt;/h2&gt;

&lt;p&gt;The request body is still not logged, which means POST, PUT and PATCH requests aren't 100% covered.&lt;/p&gt;

&lt;p&gt;To get the body into the logs too, we need a way to extract it from our request object.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://nodejs.org/dist/latest-v10.x/docs/api/http.html#http_class_http_incomingmessage" rel="noopener noreferrer"&gt;IncomingMessage&lt;/a&gt; class implements the &lt;a href="https://nodejs.org/dist/latest-v10.x/docs/api/stream.html#stream_class_stream_readable" rel="noopener noreferrer"&gt;ReadableStream&lt;/a&gt; interface. It uses the events of that interface to signal when body data from the client arrives.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;data&lt;/code&gt; event is fired when the server received new chunks of data from the client&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;end&lt;/code&gt; event is called when all data has been sent&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;error&lt;/code&gt; event is called when something goes wrong&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's update our code:&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;requestStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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;errorMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chunk&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;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;end&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;body&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="nf"&gt;concat&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;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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;errorMessage&lt;/span&gt; &lt;span class="o"&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;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rawHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;httpVersion&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="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;remoteFamily&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;processingTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;requestStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;rawHeaders&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;errorMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;httpVersion&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="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;remoteFamily&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;url&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="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way we log an additional error message when something goes wrong and add the body content to the logging.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Caution: The body can be very big and/or binary, so validation checks are needed, otherwise the amount of data or the encoding can mess up our logs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Logging Response Data
&lt;/h2&gt;

&lt;p&gt;Now that we got the requests down, the next step is the logging of our responses.&lt;/p&gt;

&lt;p&gt;We already listen to the &lt;code&gt;finish&lt;/code&gt; event of our response, so we have a pretty safe way to get all the data. We just have to extract what the response object holds.&lt;/p&gt;

&lt;p&gt;Let's look at the docs for the &lt;a href="https://nodejs.org/dist/latest-v10.x/docs/api/http.html#http_class_http_serverresponse" rel="noopener noreferrer"&gt;ServerResponse&lt;/a&gt; class to find out what it offers us.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;statusCode&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;statusMessage&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getHeaders()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's add it to our code.&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;requestStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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;errorMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chunk&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;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;end&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;body&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="nf"&gt;concat&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="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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;errorMessage&lt;/span&gt; &lt;span class="o"&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;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rawHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;httpVersion&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="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;remoteFamily&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;socket&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;statusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;statusMessage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&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;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHeaders&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;processingTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;requestStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;rawHeaders&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;errorMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;httpVersion&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="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;remoteFamily&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;statusMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;headers&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="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Handling Response Errors and Client Aborts
&lt;/h2&gt;

&lt;p&gt;At the moment we only log when the response &lt;code&gt;finish&lt;/code&gt; event is fired, this isn't the case if something goes wrong in response or if the client aborts the request.&lt;/p&gt;

&lt;p&gt;For these two cases, we need to create additional handlers.&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;requestStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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;body&lt;/span&gt; &lt;span class="o"&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;requestErrorMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getChunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&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;assembleBody&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="nx"&gt;body&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="nf"&gt;concat&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="nf"&gt;toString&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;getError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&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;requestErrorMessage&lt;/span&gt; &lt;span class="o"&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;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getChunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;assembleBody&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getError&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;logClose&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="nf"&gt;removeHandlers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Client aborted.&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;logError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&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;removeHandlers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&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;logFinish&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="nf"&gt;removeHandlers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestErrorMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;close&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logClose&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="nx"&gt;logFinish&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;removeHandlers&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="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getChunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;assembleBody&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logClose&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&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="nx"&gt;logFinish&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;errorMessage&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;rawHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;httpVersion&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="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;remoteFamily&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;socket&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;statusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;statusMessage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&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;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHeaders&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;processingTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;requestStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;rawHeaders&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;errorMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;httpVersion&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="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;remoteFamily&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;statusMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;headers&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;Now, we also log errors and aborts.&lt;/p&gt;

&lt;p&gt;The logging handlers are also removed when the response finished and all the logging is moved to an extra function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logging to an external API
&lt;/h2&gt;

&lt;p&gt;At the moment the script only writes its logs to the console and in many cases, this is enough because operating systems allow other programs to capture the stdout and do their thing with it, like writing into a file or sending it to a third-party API like Moesif, for example. API logs are vital tools for monitoring the health of APIs, optimizing performance, and ensuring compliance with regulatory standards.&lt;/p&gt;

&lt;p&gt;In some environments, this isn't possible, but since we gathered all information into one place, we can replace the call to console.log with a third-party function.&lt;/p&gt;

&lt;p&gt;Let's refactor the code so it resembles a library and logs to some external service.&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;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;loggingLibrary&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;XYZ&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loggingLibray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&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;loggingApiHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bearer &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiKey&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;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;errorMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestStart&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;rawHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;httpVersion&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="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;remoteFamily&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;socket&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;statusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;statusMessage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&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;responseHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHeaders&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.org/logging-endpoint&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;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;loggingApiHeaders&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;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;requestStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;processingTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;requestStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;rawHeaders&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;errorMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;httpVersion&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="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;remoteFamily&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;statusMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;responseHeaders&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;requestStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// ========== REQUEST HANLDING ==========&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&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;requestErrorMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getChunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&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;assembleBody&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="nx"&gt;body&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="nf"&gt;concat&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="nf"&gt;toString&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;getError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&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;requestErrorMessage&lt;/span&gt; &lt;span class="o"&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;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getChunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;assembleBody&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// ========== RESPONSE HANLDING ==========&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logClose&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="nf"&gt;removeHandlers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Client aborted.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestStart&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;logError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&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;removeHandlers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestStart&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;logFinish&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="nf"&gt;removeHandlers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestErrorMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestStart&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;close&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logClose&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="nx"&gt;logFinish&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// ========== CLEANUP ==========&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;removeHandlers&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="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getChunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;assembleBody&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logClose&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&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="nx"&gt;logFinish&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;With these changes, we can now use our logging implementation as we would use &lt;a href="https://github.com/Moesif/moesif-nodejs" rel="noopener noreferrer"&gt;moesif-nodejs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;loggingLibrary&lt;/code&gt; function takes an API-key as configuration and returns the actual logging-function that will send the log-data to a logging service via HTTP. The logging-function itself takes a &lt;code&gt;request&lt;/code&gt; and &lt;code&gt;response&lt;/code&gt; object.&lt;/p&gt;

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

&lt;p&gt;With these changes, we can now use our logging implementation as we would use &lt;a href="https://github.com/Moesif/moesif-nodejs" rel="noopener noreferrer"&gt;moesif-nodejs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The loggingLibrary function takes an API-key as configuration and returns the actual logging-function that will send the log-data to a logging service via HTTP. The logging-function itself takes a requestresponse object.&lt;/p&gt;

&lt;p&gt;Here are a few:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/trentm/node-bunyan" rel="noopener noreferrer"&gt;Bunyan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/visionmedia/debug" rel="noopener noreferrer"&gt;Debug&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/log4js-node/log4js-node" rel="noopener noreferrer"&gt;Log4js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/expressjs/morgan" rel="noopener noreferrer"&gt;Morgan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/npm/npmlog" rel="noopener noreferrer"&gt;Npmlog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/winstonjs/winston" rel="noopener noreferrer"&gt;Winston&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For developers who want deeper visibility into API traffic with minimal setup, &lt;a href="https://www.moesif.com/wrap?onboard=true&amp;amp;utm_source=devto&amp;amp;utm_medium=bottom-cta&amp;amp;utm_content=how-we-built-nodejs-middleware" rel="noopener noreferrer"&gt;Moesif&lt;/a&gt; offers a seamless solution. Unlike traditional logging libraries, Moesif provides real-time API analytics, retention tracking, and security monitoring, making it an essential tool for scaling and optimizing API-driven applications.&lt;/p&gt;

</description>
      <category>node</category>
      <category>middleware</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>10 Error Status Codes When Building APIs For The First Time And How To Fix Them</title>
      <dc:creator>Moesif Staff</dc:creator>
      <pubDate>Thu, 20 Mar 2025 17:28:25 +0000</pubDate>
      <link>https://forem.com/moesif/10-error-status-codes-when-building-apis-for-the-first-time-and-how-to-fix-them-1cdg</link>
      <guid>https://forem.com/moesif/10-error-status-codes-when-building-apis-for-the-first-time-and-how-to-fix-them-1cdg</guid>
      <description>&lt;p&gt;When making your first API call, issues can arise, especially if you're new to API integration. It's common for documentation to lack details on API error status codes, as the focus is often on successful scenarios rather than potential problems.&lt;/p&gt;

&lt;p&gt;HTTP status codes provide insight into what occurred during your API call. These standardized codes range from 100 to 511, each with a specific meaning. However, only codes from 400 to 511 indicate an error response.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.moesif.com/features/api-analytics?utm_source=devto&amp;amp;utm_medium=top-cta&amp;amp;utm_term=10-error-status-codes" rel="noopener noreferrer"&gt;Moesif API Analytics&lt;/a&gt; can help track and analyze API errors in real-time, providing deep insights into API performance and response trends. This ensures developers can diagnose issues more effectively and optimize their API’s reliability.&lt;/p&gt;

&lt;p&gt;Let's explore the 10 most prevalent HTTP status codes that signal an error response, whether on the client or server side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding HTTP Status Codes
&lt;/h2&gt;

&lt;p&gt;HTTP status codes are a crucial part of the HTTP protocol, used to convey the results of a client’s request. They are divided into five categories: informational responses, successful responses, redirection messages, client error responses, and server error responses. Understanding HTTP status codes is essential for developers, as they provide valuable information about the outcome of a request. By analyzing status codes, developers can identify errors, optimize their applications, and improve user experience.&lt;/p&gt;

&lt;p&gt;When you make an API call, the server responds with an HTTP status code that indicates whether the request was successful or if there was an issue. For instance, a 200 OK status code means the request was successful, while a 404 Not Found status code indicates that the requested resource could not be found. By familiarizing yourself with these codes, you can quickly diagnose problems and take appropriate action to resolve them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error Types and Causes
&lt;/h2&gt;

&lt;p&gt;HTTP errors can be broadly classified into two categories: client-side errors and server-side errors. Client-side errors, such as 400 Bad Request, 401 Unauthorized, and 403 Forbidden, occur when the client’s request is invalid or cannot be processed by the server. These errors often result from issues like malformed request syntax, invalid authentication credentials, or insufficient permissions.&lt;/p&gt;

&lt;p&gt;On the other hand, server-side errors, such as 500 Internal Server Error, 502 Bad Gateway, and 503 Service Unavailable, occur when the server encounters an unexpected condition or is unable to fulfill the request. These errors can be caused by server overload, internal configuration errors, or issues with upstream services. Understanding the types and causes of errors is crucial for developers to diagnose and fix issues efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Client-Side Status Codes
&lt;/h2&gt;

&lt;p&gt;Status codes in the 4XX range typically indicate client-side errors, though modifications to the API can also trigger them. These status codes inform the user agent about the nature of the error, guiding it to take specific actions such as retrying requests or modifying document views. Here are the 5 most common client-side status error codes and how to solve for them:&lt;/p&gt;

&lt;h3&gt;
  
  
  400 Bad Request: Malformed Request Syntax
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;400 Bad Request&lt;/em&gt; error message is one of the most common HTTP status codes, indicating that your API request was not properly formatted. Deceptive request routing can also result in a 400 Bad Request error, as it involves malformed or invalid requests that the server cannot process. If no additional error details are provided in the response body, it's essential to consult the documentation. You might be missing a query parameter, a field in the request body, or there could be an error in a header field. Additionally, incorrect syntax in your request data could be the culprit.&lt;/p&gt;

&lt;p&gt;This differs from the &lt;em&gt;422 Unprocessable Entity&lt;/em&gt; error message, which occurs when your request is properly formatted but cannot be processed.&lt;/p&gt;

&lt;h3&gt;
  
  
  403 Forbidden
&lt;/h3&gt;

&lt;p&gt;403 Forbidden status indicates that you lack the necessary permissions to access the requested URL. Although you are authenticated, the user or role associated with your credentials is not authorized to perform the API request.&lt;/p&gt;

&lt;p&gt;This error may also occur due to authentication problems, such as using an incorrect API key or attempting to access features not included in your subscription plan. To avoid a 403 Forbidden error and gain network access, ensure proper authentication.&lt;/p&gt;

&lt;h3&gt;
  
  
  404 Not Found
&lt;/h3&gt;

&lt;p&gt;The 404 Not Found error is among the most frequent HTTP status codes encountered. It signifies that the URL specified in your request is not available on the API server or the origin server. Although categorized as a 4XX error, typically indicating a client-side issue, it can also suggest a problem on the server's end. Changes in API URL paths after a version update or server-related issues can lead to this error.&lt;/p&gt;

&lt;p&gt;Errors from a prior request might also trigger a 404 Not Found error, particularly if earlier interactions resulted in state management problems between requests.&lt;/p&gt;

&lt;p&gt;To address this, first verify for typos in your client code, then investigate potential API issues.&lt;/p&gt;

&lt;p&gt;This status code further implies that you haven't authenticated with the API. The API is unable to identify you and, therefore, cannot fulfill your request.&lt;/p&gt;

&lt;p&gt;Usually, you need to register and obtain an API key for most APIs. This key should be included in an HTTP header field when making a request to inform the API of your identity.&lt;/p&gt;

&lt;p&gt;This HTTP status code is akin to the less common &lt;em&gt;407 Proxy Authentication Required&lt;/em&gt;, which indicates a lack of authentication with the proxy.&lt;/p&gt;

&lt;h3&gt;
  
  
  429 Too Many Requests
&lt;/h3&gt;

&lt;p&gt;API subscription plans often have usage limits, with lower-cost options allowing fewer requests per second for your API key. If you're sending too many requests quickly, consider implementing throttling in your client. Request headers can dictate conditions that affect rate limits and response behavior, including status codes like 412 (Precondition Failed) and 304 (Not Modified). This response might also indicate you've reached a daily, weekly, or monthly limit on your account. Without utilizing API analytics, you might hit these limits without receiving notifications or alerts.&lt;/p&gt;

&lt;p&gt;An API might seem ideal until you encounter its limitations, which can render it unsuitable for your needs. It's crucial to understand your API subscription details before integration to avoid potential issues down the line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server-Side Status Codes
&lt;/h2&gt;

&lt;p&gt;The 5XX range of status codes typically indicates server-side errors, although sometimes an invalid API call that should result in a 4XX error might instead return a 5XX error if not properly handled by the server. Here are the five most common server-side errors and how to resolve them:&lt;/p&gt;

&lt;h3&gt;
  
  
  500 Internal Server Error
&lt;/h3&gt;

&lt;p&gt;This HTTP status code can mean anything really, but it usually indicates the API server crashed. It could have been caused by something related to your API call. A 500 error can also occur if the server encounters issues with the message body during content negotiation.&lt;/p&gt;

&lt;p&gt;Double-check the docs to make sure you did everything right: query fields, body fields, headers, and format.&lt;/p&gt;

&lt;p&gt;If the issue persists, it could be due to an API update that introduced problematic code or data from an upstream service. In such cases, your best course of action is to contact the API's support team for&lt;/p&gt;

&lt;h3&gt;
  
  
  501 Not Implemented
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;501 Not Implemented&lt;/em&gt; status code relates to the HTTP method used to request a URL. To resolve this, try using a different HTTP method for your request.&lt;/p&gt;

&lt;p&gt;Some response codes, like 501, dictate that the user agent will continue using the same HTTP method from the prior request.&lt;/p&gt;

&lt;p&gt;Typically, an HTTP request using an incorrect method results in a 404 Not Found status. A Not Implemented status, however, suggests that the method is not available "yet." This status allows the API creator to inform clients that this method will be accessible in future requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  502 Bad Gateway
&lt;/h3&gt;

&lt;p&gt;This response indicates that the server you contacted was not the actual API server but rather a gateway or proxy. The proxy server attempts to communicate with the API server on your behalf. This error response also suggests that the API server did not respond. This could be due to a network issue, the API server crashing, or it being down for maintenance.&lt;/p&gt;

&lt;p&gt;A 502 error indicates that the proxy server could not get the requested response from the API server.&lt;/p&gt;

&lt;p&gt;A "bad gateway" error is generally a temporary issue and should be addressed by the API provider. However, if the problem continues, it's advisable to contact their support team for assistance.&lt;/p&gt;

&lt;h3&gt;
  
  
  503 Service Unavailable
&lt;/h3&gt;

&lt;p&gt;The 503 Service Unavailable status signifies a server error. This typically occurs when the server is overwhelmed by too many API requests and cannot handle additional ones. While this issue may resolve itself as clients reduce the number of future requests, it might also indicate that the API provider hasn't allocated sufficient resources.&lt;/p&gt;

&lt;p&gt;Request headers can influence the server's response behavior, potentially leading to a 503 error if certain conditions are not met.&lt;/p&gt;

&lt;p&gt;To enhance your client's resilience against this error, consider implementing a delay before sending another request. However, if the error code persists, it's crucial to reach out to the API provider for further assistance.&lt;/p&gt;

&lt;h3&gt;
  
  
  504 Gateway Timed Out
&lt;/h3&gt;

&lt;p&gt;Similar to the 502 Bad Gateway status, this response code indicates that the server you're contacting is acting as a proxy for the actual API server. In this case, the issue lies with the API server's delayed response.&lt;/p&gt;

&lt;p&gt;This could be related to high network latency between the proxy and the API server. It could also mean that the API server takes too long to process your request. Additionally, a 504 error can occur when a user agent requested content that the server cannot provide due to slow response times.&lt;/p&gt;

&lt;p&gt;To address this issue, examine whether the content of your request might be contributing to the timeout. If you are requesting an excessive amount of data or performing a calculation that takes an extended time, consider minimizing the request.&lt;/p&gt;

&lt;p&gt;If you think your request is reasonable and the status doesn’t go away, contact support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for Error Handling
&lt;/h2&gt;

&lt;p&gt;Error handling is a critical aspect of web development, and best practices can help developers handle errors effectively. Here are some best practices for error handling:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use meaningful error messages&lt;/strong&gt;: Provide clear and concise error messages that help users understand what went wrong. Avoid generic messages like “An error occurred” and instead offer specific details about the issue.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Log errors&lt;/strong&gt;: Log errors to track and analyze issues, and to identify patterns and trends. This can help you pinpoint recurring problems and address them proactively. &lt;a href="https://www.moesif.com/features/api-analytics?utm_source=devto&amp;amp;utm_medium=top-cta&amp;amp;utm_term=10-error-status-codes" rel="noopener noreferrer"&gt;Moesif API Analytics&lt;/a&gt; can help streamline this process by providing detailed logs and filtering capabilities to detect anomalies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use error codes&lt;/strong&gt;: Use standardized error codes, such as HTTP status codes, to convey error information. This makes it easier for developers to understand and troubleshoot issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Handle errors centrally&lt;/strong&gt;: Handle errors centrally, using a single error handling mechanism, to simplify error handling and reduce code duplication. This approach ensures consistency and makes it easier to manage errors across your application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test error handling&lt;/strong&gt;: Test error handling mechanisms thoroughly to ensure they work as expected. Simulate different error scenarios to verify that your application responds appropriately and provides useful feedback to users.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By integrating &lt;a href="https://www.moesif.com/features/api-monitoring?utm_source=devto&amp;amp;utm_medium=top-cta&amp;amp;utm_term=10-error-status-codes" rel="noopener noreferrer"&gt;Moesif API Monitoring&lt;/a&gt;, developers can receive real-time alerts on critical API failures, ensuring that error handling mechanisms are optimized and efficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error Prevention Strategies
&lt;/h2&gt;

&lt;p&gt;Preventing errors is always better than handling them. Here are some strategies to prevent errors:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Validate user input&lt;/strong&gt;: Validate user input to prevent malformed requests and reduce the risk of errors. Ensure that all required fields are present and that the data is in the correct format.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use caching&lt;/strong&gt;: Use caching to reduce the load on servers and prevent errors caused by high traffic. Caching can improve performance and reduce the likelihood of server overload.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Optimize database queries&lt;/strong&gt;: Optimize database queries to prevent errors caused by slow or inefficient queries. Use indexing, query optimization techniques, and database tuning to improve query performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use load balancing&lt;/strong&gt;: Use load balancing to distribute traffic evenly and prevent errors caused by server overload. Load balancing can help ensure that your application remains responsive even under heavy load.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monitor performance&lt;/strong&gt;: Monitor performance regularly to identify potential issues and prevent errors. Use monitoring tools to track key metrics and set up alerts for unusual activity.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By implementing these strategies, you can reduce the likelihood of errors and ensure a smoother experience for your users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring HTTP Status Codes With Moesif
&lt;/h2&gt;

&lt;p&gt;Moesif offers comprehensive &lt;a href="https://www.moesif.com/features/api-monitoring?utm_campaign=Int-site&amp;amp;utm_source=devto&amp;amp;utm_medium=body-cta&amp;amp;utm_content=10-error-status-codes" rel="noopener noreferrer"&gt;monitoring and notification&lt;/a&gt; features, enabling you to automatically track any HTTP status code errors and gain valuable insights from your error response patterns.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;real-time error alerts&lt;/strong&gt;, &lt;a href="https://www.moesif.com/features/api-monitoring?utm_source=devto&amp;amp;utm_medium=top-cta&amp;amp;utm_term=10-error-status-codes" rel="noopener noreferrer"&gt;Moesif API Monitoring&lt;/a&gt; helps identify critical issues before they impact users, allowing teams to proactively resolve them. Whether it’s a spike in 500 Internal Server Errors or unexpected 429 rate-limit errors, Moesif ensures you stay informed with actionable notifications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fukdh11sy9rufbag02nou.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fukdh11sy9rufbag02nou.png" alt="Dashbaords showing 4xx and 5xx error trends" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;API calls are consistently monitored with user identity, facilitating the swift identification and resolution of errors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fumx5d7z74pmj993b597w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fumx5d7z74pmj993b597w.png" alt="Automatic altering for status code" width="723" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Undoubtedly, you’ll see many error codes when using APIs, but most have reasonable fixes. Some are related to server errors and some to client-side errors, where often one can cause the other. Request headers allow clients to customize their HTTP requests and influence the server's response behavior.&lt;/p&gt;

&lt;p&gt;Make sure to thoroughly read the documentation and API notes to avoid missing any crucial details during integration. If issues persist, reach out to the API provider for assistance.&lt;/p&gt;

&lt;p&gt;Sometimes, the API provider may not resolve an issue for a consumer. If you're using a widely-used API, consider searching online for solutions, particularly on &lt;a href="https://stackoverflow.com/" rel="noopener noreferrer"&gt;StackOverflow&lt;/a&gt; or generative artificial intelligence chatbot's like &lt;a href="https://chatgpt.com/" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt;, to find a fix for your error responses. Stay determined, and you’ll see your 200 OK status codes in no time.&lt;/p&gt;

</description>
      <category>api</category>
      <category>apimonitoring</category>
      <category>apistatuscode</category>
      <category>realtimeerroralerts</category>
    </item>
    <item>
      <title>Why a Unified View of API Usage is Critical for Managing Multiple API Gateways</title>
      <dc:creator>Moesif Staff</dc:creator>
      <pubDate>Tue, 04 Mar 2025 14:31:55 +0000</pubDate>
      <link>https://forem.com/moesif/why-a-unified-view-of-api-usage-is-critical-for-managing-multiple-api-gateways-337n</link>
      <guid>https://forem.com/moesif/why-a-unified-view-of-api-usage-is-critical-for-managing-multiple-api-gateways-337n</guid>
      <description>&lt;p&gt;APIs have become the backbone of our digital world, with surveys showing that &lt;a href="https://www.postman.com/state-of-api/2024" rel="noopener noreferrer"&gt;over 70% of developers plan to increase API usage year-over-year&lt;/a&gt;. They power everything from mobile apps and SaaS integrations to IoT devices and partner platforms, enabling businesses to deliver seamless services and experiences to customers. As organizations grow, however, so does the complexity of their API ecosystem. Many teams end up deploying multiple API gateways, often to manage different product lines, microservices, or regional deployments.&lt;/p&gt;

&lt;p&gt;While multiple gateways can offer flexibility and specialized features, they also create new challenges. Each gateway might have its own monitoring dashboards, security configurations, documentation portals, and analytics tools. Product managers, developers, and operational teams quickly find themselves juggling scattered bits of data as they attempt to track API usage, performance metrics, and monetization results across these disparate solutions.&lt;/p&gt;

&lt;p&gt;That’s why forward-thinking organizations are turning to &lt;strong&gt;unified API management for multiple API gateways&lt;/strong&gt;. By consolidating data from all their gateways into a &lt;strong&gt;single source of truth&lt;/strong&gt;, they gain the ability to track both operational KPIs (like uptime and error rates) and business KPIs (like usage growth and revenue impact) from one shared dashboard. This not only streamlines troubleshooting and optimization but also enables product managers to “package” their APIs effectively whether for new subscription tiers, developer-friendly bundles, or usage-based pricing models.&lt;/p&gt;

&lt;p&gt;Let's explore the reasons organizations end up with multiple API gateways, the pitfalls and pain points they encounter, and the specific benefits that a unified approach can deliver. Along the way, you’ll see how incorporating centralized analytics and leveraging platforms like &lt;strong&gt;Moesif&lt;/strong&gt;, can help unravel the complexity, drive better decisions, and ultimately boost your bottom line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unified API Management for Multiple API Gateways: Why They Exist in an Organization
&lt;/h2&gt;

&lt;p&gt;For many organizations, having multiple API gateways isn’t necessarily an intentional design choice. Instead, it often evolves organically over time as the business scales or adapts to new requirements. Here are a few common scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Organic Growth Across Teams&lt;/strong&gt;: Teams select API gateways based on their specific needs, leading to multiple “best-of-breed” gateways operating in parallel.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mergers and Acquisitions&lt;/strong&gt;: Companies inherit different API infrastructures and maintain multiple gateways to ensure compatibility and avoid integration disruptions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Varied Deployment Environments&lt;/strong&gt;: Different regions require specialized gateways for compliance, data sovereignty, or latency optimization (e.g., GDPR in the EU, domestic payment systems in the U.S.).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Specialized Use Cases&lt;/strong&gt;: Internal microservices may use lightweight gateways, while customer-facing APIs require advanced features like authentication flows and developer portals.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An API management solution can help manage the complexities of having multiple API gateways by providing a flexible protocol that allows businesses to switch between different gateways without needing to rework their developer portal. This enhances integration and functionality across the organization.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem and Why It Matters
&lt;/h3&gt;

&lt;p&gt;Although multiple gateways can serve unique demands, it quickly becomes tricky to manage. Teams end up with fragmented data on API usage, performance, and developer adoption. Each gateway might provide its own analytics dashboard, making cross-team collaboration and strategic decision-making difficult.&lt;/p&gt;

&lt;p&gt;Without a unified view across these gateways, product managers and leadership struggle to see which APIs are truly driving revenue, where traffic is spiking, or how user onboarding differs from one product line to another. In fact, recent research shows that &lt;a href="https://www.salesforce.com/news/stories/connectivity-report-2023/" rel="noopener noreferrer"&gt;38% of revenue-generating digital assets in enterprises are API-driven&lt;/a&gt;, making visibility across multiple gateways critical. Let’s dive deeper into these challenges and how a consolidated approach can save organizations from operational headaches while unlocking valuable new business insights. A developer portal can help manage these issues by providing a seamless interface with various API gateways, enabling fast key provisioning and integration, and offering a unified view that simplifies the evaluation process for API management solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges of Managing Multiple API Gateways and the Power of Unified API Management
&lt;/h2&gt;

&lt;p&gt;As organizations scale, different teams, business units, or regions may adopt various API gateways based on their unique requirements—whether for performance, security, compliance, or regional deployment. While this flexibility allows for tailored solutions, it also introduces operational inefficiencies, governance inconsistencies, and fragmented data visibility. Without a unified approach, companies struggle to optimize API performance, enforce security policies, and drive monetization strategies effectively. API providers can help manage these challenges by offering centralized solutions that streamline operations, enhance security, and create new revenue streams through various pricing models like pay-per-use and partnerships.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Hidden Costs of API Fragmentation
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Data Silos and Limited Insights&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Inconsistent API Metrics&lt;/strong&gt;: Each gateway provides separate analytics, making it difficult to gain a comprehensive view of API usage, performance, and adoption trends.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Difficult Monetization Tracking&lt;/strong&gt;: API monetization models vary across gateways, leading to inconsistent revenue tracking and missed optimization opportunities.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding API users can help overcome data silos and gain better insights by enriching user profiles and analyzing API usage alongside revenue and customer success.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Limited Business Visibility and Growth Potential&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Unclear API Adoption Metrics&lt;/strong&gt;: Understanding customer behavior and high-value integrations is challenging when data remains siloed. Tracking API usage growth can help improve business visibility and growth potential by identifying trends and ensuring consistent usage over time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hindered API Packaging Strategies&lt;/strong&gt;: Product managers lack the insights needed to refine pricing tiers, subscription models, and usage-based billing structures.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Solution: A Unified View for API Management
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;unified API management strategy&lt;/strong&gt; eliminates these challenges by consolidating API analytics, governance, and monetization tools into a single platform. Moesif provides deep visibility into API performance, security, and usage patterns across multiple gateways, empowering teams to make data-driven decisions and maximize API business potential. API management solutions can provide a unified view for API management, offering innovative features and developer portals that are essential for organizations, especially in multi-gateway environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Holistic API Observability and Governance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real-Time API Monitoring&lt;/strong&gt;: &lt;a href="https://www.moesif.com/features/api-analytics?utm_source=blog&amp;amp;utm_medium=content-link&amp;amp;utm_content=DEVTO-Unified-View-of-API-Usage" rel="noopener noreferrer"&gt;Moesif can aggregate usage and performance metrics&lt;/a&gt; from multiple API gateways, enabling rapid issue detection and resolution. Monitoring CPU and memory usage can help in holistic API observability and governance by providing insights into application responsiveness and server health, which are crucial for resource planning and diagnosing performance issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standardized Security Policies&lt;/strong&gt;: &lt;a href="https://www.moesif.com/features/api-governance-rules?utm_source=blog&amp;amp;utm_medium=content-link&amp;amp;utm_content=DEVTO-Unified-View-of-API-Usage" rel="noopener noreferrer"&gt;Enforce rate limiting&lt;/a&gt; and compliance requirements consistently across all API deployments.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Smarter Monetization and Product Strategy
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cross-Gateway API Insights&lt;/strong&gt;: Product managers can analyze API adoption, revenue impact, and retention rates in a single unified view within Moesif.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Optimized Pricing and Billing&lt;/strong&gt;: Gain the intelligence needed to refine monetization models, implement tiered pricing, and maximize API revenue. Understanding the API product lifecycle can further enhance these strategies by providing insights into key performance indicators relevant to business impact and operational success.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Aligning Business Goals with API Strategy
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Unified KPI Tracking&lt;/strong&gt;: Moesif enables organizations to track API performance, customer engagement, and bxusiness impact through a single analytics platform with &lt;a href="https://www.moesif.com/features/api-dashboards?utm_source=blog&amp;amp;utm_medium=content-link&amp;amp;utm_content=DEVTO-Unified-View-of-API-Usage" rel="noopener noreferrer"&gt;custom API dashboards&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scalable API Growth&lt;/strong&gt;: Centralized visibility into API usage trends supports infrastructure planning and expansion strategies.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Moesif, companies can transform chaotic, multi-gateway environments into streamlined, data-driven ecosystems that enhance developer experience, improve security, and drive new revenue opportunities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unified Analytics Help Product Managers Package and Monetize APIs
&lt;/h2&gt;

&lt;p&gt;When an organization has visibility into &lt;strong&gt;who&lt;/strong&gt; is using their APIs, &lt;strong&gt;how&lt;/strong&gt; they’re using them, and &lt;strong&gt;why&lt;/strong&gt; they’re valuable, it becomes far easier to design effective pricing and packaging strategies. This is where unified analytics across multiple gateways truly shines. Let’s explore how product managers can leverage this holistic data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Segmenting and Identifying High-Value API Usage
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Usage Segmentation&lt;/strong&gt;: Aggregating data across gateways helps segment usage by customer tiers, feature adoption, or endpoint activity. If enterprise customers heavily rely on a particular API, it may justify premium pricing or SLAs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Customer Profiling&lt;/strong&gt;: Understanding which customers generate the highest traffic or revenue allows for better API packaging and monetization strategies.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Optimizing Pricing, Packaging, and Monetization
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Subscription vs. Pay-Per-Use&lt;/strong&gt;: A clear view of API consumption enables Product Managers to choose the right pricing model—subscriptions for predictable costs or pay-per-use for flexibility.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Feature-Based Monetization&lt;/strong&gt;: Advanced features, such as analytics or premium data, can be placed in higher-tier plans based on real usage data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multi-API Bundles &amp;amp; Upsell Paths&lt;/strong&gt;: Bundling related APIs and tracking usage thresholds can drive upsells, ensuring that developers move into higher tiers when they exceed their limits.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Data-Driven Adjustments and Transparency
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real-Time Optimization&lt;/strong&gt;: A unified view allows for quick adjustments to pricing and packaging based on under or overutilized features.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Promotional Campaign Tracking&lt;/strong&gt;: Integrated analytics provide insights into how trials or promotions impact usage, guiding future marketing efforts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Clear Metering &amp;amp; Billing Transparency&lt;/strong&gt;: Giving customers visibility into their API usage reduces billing disputes and builds trust, while automated alerts prevent unexpected overages.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A singular view of usage is a powerful enabler for API productization and monetization. It provides the granular insights product managers need to craft compelling offers, set competitive pricing, and nurture stronger relationships with customers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key API Metrics: Why a Unified View Matters
&lt;/h2&gt;

&lt;p&gt;Bringing together multiple API gateways under one management framework grants you the power to observe performance, reliability, and revenue-generation in a consistent, holistic way. These &lt;strong&gt;key metrics&lt;/strong&gt; form the bedrock of continuous improvement, helping you spot bottlenecks, optimize resource allocation, and validate strategic decisions. Increased API usage is a key metric for understanding API performance and customer engagement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Product and Business KPIs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Revenue or Billing Metrics&lt;/strong&gt;: For monetized APIs, track revenue per call, average revenue per user (ARPU), and churn rates. These metrics can confirm whether your pricing models and product tiers are viable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Conversion Rates&lt;/strong&gt;: If you offer freemium or trial tiers, measure how many developers or businesses upgrade to paid plans. This indicates how compelling your paid features are and where you might refine your funnel.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Feature Adoption&lt;/strong&gt;: Monitor usage of key endpoints or functionalities. Understanding which features gain the most traction guides future roadmap decisions and helps you prioritize improvements that yield the highest ROI.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Adoption and Usage Metrics
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;API Call Distribution&lt;/strong&gt;: With multiple gateways, it’s vital to see how traffic is distributed across various services and endpoints. This reveals which APIs are under heavy load or show strong growth potential.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;New vs. Returning Developers&lt;/strong&gt;: A unified view of developer onboarding metrics—like time-to-first-successful-call or repeat usage—indicates how well your APIs retain interest and whether your documentation or portals are effective.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Geographical/Regional Usage&lt;/strong&gt;: Tracking where calls originate can guide infrastructure decisions (e.g., data center locations) and help you tailor security policies for each region.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Operational Metrics
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Latency (Average and Peak)&lt;/strong&gt;: Monitoring response times is crucial for ensuring a seamless end-user experience. High latency can signal underlying issues in either the gateway configurations or your backend services. In fact, &lt;a href="https://www.cloudflare.com/en-ca/2024-api-security-management-report/" rel="noopener noreferrer"&gt;Cloudflare’s API Performance Report&lt;/a&gt; shows that 57% of internet traffic is now composed of API requests, making speed and efficiency a top priority.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Error Rates&lt;/strong&gt;: Tracking the frequency and type of errors (4XX, 5XX) helps surface potential configuration problems, code bugs, or external dependencies failing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Throughput and Request Volume&lt;/strong&gt;: By understanding how many requests each gateway handles—and during which time frames—you can proactively manage capacity, load balance effectively, and predict scaling needs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a multi-gateway environment, relying on fragmented statistics can lead to misinformed decisions. A unified dashboard ensures every team, from product management to engineering, has the data they need to troubleshoot issues, iterate on features, and prove ROI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Business and Growth: Why You Need a Unified Dashboard
&lt;/h2&gt;

&lt;p&gt;While operational stability and a strong developer experience are vital, most organizations ultimately measure API success by its impact on the &lt;strong&gt;bottom line&lt;/strong&gt; and long-term &lt;strong&gt;market positioning&lt;/strong&gt;. A unified API strategy can serve as a foundation for sustainable growth, allowing product teams to focus on strategic initiatives rather than firefighting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customer Acquisition and Retention
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Targeted Upsell/Cross-Sell&lt;/strong&gt;: By analyzing usage across gateways, you can identify which customers or partners might benefit from additional API features or higher-tier plans. Automated alerts or in-app notifications can then prompt timely upsells, increasing average revenue per user.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reduced Churn through Insights&lt;/strong&gt;: A single pane of glass for customer activity enables quick identification of at-risk accounts—e.g., a sudden drop in API calls could signal dissatisfaction or technical hurdles. Product managers can intervene proactively, offering support or new features to keep users engaged.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Strategic Growth and Market Expansion
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data-Backed Roadmap Decisions&lt;/strong&gt;: Comprehensive usage data drives more accurate prioritization of new features, expansions, or acquisitions. Leadership can see precisely which APIs or endpoints are fueling growth—and invest accordingly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Confident Expansion into New Markets&lt;/strong&gt;: If the data shows strong traction in a specific region or industry, leadership can allocate resources strategically, designing localized solutions or forging new partnerships.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Preparing for Emerging Business Models&lt;/strong&gt;: Whether it’s launching new subscription tiers or integrating with ecosystem partners, a unified API platform makes it simpler to experiment with—and measure—new revenue models.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Staying Agile in Fast-Changing Markets&lt;/strong&gt;: Unified metrics and streamlined workflows help your teams pivot quickly in response to market feedback or disruptive trends, ensuring long-term competitiveness.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cross-Functional Visibility and Scalable Collaboration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Single Source of Truth for Stakeholders&lt;/strong&gt;: When executives, product managers, and DevOps share the same metrics, conversations shift from “do we have the data?” to “how do we use it?” This alignment empowers better coordination on product strategy, sales, and support.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bridging Silos in Large Organizations&lt;/strong&gt;: In multi-division or globally distributed companies, a unified approach ensures that successes and challenges are visible across the organization, reducing duplication of effort and fostering synergy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scalable Growth and Future-Proofing&lt;/strong&gt;: A well-structured API management strategy ensures that teams can scale efficiently, breaking down data silos and creating a foundation for long-term business growth.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Centralized API management isn’t just about operational efficiency; it’s also a strategic growth lever. When teams have clear, unified data on API usage and performance, they can make stronger decisions about product direction, pricing strategies, and market expansion. In the next section, we’ll discuss how these elements come together in real-world scenarios, using unified analytics to maximize both technical efficiency and business ROI.&lt;/p&gt;

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

&lt;p&gt;From the moment an organization deploys its first API gateway to the point where dozens of microservices and product lines are live, the complexity of managing and measuring API usage can grow exponentially. Multiple gateways, each with its own analytics and operational quirks, can easily lead to data silos and fragmented insights. Yet by &lt;strong&gt;consolidating&lt;/strong&gt; these gateways under a single lens, companies unlock new levels of transparency and control.&lt;/p&gt;

&lt;p&gt;While each organization’s path to unified API management is unique, the overarching takeaway remains constant: &lt;strong&gt;centralizing your API data is a multiplier on success&lt;/strong&gt;. It nurtures a holistic understanding of user behavior, helps maintain strong SLAs, and aligns teams around shared, data-driven goals.&lt;/p&gt;

&lt;p&gt;If you’re exploring solutions in this space, Moesif is capable of providing deep insights into both the operational and business aspects of your APIs. The key is to find tools and processes that facilitate, rather than hinder, your multi-gateway strategy. &lt;a href="https://www.moesif.com/wrap?onboard=true&amp;amp;utm_source=blog&amp;amp;utm_medium=bottom-cta&amp;amp;utm_content=DEVTO-Unified-View-of-API-Usage" rel="noopener noreferrer"&gt;Sign up today for a free trial&lt;/a&gt;, no credit card required.&lt;/p&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Best Practices for Monetizing AI Successfully</title>
      <dc:creator>Moesif Staff</dc:creator>
      <pubDate>Tue, 25 Feb 2025 14:49:13 +0000</pubDate>
      <link>https://forem.com/moesif/best-practices-for-monetizing-ai-successfully-356n</link>
      <guid>https://forem.com/moesif/best-practices-for-monetizing-ai-successfully-356n</guid>
      <description>&lt;p&gt;Artificial intelligence has become a driving force behind modern innovation, helping businesses across all industries optimize processes and generate income. But how do you monetize AI usage effectively? Whether you’re integrating AI features into an existing plan or launching entirely new AI products, choosing the right approach can unlock steady revenue growth and strengthen competitive advantage.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore several proven monetization strategies for artificial intelligence, from direct monetization to indirect monetization, and shed light on developing an AI pricing strategy that suits your target audience. We’ll also highlight best practices for defining value metrics, managing cost structures, and building long-term customer satisfaction. By the end, you’ll see how even a significant investment in AI can be recouped through the right pricing model and thoughtful execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding AI Monetization
&lt;/h2&gt;

&lt;p&gt;“Monetizing AI” involves generating revenue from artificial intelligence tools, such as AI-powered tools, AI chatbots, or AI-enhanced analytics. To effectively monetize AI usage, businesses typically offer AI capabilities—such as NLP, computer vision, or large language models—as paid features or standalone solutions that deliver actual value to customers.&lt;/p&gt;

&lt;p&gt;Businesses can adopt a variety of monetization strategies, including usage-based pricing, bundled pricing, or a value-led pricing strategy. Regardless of the approach, the key is to determine how much value your AI functionalities bring to users and to align pricing with that actual usage. Doing so ensures customers feel they’re paying the right price point for the benefits they receive.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is AI Monetization?
&lt;/h3&gt;

&lt;p&gt;AI monetization refers to the process of generating revenue from artificial intelligence (AI) capabilities, features, and products. This involves developing and implementing effective pricing strategies, packaging, and licensing models to capture the value created by AI. For businesses, AI monetization is critical to recoup their investments in AI research, development, and deployment. By strategically leveraging AI capabilities, companies can unlock new revenue streams, enhance customer experiences, and maintain a competitive edge in the market.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why It Matters
&lt;/h3&gt;

&lt;p&gt;Monetizing AI is more than just an opportunity—it's quickly becoming a necessity for businesses looking to stay competitive. AI is reshaping industries by improving efficiency, personalizing customer experiences, and driving automation. However, understanding how to extract real monetary value from AI investments is a challenge that many businesses face. By leveraging the right strategies, companies can ensure that their AI innovations not only enhance operations but also contribute directly to revenue growth.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;AI can reduce manual labor and handle complex tasks that would otherwise require human intelligence. By automating repetitive processes, AI allows employees to focus on higher-value work, leading to increased productivity and cost savings. Additionally, AI-driven automation can scale operations more efficiently, handling large volumes of data-driven tasks without the need for proportional increases in workforce size.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AI uncovers insights in usage data for better decision-making and customer behavior analysis. By leveraging machine learning algorithms, businesses can detect patterns, predict future trends, and personalize customer interactions. AI-driven analytics can provide real-time feedback, allowing organizations to refine marketing strategies, optimize pricing models, and enhance customer segmentation for maximum impact.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Many organizations incorporate generative AI (e.g., AI-generated art) to differentiate their offerings. This includes leveraging AI to create unique marketing assets, generate personalized content at scale, and develop innovative digital products that stand out in competitive markets. Additionally, businesses are using AI to streamline creative workflows, reducing costs and enhancing efficiency in content production.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ultimately, artificial intelligence can be a significant investment. However, with proper planning and a well-defined AI monetization strategy, it becomes easier to monetize AI usage and see a return on that investment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Trends in AI Monetization
&lt;/h3&gt;

&lt;p&gt;The AI monetization landscape is rapidly evolving, with several key trends emerging. Businesses are continuously exploring innovative ways to capitalize on AI's capabilities, from advanced pricing models to AI-driven market analysis. As AI adoption increases across industries, companies must stay ahead of these trends to maintain a competitive edge and unlock new revenue streams.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hybrid Pricing Models&lt;/strong&gt;: Companies are increasingly adopting hybrid pricing models that combine subscription-based, usage-based, and tiered pricing. This approach allows businesses to capture the value of AI features and products more effectively by aligning pricing with customer usage and needs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Value-Based Pricing&lt;/strong&gt;: AI vendors are shifting towards value-based pricing, where the price of AI features and products is tied to the actual value they deliver to customers. This ensures that customers perceive they are getting their money’s worth, fostering long-term satisfaction and loyalty.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AI-Powered Pricing Optimization&lt;/strong&gt;: Companies are leveraging AI-powered pricing optimization tools to analyze customer behavior, market trends, and competitor pricing. These tools help businesses fine-tune their AI pricing strategies, ensuring they remain competitive while maximizing revenue.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Increased Focus on Customer Lifetime Value&lt;/strong&gt;: AI vendors are prioritizing customer lifetime value (CLV) as a key metric to measure the success of their AI monetization strategies. By focusing on CLV, companies can develop strategies that enhance customer retention and drive long-term profitability.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By taking a structured approach to AI monetization, businesses can harness the full potential of their AI investments. Whether through direct revenue models, data-driven insights, or enhanced customer experiences, AI presents numerous opportunities to generate income and improve operational efficiency. The key lies in selecting the right strategy, continuously optimizing offerings, and adapting to market trends to stay ahead of the competition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identifying AI Capabilities
&lt;/h2&gt;

&lt;p&gt;Identifying AI capabilities is crucial for developing effective AI monetization strategies. Understanding the full range of AI functionalities allows businesses to leverage these technologies in innovative ways, enhancing both operational efficiency and revenue potential. By identifying AI capabilities, companies can better align their offerings with customer needs, optimize pricing structures, and create unique value propositions that set them apart in competitive markets. AI capabilities can be categorized into several types, including:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Machine Learning&lt;/strong&gt;: Machine learning capabilities enable AI systems to learn from data and improve their performance over time. This can be applied in various domains, from predictive maintenance to personalized recommendations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Natural Language Processing&lt;/strong&gt;: Natural language processing (NLP) capabilities enable AI systems to understand and generate human language. This is essential for applications like chatbots, virtual assistants, and sentiment analysis.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Computer Vision&lt;/strong&gt;: Computer vision capabilities enable AI systems to interpret and understand visual data from images and videos. This technology is used in areas such as facial recognition, autonomous vehicles, and medical imaging.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Predictive Analytics&lt;/strong&gt;: Predictive analytics capabilities enable AI systems to analyze data and make predictions about future outcomes. This is valuable for applications like demand forecasting, risk assessment, and customer behavior analysis.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Large Language Models (LLMs)&lt;/strong&gt;: LLMs enable AI systems to process and generate human-like text, facilitating advanced applications like conversational AI, content creation, and automated code generation. These models are widely used in virtual assistants, customer service automation, and knowledge management solutions.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By staying informed about emerging AI trends and continuously refining strategies, businesses can maximize the value of their AI investments. The evolving landscape of AI presents endless opportunities, and companies that adopt a proactive approach will be best positioned for long-term success.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Features and Products
&lt;/h2&gt;

&lt;p&gt;AI features and products can be monetized in various ways, including leveraging AI-driven automation, enhancing existing services with intelligent capabilities, and integrating AI-powered insights into business operations. Companies can implement diverse strategies to create new revenue streams, expand market reach, and differentiate their offerings from competitors.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Direct Monetization&lt;/strong&gt;: Charging customers directly for AI features and products through one-time purchases, subscriptions, or pay-per-use models. Businesses can implement this by offering premium AI functionalities, API access, or AI-driven enhancements as paid upgrades. Additionally, companies can monetize AI through licensing agreements, enterprise contracts, and tiered service offerings that provide advanced capabilities for higher fees. The flexibility of direct monetization allows organizations to tailor pricing structures to customer needs while ensuring a steady revenue stream from AI innovations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Indirect Monetization&lt;/strong&gt;: Generating revenue from AI features and products through indirect channels, such as advertising, data analytics, or strategic partnerships. Businesses can leverage AI to enhance targeted marketing campaigns, optimize customer insights for improved engagement, and create AI-driven solutions that drive customer retention. Additionally, companies can monetize AI by offering data-driven insights to third parties, providing enhanced analytics for advertisers, or integrating AI-powered recommendations into existing services to increase value without direct fees.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Freemium Models&lt;/strong&gt;: Offering basic AI features and products for free while charging customers for premium features and products. This approach allows businesses to attract a large user base and demonstrate the value of their AI-driven services. Free-tier users can experience core functionalities, increasing the likelihood of upgrading to paid tiers for advanced capabilities, enhanced performance, or additional integrations. Many successful AI companies use freemium models to create a strong customer pipeline while monetizing premium-tier users effectively.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Subscription-Based Models&lt;/strong&gt;: Charging customers a recurring fee for access to AI features and products, providing a steady revenue stream. Subscription models allow businesses to build predictable revenue while fostering long-term customer relationships. Companies can structure subscriptions with multiple tiers, offering varied levels of AI functionalities, customer support, or additional integrations. Many AI-driven platforms use this approach to provide ongoing updates and improvements, ensuring continued customer value and retention.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By implementing the right monetization strategy, businesses can unlock the full potential of AI-driven features and products. Whether through direct sales, indirect revenue streams, or subscription-based models, companies can leverage AI to enhance their offerings and drive long-term growth. The key is to align monetization strategies with customer needs, ensuring both value delivery and sustainable revenue generation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing Models for AI
&lt;/h2&gt;

&lt;p&gt;Selecting the right AI pricing strategy is crucial to profitability. A well-structured pricing model ensures that businesses can maximize revenue while maintaining customer satisfaction. Companies must consider factors such as market demand, competitive positioning, and scalability when determining the most effective approach. Common models include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Usage-Based Pricing&lt;/strong&gt;: Charging per transaction, per query, or per processing cycle.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tiered Pricing&lt;/strong&gt;: Offering multiple subscription tiers, each unlocking different AI features.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Value-Based Pricing&lt;/strong&gt;: Pricing AI based on perceived value and user impact.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choosing the right pricing model for AI solutions can significantly impact revenue and customer adoption. By aligning pricing with usage patterns, feature accessibility, or perceived value, businesses can optimize profitability while ensuring customer satisfaction. A flexible approach allows companies to adapt to market trends and evolving user needs, creating a sustainable and competitive pricing strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Operationalize Your AI Monetization Strategy
&lt;/h2&gt;

&lt;p&gt;To successfully monetize AI, businesses need the right infrastructure, compliance measures, and performance monitoring. Best practices include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Building a solid AI infrastructure&lt;/strong&gt;: Partnering with AI providers or developing in-house expertise.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Using AI pricing optimization tools&lt;/strong&gt;: Leveraging AI-powered insights to refine pricing strategies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Focusing on customer feedback&lt;/strong&gt;: Implementing beta programs to assess user value.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ensuring security and compliance&lt;/strong&gt;: Adhering to data protection regulations like GDPR and CCPA.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By focusing on these key operational elements, businesses can effectively implement their AI monetization strategy, ensuring that their AI capabilities are not only innovative but also profitable. A well-executed strategy involves a combination of robust infrastructure, strategic pricing, and a commitment to customer satisfaction.&lt;/p&gt;

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

&lt;p&gt;Successfully monetizing AI requires a blend of pricing strategy, value metrics, and ongoing customer engagement. Whether through direct monetization or indirect monetization, aligning AI functionalities with real-world user needs is key to driving revenue and maintaining a competitive edge.&lt;/p&gt;

&lt;p&gt;If you want to experience how an AI-powered API platform can transform your API product management, &lt;a href="https://www.moesif.com/wrap?onboard=true&amp;amp;utm_source=blog&amp;amp;utm_medium=bottom-cta&amp;amp;utm_content=DEVTO-Best-Practices-for-Monetizing-AI-Successfully" rel="noopener noreferrer"&gt;sign up for Moesif today for a free trial&lt;/a&gt;, no credit card required.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webmonetization</category>
      <category>revenuegrowth</category>
      <category>bestpractices</category>
    </item>
    <item>
      <title>Monitoring Cost and Consumption of AI APIs and Apps</title>
      <dc:creator>Moesif Staff</dc:creator>
      <pubDate>Thu, 20 Feb 2025 18:56:52 +0000</pubDate>
      <link>https://forem.com/moesif/monitoring-cost-and-consumption-of-ai-apis-and-apps-56gb</link>
      <guid>https://forem.com/moesif/monitoring-cost-and-consumption-of-ai-apis-and-apps-56gb</guid>
      <description>&lt;p&gt;The rise of AI has transformed how businesses operate, creating a surge in demand for AI-driven APIs, particularly those that leverage Large Language Models (LLMs). These APIs are at the heart of many modern applications, driving automation, customer interaction, and sophisticated data analysis. However, with this increased use comes a need for organizations to effectively monitor and manage the costs and consumption of these APIs. Understanding how different customers and applications interact with your APIs is crucial to maintaining profitability and ensuring efficient resource use.&lt;/p&gt;

&lt;p&gt;In this blog post, we’ll explore how Moesif can help organizations achieve full observability into their AI APIs. We’ll discuss common challenges like cost tracking, consumption monitoring, and how Moesif’s capabilities can simplify cost attribution, helping you stay in control of your AI-related expenditures. We'll also look into best practices for managing costs and ways to improve profitability using data-driven insights.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge of Understanding Costs in AI APIs
&lt;/h2&gt;

&lt;p&gt;AI-powered APIs, especially those relying on LLMs such as OpenAI's models, introduce unique challenges when it comes to understanding costs. Unlike traditional APIs, the cost of LLM-based APIs can vary significantly depending on the nature of each request. Factors like the complexity of prompts, the volume of data processed, and compute intensity can all impact costs. This variability makes it challenging for organizations to maintain predictability and control over their operational expenses.&lt;/p&gt;

&lt;p&gt;For AI businesses to maintain a sustainable financial model, it’s essential to &lt;a href="https://www.moesif.com/blog/technical/api-development/Optimizing-Profits-Calculating-and-Reporting-COGS-for-Your-OpenAI-Powered-API/" rel="noopener noreferrer"&gt;calculate the Cost of Goods Sold (COGS) accurately&lt;/a&gt;. This requires comprehensive tracking of direct expenses, such as provider fees for AI models, as well as indirect costs, like infrastructure and server maintenance. Without accurate cost tracking, businesses risk running into budget overruns and profitability issues. Moesif offers a detailed view into these cost components by monitoring API interactions in real time, providing valuable insights that help ensure you are fully aware of what is driving your COGS.&lt;/p&gt;

&lt;p&gt;With Moesif’s capabilities, organizations can set up &lt;a href="https://www.moesif.com/features/api-dashboards" rel="noopener noreferrer"&gt;custom dashboards&lt;/a&gt; that provide detailed breakdowns of costs by different dimensions, such as request type, endpoint, or customer segment. This level of detail empowers finance and engineering teams to work together to optimize both cost efficiency and performance. By identifying where costs are highest and why, organizations can make informed decisions to improve their overall API strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identifying High-Cost Customers
&lt;/h2&gt;

&lt;p&gt;One significant challenge that many companies face is identifying which customers are responsible for the bulk of their API costs. Different users interact with AI APIs in varying ways—some use straightforward, low-cost requests, while others may make highly complex or frequent requests that drive up costs considerably. This disparity in usage often means that a small percentage of customers contribute disproportionately to the overall API expenses.&lt;/p&gt;

&lt;p&gt;Moesif provides granular visibility into customer-specific API usage, enabling organizations to pinpoint which users are contributing most to operational expenses. With these insights, companies can make informed decisions about implementing tiered pricing models, optimizing customer usage, or even adjusting service levels to better align with their costs. By using Moesif, businesses can create user segmentation based on usage intensity and cost impact, allowing for more personalized communication and pricing adjustments.&lt;/p&gt;

&lt;p&gt;For example, a SaaS company offering an AI-based API could use Moesif to identify customers that frequently make high-cost API calls. By understanding these usage patterns, the company could introduce premium pricing plans tailored to customers who derive significant value from more intensive API use. Alternatively, they could work with these customers to optimize their API requests, potentially reducing their own costs while improving efficiency for the customer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring Consumption for LLM APIs
&lt;/h2&gt;

&lt;p&gt;Large Language Models are powerful tools, but their cost structure can be challenging. The way customers use LLMs—such as making complex queries or frequent calls—can directly affect the overall expenses. In particular, queries that require significant computational power, such as those with extensive context or specialized responses, can increase the cost per request. Moesif enables real-time monitoring of LLM consumption, helping companies understand usage patterns that lead to higher costs.&lt;/p&gt;

&lt;p&gt;By analyzing customer interaction data, businesses can identify usage trends that may be leading to inefficiencies. For example, if a small subset of customers is responsible for an outsized portion of LLM costs, organizations can engage with those customers to optimize prompt usage or even shift them to a more cost-effective pricing tier. This level of insight allows companies to fine-tune their API strategy to manage costs without compromising customer satisfaction.&lt;/p&gt;

&lt;p&gt;Moesif also allows for proactive alerts and notifications. If a customer’s usage suddenly spikes, leading to higher-than-expected costs, teams can be alerted in real-time. This enables companies to take immediate action—such as reaching out to customers to understand the changes in their usage patterns, offering guidance on more efficient usage, or implementing rate limiting to prevent runaway costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Breaking Down Costs by Tenant
&lt;/h2&gt;

&lt;p&gt;For companies that operate multi-tenant SaaS products, understanding the cost of supporting each tenant is essential. Moesif offers the ability to attribute costs accurately on a per-tenant basis, helping businesses understand how much each client is contributing to the overall expenditure. Tenant-level cost attribution provides crucial visibility that helps in financial planning and customer profitability analysis.&lt;/p&gt;

&lt;p&gt;This tenant-level visibility is especially valuable for SaaS providers who need to assess the financial impact of different tenants. By accurately attributing costs to each tenant, companies can make better decisions about pricing, resource allocation, and even customer support prioritization. For instance, if a particular tenant is driving significantly higher costs compared to others, the business can investigate why this is happening and whether it makes sense to adjust the pricing structure or impose usage limits.&lt;/p&gt;

&lt;p&gt;Additionally, having detailed insights into tenant-level costs allows businesses to better understand the value they provide to their customers. By correlating revenue generated from each tenant with their respective costs, companies can determine which customers are most profitable and which may need more attention to ensure they are a sustainable part of the business. This enables data-driven discussions with customers about the value they are receiving and potential ways to optimize their usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use Moesif to Monitor and Manage Costs
&lt;/h2&gt;

&lt;p&gt;To achieve effective cost monitoring and control with Moesif, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Set Up Real-Time Monitoring&lt;/strong&gt;: &lt;a href="https://www.moesif.com/features/api-monitoring" rel="noopener noreferrer"&gt;Moesif allows you to track every API request in real time&lt;/a&gt;. Begin by integrating Moesif into your API infrastructure. Once integrated, Moesif captures critical data such as request paths, response times, and payloads, which helps you understand your API's overall usage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create Custom Dashboards&lt;/strong&gt;: &lt;a href="https://www.moesif.com/features/api-dashboards" rel="noopener noreferrer"&gt;Use Moesif’s custom dashboards to visualize cost-related metrics&lt;/a&gt;. You can build dashboards that show detailed breakdowns by customer, endpoint, or type of request. This helps in identifying which parts of your API are contributing most to the costs and which users are driving up usage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use Cost Analysis Metrics&lt;/strong&gt;: &lt;a href="https://www.moesif.com/features/product-catalog" rel="noopener noreferrer"&gt;Moesif provides the ability to assign costs to different API transactions&lt;/a&gt;. Set up metrics to track usage by customer, including the number of calls, data transferred, and the complexity of prompts sent to LLMs. This data helps in identifying the top customers contributing to costs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Set Alerts for Usage Spikes&lt;/strong&gt;: &lt;a href="https://www.moesif.com/features/api-monitoring" rel="noopener noreferrer"&gt;Establish alert triggers to notify you when a customer’s usage exceeds predefined thresholds&lt;/a&gt;. This helps in mitigating unexpected cost spikes before they impact your budget. Alerts can be set for different dimensions, such as high-frequency API calls or unusually large payloads.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Segment Customers by Usage&lt;/strong&gt;: &lt;a href="https://www.moesif.com/docs/api-analytics/segmentation/" rel="noopener noreferrer"&gt;Use Moesif’s segmentation tools to categorize customers based on their usage patterns&lt;/a&gt;. Identify heavy users who make complex or high-frequency requests and create segments for them. This will help tailor pricing models that better reflect the costs they incur.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Analyze Customer Behavior&lt;/strong&gt;: &lt;a href="https://www.moesif.com/features/behavioral-cohorts" rel="noopener noreferrer"&gt;Use the behavioral analysis tools in Moesif to understand how different customer segments interact with your API&lt;/a&gt;. Understanding the journey customers take and which endpoints they hit the most often allows you to optimize both user experience and cost efficiency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Leverage Cost Attribution Features&lt;/strong&gt;: For multi-tenant SaaS platforms, &lt;a href="https://www.moesif.com/solutions/metered-api-billing" rel="noopener noreferrer"&gt;Moesif’s cost attribution features allow you to break down costs per tenant&lt;/a&gt;. This provides clarity on which tenants are using the most resources, enabling precise cost allocation and ensuring pricing structures are fair and reflective of usage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Optimize Usage Patterns&lt;/strong&gt;: Once you have visibility into the cost drivers, work with customers to optimize their usage. This could involve helping them craft more efficient prompts, advising them on reducing request frequency, or recommending features that provide the most value at a lower cost.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Driving Profitability with Moesif
&lt;/h2&gt;

&lt;p&gt;Moesif doesn’t just help you monitor costs—it also helps optimize profitability. By combining usage data with cost insights, companies can better understand the relationship between customer behaviors and profitability. Moesif enables organizations to identify opportunities for upselling high-value features to customers who are already consuming significant resources or even to &lt;a href="https://www.moesif.com/features/api-governance-rules" rel="noopener noreferrer"&gt;introduce throttling mechanisms&lt;/a&gt; for customers whose usage exceeds acceptable cost limits.&lt;/p&gt;

&lt;p&gt;These actionable insights empower teams to proactively manage both customer experience and operational costs, ensuring that AI APIs remain both effective and profitable. Moesif's real-time monitoring, alert capabilities, and advanced analytics equip companies to make data-driven decisions that enhance efficiency and boost the bottom line. For example, identifying the most resource-intensive endpoints allows engineering teams to optimize those endpoints, potentially reducing the cost per request and improving the overall performance of the API.&lt;/p&gt;

&lt;p&gt;Moesif’s analytics enable teams to understand long-term trends in API usage and costs, which is vital for strategic planning. By visualizing how costs evolve over time and how different customers contribute to those trends, companies can adjust their growth strategies and anticipate future needs. Whether it's refining pricing models, reallocating infrastructure resources, or changing product offerings, Moesif's data-driven approach ensures that decisions are backed by comprehensive insights.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for Cost Management
&lt;/h2&gt;

&lt;p&gt;To effectively manage costs associated with AI APIs, companies should adopt several best practices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Segment Customers by Usage&lt;/strong&gt;: Use data to identify different segments of customers based on how they interact with your APIs. This can help tailor pricing and optimize resource use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimize Prompts and Requests&lt;/strong&gt;: Work with customers to streamline their prompts and requests to minimize computational overhead while maintaining effectiveness.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set Up Alerts for Unusual Activity&lt;/strong&gt;: Leverage Moesif’s alert system to quickly respond to unexpected usage spikes that could lead to significant cost increases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regularly Review Cost and Usage Data&lt;/strong&gt;: Periodically assess your API usage and cost data to identify trends and make adjustments as needed. Moesif’s dashboards make this easy to visualize and analyze.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement Tiered Pricing Models&lt;/strong&gt;: Consider &lt;a href="https://www.moesif.com/blog/api-analytics/product/Pricing-Strategies-for-APIs/" rel="noopener noreferrer"&gt;implementing pricing models&lt;/a&gt; that align more closely with the value delivered to customers and the costs incurred by their usage.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;AI has opened up remarkable opportunities for innovation, but it has also brought new challenges in managing the costs associated with API consumption. Moesif helps organizations overcome these challenges by offering comprehensive observability into the usage and cost of AI-driven APIs. From understanding LLM usage patterns to accurately attributing costs across tenants, Moesif provides the tools needed to turn complex cost structures into clear, actionable insights.&lt;/p&gt;

&lt;p&gt;By adopting best practices for cost management and leveraging Moesif’s advanced analytics and monitoring tools, businesses can ensure they stay ahead of the cost curve while continuing to deliver exceptional value through their AI APIs. If you're ready to take control of your API costs and get a clearer picture of your AI-powered applications, &lt;a href="https://www.moesif.com/wrap?onboard=true&amp;amp;utm_source=blog&amp;amp;utm_medium=bottom-cta&amp;amp;utm_content=DEVTO-Monitoring-Cost-and-Consumption-of-AI-APIs-and-Apps" rel="noopener noreferrer"&gt;start your 14-day free trial with Moesif today&lt;/a&gt;—no credit card required.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>moesif</category>
    </item>
    <item>
      <title>Debugging Production APIs with Postman and Moesif</title>
      <dc:creator>Moesif Staff</dc:creator>
      <pubDate>Wed, 19 Feb 2025 18:03:05 +0000</pubDate>
      <link>https://forem.com/moesif/debugging-production-apis-with-postman-and-moesif-39pl</link>
      <guid>https://forem.com/moesif/debugging-production-apis-with-postman-and-moesif-39pl</guid>
      <description>&lt;p&gt;Debugging APIs can be a challenge for any developer dealing with REST APIs. Trying to create an exact API request, especially for highly complex requests with large bodies and multiple headers, is essential but also tough to do. By using a tool like Postman to create a request for debugging and API testing purposes, you can easily replay an API request with the exact configuration of the original request. This can enable developers to reproduce the scenario when running API tests and debugging in a consistent manner.&lt;/p&gt;

&lt;p&gt;Moesif can also help to make the process of debugging and running API tests against your REST APIs easier as well. Since Moesif receives data around every aspect of a request, it makes it a great platform to export all of the details of a request so it can easily be replayed to assist with debugging. In Moesif, you can easily export all of your API call data into a Postman Collection. This helps developers to replicate the calls in Postman automatically by importing the generated Collection.&lt;/p&gt;

&lt;p&gt;Debugging best practices aim to ensure that the conditions in which you are debugging an error match exactly with the conditions that caused the error. By using Moesif’s export functionalities as part of your debugging and API testing method, you’re guaranteed that the API endpoint and debugger are receiving the same request data that caused the error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exporting Events in Moesif
&lt;/h2&gt;

&lt;p&gt;To export calls from Moesif, first, navigate to the &lt;strong&gt;Live Event Log&lt;/strong&gt; screen. Click &lt;strong&gt;Create New&lt;/strong&gt; on the left navigation pane and select &lt;strong&gt;Live Event Log&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz5lft3o0dxu0zgefkjfm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz5lft3o0dxu0zgefkjfm.png" alt="Moesifs Live Event Log" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can choose the calls you wish to export into the Postman Collection. You can easily select the desired calls by &lt;strong&gt;checking the boxes&lt;/strong&gt; next to them, and then export them by clicking on &lt;strong&gt;Run in Postman&lt;/strong&gt;.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;By exporting the calls to a Postman API collection, you will have all the necessary components at your disposal to recreate an API call. This includes the body, headers, parameters, and any other relevant elements.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After clicking the button labeled &lt;strong&gt;Run in Postman&lt;/strong&gt;, a modal window will pop up, giving you the option to download the collection. Simply click on &lt;strong&gt;Download Postman Collection&lt;/strong&gt; to acquire the collection file in a easy to read json format and save it on your local machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi1hymkcxqjc38d0yxyrm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi1hymkcxqjc38d0yxyrm.png" alt="Downloading a Postman collection in Moesif" width="660" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Postman API Collection will be downloaded and ready to load into Postman. Let’s check out how to do that!&lt;/p&gt;

&lt;h2&gt;
  
  
  Importing the Collection in Postman
&lt;/h2&gt;

&lt;p&gt;Open Postman and click the &lt;strong&gt;Collections&lt;/strong&gt; tab on the left side of screen. Click the &lt;strong&gt;Import&lt;/strong&gt; button on the top left.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9cv6puryt98037o86pfz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9cv6puryt98037o86pfz.png" alt="Import collection into Postman" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After that, a modal will appear where you can either select the Postman Collection file or drag-and-drop it onto the modal to import it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5zvcmk4dwijoknqgj802.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5zvcmk4dwijoknqgj802.png" alt="Postman import file" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The collection will be displayed in the &lt;strong&gt;Collections&lt;/strong&gt; pane in Postman.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj0jg573wl32fwlkfr9p0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj0jg573wl32fwlkfr9p0.png" alt="Postman Collection List" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Replaying Requests and Debugging
&lt;/h2&gt;

&lt;p&gt;Choose one of the requests to replay and all the details of the HTTP request will be shown, including the parameters, headers, and body.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F041zyuloozm9g39bgop1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F041zyuloozm9g39bgop1.png" alt="Postman API list view" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the blue &lt;strong&gt;Send&lt;/strong&gt; button located beside the URL to send a Postman API call which is an exact copy of the web API request you are trying to debug to the desired endpoint. By doing this, your debugger will be loaded with the same data as the error call you are currently debugging since it has been loaded from the exported Postman API Collection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Experience It for Yourself
&lt;/h2&gt;

&lt;p&gt;The next time you encounter an issue while debugging or conducting API tests with your REST API , you can make use of Moesif to easily replicate the HTTP requests that led to the error. No need to worry about missing details by manually inputting the HTTP request into Postman. Simply export the request from Moesif and replay it through Postman to debug your API in the exact same conditions that caused the issue in the first place. Just choose the requests that you need, export them, and start debugging. By doing so, you’ll be able to hit your debugger breakpoint with the same data included in the original request. Debugging REST API errors should be a breeze, and with Postman and Moesif, it definitely is!&lt;/p&gt;

&lt;p&gt;If you want to enhance your debugging and API testing capabilities, Moesif is the way to go. It lets you effortlessly replicate the HTTP request that caused an error in your REST API code, without any risk of omitting important details. You can then export the requests and replay them in Postman to debug your API under the exact same conditions that led to the issue. With &lt;a href="https://www.moesif.com/signup?utm_campaign=Int-site&amp;amp;utm_source=blog&amp;amp;utm_medium=body-cta&amp;amp;utm_term=devto-debugging-production-with-postman" rel="noopener noreferrer"&gt;Moesif&lt;/a&gt;, debugging REST API errors becomes a breeze.&lt;/p&gt;

</description>
      <category>api</category>
      <category>postman</category>
      <category>moesif</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Best Practices: Maximizing AI Revenue Growth with Customer Success</title>
      <dc:creator>Xing Wang</dc:creator>
      <pubDate>Fri, 22 Dec 2023 22:32:48 +0000</pubDate>
      <link>https://forem.com/moesif/best-practices-maximizing-ai-revenue-growth-with-customer-success-14lg</link>
      <guid>https://forem.com/moesif/best-practices-maximizing-ai-revenue-growth-with-customer-success-14lg</guid>
      <description>&lt;p&gt;In the world of artificial intelligence, maximizing revenue growth is of great importance for businesses seeking to capitalize on their AI products. Like all SaaS tools, the defining linchpin in widespread product adoption is the end user experience. There is an intricate relationship between customer success and &lt;a href="https://www.moesif.com/solutions/metered-api-billing?utm_campaign=Int-site&amp;amp;utm_source=blog&amp;amp;utm_medium=sticky-cta&amp;amp;utm_content=Maximizing-AI-Revenue-Growth"&gt;how to make money&lt;/a&gt; from AI products, as the successful integration of AI products into a given market relies heavily on not only the technological capabilities of the solution but also on how effectively businesses cater to their customers.&lt;/p&gt;

&lt;p&gt;The intersection of customer success with how to make money from AI offerings is a strategic balance where technology meets user satisfaction and drives &lt;a href="https://www.moesif.com/blog/api-monetization/api-strategy/What-Is-PLG-For-AI/?utm_campaign=Int-site&amp;amp;utm_source=blog&amp;amp;utm_medium=body-cta&amp;amp;utm_content=Maximizing-AI-Revenue-Growth"&gt;business growth&lt;/a&gt;. As businesses introduce AI products to the market, an emphasis on customer success becomes a differentiator. Customer success practices including personalized onboarding, continuous support, and proactive engagement are not just ancillary components of CS success. They’re integral elements that ensure users derive optimal value from AI solutions, encouraging customer satisfaction and lowering churn. &lt;/p&gt;

&lt;p&gt;The commercial viability of an AI tool is not solely contingent on its technical capabilities; if that were the case, there would be a greater monopoly in the market. Rather, success hinges on how effectively businesses can align their AI technology with the unique needs of their customers, fostering loyalty, and driving sustained revenue growth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Commercializing AI-Based Offerings: An Overview
&lt;/h2&gt;

&lt;p&gt;Generating revenue from AI-based offerings is a multifaceted process to turn innovative (or more user friendly than competition) AI technology into a marketable and profitable product. As mentioned before, sometimes a product doesn’t have to be groundbreaking to be successful. Rather, a focus on a cohesive and enjoyable user experience can be the deciding factor in a company’s choice of machine learning solution. &lt;/p&gt;

&lt;h3&gt;
  
  
  Value Proposition
&lt;/h3&gt;

&lt;p&gt;The journey begins with identifying opportunities in a given market where AI solutions can address specific needs or challenges. This phase includes market research to understand customer demands, competition, and potential niches for AI software. Challenges arise in navigating the complexity of AI development, from the initial investment in cutting-edge technology and talent acquisition to research and development. The &lt;a href="https://www.moesif.com/features/api-governance-rules?utm_campaign=Int-site&amp;amp;utm_source=blog&amp;amp;utm_medium=body-cta&amp;amp;utm_content=Maximizing-AI-Revenue-Growth"&gt;commercialization process&lt;/a&gt; also entails creating a compelling value proposition, highlighting how the AI application addresses pain points and delivers unique benefits to potential users. &lt;/p&gt;

&lt;h3&gt;
  
  
  AI in Healthcare
&lt;/h3&gt;

&lt;p&gt;One example of an industry that could significantly benefit from the adoption of AI-based software is the healthcare sector. Generative AI tools and AI algorithms have the potential to revolutionize various aspects of healthcare, ranging from diagnostic tools and personalized treatment plans to administrative processes assisted by AI model technology. For this example, AI startups for healthcare would need to build a HIPAA compliant AI powered tool, and likely trained on unique, complex language models that may not be easy for the AI product provider to set up. As such, AI companies would need to satisfy a need that could be monetized effectively to offset the costs associated with creation and maintenance of a medical LLM, or some kind of integration accessing a current provider like Med-PaLM. &lt;/p&gt;

&lt;h3&gt;
  
  
  Pricing Model
&lt;/h3&gt;

&lt;p&gt;Opportunities emerge as businesses position their generative AI solutions as efficient and capable of driving significant value for users. Once developed, deciding on a pricing model strategy becomes a business priority. Having effective marketing strategies is one thing, but pricing your AI based product is a can of worms on its own. Because of the expensive nature of offering an AI system, the upstart and &lt;a href="https://www.moesif.com/blog/api-monetization/api-strategy/AI-Infrastracture-Costs/?utm_campaign=Int-site&amp;amp;utm_source=blog&amp;amp;utm_medium=body-cta&amp;amp;utm_content=Maximizing-AI-Revenue-Growth"&gt;maintenance costs&lt;/a&gt; associated with artificial intelligence  and big data models can’t be overlooked and should be accounted for in the pricing structure of your AI API. By employing a usage based pricing plan, businesses can directly offset the actual usage of their AI products. Usage based pricing also enables organizations to tailor sales approaches to appeal to a diverse customer base, including self service data scientist or data analyst users. &lt;/p&gt;

&lt;h3&gt;
  
  
  Customer Success
&lt;/h3&gt;

&lt;p&gt;Ensuring user-friendly interfaces, seamless integration into existing workflows, and comprehensive customer service further enhance the customer experience and, by result, improve customer success. Throughout this journey, the iterative nature of AI development enables continuous improvements and adaptations based on user feedback and &lt;a href="https://www.moesif.com/features/api-analytics?H1=API-Analytics-for-AI-Apps&amp;amp;utm_campaign=Int-site&amp;amp;utm_source=blog&amp;amp;utm_medium=body-cta&amp;amp;utm_content=Maximizing-AI-Revenue-Growth"&gt;analytics&lt;/a&gt;, presenting ongoing opportunities for refinement and feature expansion. Ultimately, successful commercialization via the AI revolution requires transformative capabilities to not only meet user needs but to create new opportunities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost Considerations in AI Commercialization
&lt;/h2&gt;

&lt;p&gt;Building AI products comes with unique challenges and expenses that reflect the complex nature of artificial intelligence development.Substantial computational power is necessary to train and run AI models effectively, often requiring specialized hardware which can be expensive to acquire and maintain. Another challenge lies in procuring and managing large volumes of high-quality big data for training AI models. Acquiring datasets for large language models can be time-consuming and costly. Additionally, ensuring data privacy and compliance with industry or governmental regulations adds another layer of complexity and expense.&lt;/p&gt;

&lt;p&gt;Beyond initial model costs, research and development can be substantial financial strains, given the iterative nature of AI development and prompt engineering. Continuous testing, algorithm refinement, and research based on the latest natural language processing advancements require ongoing investment, which can be costly in terms of hours and money. &lt;/p&gt;

&lt;h2&gt;
  
  
  Customer Success Best Practices for AI Revenue Growth
&lt;/h2&gt;

&lt;p&gt;The role of customer success holds pivotal importance as businesses strive to not only deliver cutting-edge technology but also ensure that customers derive maximum value from their AI investments. Customer service for generative AI tools extends beyond the initial purchase, as lasting partnerships are cultivated through personalized experiences and ongoing support. This approach can maximize customer satisfaction, retention, and, ultimately, foster long-term revenue growth.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategies for Maximizing Customer Success
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Personalized Onboarding&lt;/strong&gt;: Tailor the onboarding process to individual customer use cases to ensure a smooth introduction to your generative AI solutions. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ongoing Support&lt;/strong&gt;: Continuous support is essential for addressing evolving customer requirements. Responsive and knowledgeable dedicated customer success teams and intuitive, full-scope documentation reinforces customer confidence and satisfaction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proactive Engagement&lt;/strong&gt;: Anticipating customer needs and engaging proactively based on user insights can cultivate a stronger partnership. Regular check-ins, support around bottlenecks, and timely content updates or enhancements demonstrate a commitment to customer success.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Customer Success through Moesif
&lt;/h3&gt;

&lt;p&gt;Moesif can play a pivotal role in elevating customer success for any SaaS product, but particularly within the AI landscape. By providing powerful, real-time insights into how customers interact with machine learning products, Moesif empowers AI companies to understand user behavior, optimize product usage, and intervene proactively when necessary. &lt;/p&gt;

&lt;p&gt;For example, &lt;a href="https://www.moesif.com/features/api-monitoring?utm_campaign=Int-site&amp;amp;utm_source=blog&amp;amp;utm_medium=body-cta&amp;amp;utm_content=Maximizing-AI-Revenue-Growth"&gt;monitoring API usage&lt;/a&gt; as it relates to free trial milestones and notifying sales teams of MQLs at critical junctures enables timely engagement, enabling faster conversion of trial users into paying customers. By analyzing user behavior, customer support teams can offer personalized, targeted content and support to help users maximize their usage of your AI product, streamlining their workflows and reducing your churn rate. Additionally, Moesif's ability to detect changes in API usage patterns offers valuable signals for potential upsells for non-technical teams, ensuring that AI startups can align their pricing and offerings with customer growth.&lt;/p&gt;

&lt;p&gt;Moesif's ability to manage customer success metrics extends beyond mere &lt;a href="https://www.moesif.com/features/api-analytics?utm_campaign=Int-site&amp;amp;utm_source=blog&amp;amp;utm_medium=body-cta&amp;amp;utm_content=Maximizing-AI-Revenue-Growth"&gt;analytics&lt;/a&gt;; it can be a strategic enabler for AI-based organizations to nurture strong, value-driven relationships with their customers. Enhances your overall customer experience and position your AI products for &lt;a href="https://www.moesif.com/solutions/api-product-management?utm_campaign=Int-site&amp;amp;utm_source=blog&amp;amp;utm_medium=body-cta&amp;amp;utm_content=Maximizing-AI-Revenue-Growth"&gt;sustained success&lt;/a&gt; and revenue growth in an ever-evolving landscape with Moesif.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Implementing Volume-Based Pricing</title>
      <dc:creator>Xing Wang</dc:creator>
      <pubDate>Fri, 15 Dec 2023 23:55:38 +0000</pubDate>
      <link>https://forem.com/moesif/implementing-volume-based-pricing-75</link>
      <guid>https://forem.com/moesif/implementing-volume-based-pricing-75</guid>
      <description>&lt;p&gt;When monetizing APIs, a popular approach is volume-based pricing. Of all the monetization models you can apply to your APIs, volume-based pricing is one of the easiest to implement. This blog will cover the basics of applying volume-based pricing to your APIs so your customers can be billed accordingly. Let’s start by looking at the finer details of volume-based pricing regarding monetized APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is volume-based pricing?
&lt;/h2&gt;

&lt;p&gt;Volume-based pricing is a billing strategy commonly used to monetize APIs. This approach adjusts the cost based on the volume of usage. Typically, usage is measured by the number of API calls or data transferred. This pricing model is designed to cater to diverse user bases, from startups to large enterprises, by offering flexible pricing that aligns with their usage levels. With volume-based pricing, the unit price traditionally goes down as usage increases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Implement Volume-Based Pricing?
&lt;/h3&gt;

&lt;p&gt;As mentioned, in most volume-based pricing models for APIs, the price per API call typically decreases as the volume of usage increases. This pricing structure incentivizes higher usage while making the service more cost-effective for large-volume users. Here are a few facets and benefits of volume-based pricing.&lt;/p&gt;

&lt;h4&gt;
  
  
  Tiered Pricing Structure
&lt;/h4&gt;

&lt;p&gt;API providers often use a tiered pricing model. Each tier has a set range of API calls, and the cost per call decreases as you move to higher tiers. For example, the first 1,000 calls might cost 10 cents each, but calls 1,001 to 10,000 might cost only 8 cents each, and so on.&lt;/p&gt;

&lt;h4&gt;
  
  
  Encouraging Higher Usage
&lt;/h4&gt;

&lt;p&gt;Volume-based pricing encourages users to increase API usage since the unit cost becomes more economical at higher volumes. This can be particularly appealing for growing businesses anticipating increased API usage.&lt;/p&gt;

&lt;h4&gt;
  
  
  Cost Predictability
&lt;/h4&gt;

&lt;p&gt;While the per-call cost decreases, customers can also predict their expenses more accurately as they understand how much the cost will reduce as they scale up. This makes cost a very linear calculation, one that is easy to forecast.&lt;/p&gt;

&lt;h4&gt;
  
  
  Balancing Accessibility and Revenue
&lt;/h4&gt;

&lt;p&gt;For API providers, this model helps balance making their services accessible to smaller users (who might be sensitive to high costs at low volumes) while still generating significant revenue from larger customers.&lt;/p&gt;

&lt;h4&gt;
  
  
  Custom Agreements for Very High Volumes
&lt;/h4&gt;

&lt;p&gt;Some API providers might negotiate custom pricing agreements for high-volume users, which could deviate from the standard tiered model to better suit large clients' specific needs. These agreements can still keep the essence of volume-based pricing but on a more customizable level.&lt;/p&gt;

&lt;p&gt;A massive amount of benefits can be derived from a relatively simple implementation using volume-based pricing models. It’s easy for customers to understand and a great revenue driver at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Moesif?
&lt;/h2&gt;

&lt;p&gt;Regarding API monetization and implementing volume-based pricing, Moesif is a go-to solution for making implementation easy. At its core, Moesif is an advanced API analytics and monetization platform. Within the platform, users can uncover critical insights into how their APIs are being used and efficiently monetize their existing APIs. It's designed to help companies optimize, troubleshoot, and secure their API infrastructures, with a strong emphasis on aiding in effective API monetization strategies. Let’s look at some functionality in greater detail:&lt;/p&gt;

&lt;h3&gt;
  
  
  API Usage Analytics
&lt;/h3&gt;

&lt;p&gt;Moesif allows businesses to track and analyze how their APIs are used. This includes detailed data on API calls, error rates, endpoint performance, and user behavior patterns.&lt;/p&gt;

&lt;h3&gt;
  
  
  API Monetization Capabilities
&lt;/h3&gt;

&lt;p&gt;A standout feature of Moesif is its robust support for API monetization, allowing users to track and charge users for API usage. API monetization with Moesif enables businesses to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Track Billing Metrics&lt;/strong&gt;: Understand and monitor API usage in the context of billing and revenue generation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identify Revenue Opportunities&lt;/strong&gt;: Discover which features or endpoints are most popular and generate the most revenue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement Volume-Based Pricing&lt;/strong&gt;: Easily track and manage usage-based billing, such as volume-based pricing models.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Customer Insights
&lt;/h3&gt;

&lt;p&gt;Moesif’s analytics capabilities provide deep insights into customer usage patterns, helping businesses understand which customers are the most active or potentially at risk of churning. This also helps companies determine which APIs they should charge for and how much they can charge for usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-Time Monitoring and Alerts
&lt;/h3&gt;

&lt;p&gt;The platform offers real-time monitoring of API performance, alerting businesses to issues as they arise, such as an increase in latency, enabling quick response and resolution.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Moesif’s Product Catalog?
&lt;/h2&gt;

&lt;p&gt;Moesif's Product Catalog is a comprehensive tool designed to manage billing plans and prices for APIs. It serves as a centralized platform where business owners can create, manage, view, and archive various billing plans and prices, streamlining the product creation process within Moesif. This integration ensures API product compatibility with Moesif’s API metering and billing solution. The key benefits of using Moesif's Product Catalog include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ease of Use&lt;/strong&gt;: Business owners can create plans and prices directly within the Moesif UI, eliminating the need to access multiple systems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration with Billing Systems&lt;/strong&gt;: Moesif integrates with your billing system, which remains the system of record. Any plan or price created in Moesif will also appear in your billing provider, ensuring seamless accounting and financial management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Support for Multiple Billing Systems&lt;/strong&gt;: The Product Catalog is designed to work with multiple billing systems simultaneously, such as Zuora for enterprise customers and Stripe for self-service options.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How to implement volume-based API pricing
&lt;/h2&gt;

&lt;p&gt;To implement volume-based pricing, we will use Stripe and Moesif. With Moesif’s Product Catalog and Billing Meter functionality, we can do all this right within the Moesif platform!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: You need to enable and configure the Stripe plugin in Moesif for the steps below to work. The simple steps to set that up are available &lt;a href="https://www.moesif.com/docs/metered-billing/integrate-with-stripe/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Create the Plan
&lt;/h3&gt;

&lt;p&gt;After logging into Moesif, navigate to the &lt;strong&gt;Product Catalog&lt;/strong&gt; by clicking the corresponding menu item in the left-side navigation. On the &lt;strong&gt;Plans&lt;/strong&gt; page, click the &lt;strong&gt;Create New&lt;/strong&gt; button in the top-right.&lt;/p&gt;

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

&lt;p&gt;On the &lt;strong&gt;Create Plan&lt;/strong&gt; screen, you’ll fill out the &lt;strong&gt;plan name&lt;/strong&gt; and select the billing provider. In this case, we will select &lt;strong&gt;Stripe&lt;/strong&gt;. Once done, click on the &lt;strong&gt;Create&lt;/strong&gt; button in the top-right.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Create the Price
&lt;/h3&gt;

&lt;p&gt;Next, we will create the &lt;strong&gt;Price&lt;/strong&gt; in Moesif by clicking on the &lt;strong&gt;Price&lt;/strong&gt; menu item under the &lt;strong&gt;Product Catalog&lt;/strong&gt; on the left-side menu. On the &lt;strong&gt;Price&lt;/strong&gt; screen, click the &lt;strong&gt;Create New&lt;/strong&gt; button in the top-right.&lt;/p&gt;

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

&lt;p&gt;On the next screen, you’ll add the &lt;strong&gt;price name&lt;/strong&gt; and select the &lt;strong&gt;Linked Plan&lt;/strong&gt; from the dropdown. In this case, we will select “My API Plan,” the plan we made in the previous step above.&lt;/p&gt;

&lt;p&gt;Under &lt;strong&gt;Pricing&lt;/strong&gt;, we will select the &lt;strong&gt;Pricing Model&lt;/strong&gt; as “Volume” and leave the &lt;strong&gt;Meter Usage as&lt;/strong&gt; with the defaults (unless you want to change it for your specific use case). You can add each of your tiers under the &lt;strong&gt;Price Structure&lt;/strong&gt; section. You can click the &lt;strong&gt;Add Tier&lt;/strong&gt; button at the bottom if you need another tier. You’ll see we’ve added four tiers in the example below.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Create the Billing Meter
&lt;/h3&gt;

&lt;p&gt;Now that the plan and price have been created, we will create the Billing Meter to record the usage and send it to the correct plan in Stripe. To do that, we will click on the &lt;strong&gt;Billing Meters&lt;/strong&gt; menu item in the left-side menu. On the &lt;strong&gt;Billing Meters&lt;/strong&gt; screen, click the &lt;strong&gt;Add Billing Meter&lt;/strong&gt; button in the top-right of the screen.&lt;/p&gt;

&lt;p&gt;This will bring you to the &lt;strong&gt;Create Billing Meter&lt;/strong&gt; screen. Here, you will give the meter a &lt;strong&gt;name&lt;/strong&gt;, link it to our plan and price under the &lt;strong&gt;Link to&lt;/strong&gt; section, and create our &lt;strong&gt;filter&lt;/strong&gt; and &lt;strong&gt;metrics&lt;/strong&gt;. Below is an example of a meter for a specific API endpoint (“/test-service”) that will count each API call and attribute it to the “Volume Pricing” price that we created earlier. Once you’ve created your meter, click &lt;strong&gt;Create&lt;/strong&gt; in the top-right to create and activate the meter.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Profit!
&lt;/h3&gt;

&lt;p&gt;With this in place, API usage will be billed based on your volume-based pricing model. This example has shown you how easy it is to create a volume-based pricing model and track usage towards it with Moesif and Stripe. Of course, this could also be done with other billing providers, including Chargebee, Zuora, and many others.&lt;/p&gt;

</description>
      <category>api</category>
      <category>monetization</category>
    </item>
    <item>
      <title>Self-Serve and Sales-Led API Monetization — Unlocking Product Led Growth</title>
      <dc:creator>Xing Wang</dc:creator>
      <pubDate>Fri, 08 Dec 2023 23:39:51 +0000</pubDate>
      <link>https://forem.com/moesif/self-serve-and-sales-led-api-monetization-unlocking-product-led-growth-339o</link>
      <guid>https://forem.com/moesif/self-serve-and-sales-led-api-monetization-unlocking-product-led-growth-339o</guid>
      <description>&lt;p&gt;There are as many monetization pitfalls as there are methods of monetizing APIs. As the market has shifted over the years, selling APIs and creating product userbases has become more important than ever. Today, we’re going to discuss a strategy that can lead to explosive growth – let’s talk self-service and product led growth.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Shifting Product Landscape
&lt;/h2&gt;

&lt;p&gt;The reality is that selling APIs is more difficult than ever before. The modern landscape has shifted dramatically over the years, moving both the consumption model as well as the value proposition for most adoptees of API platforms. A monolithic API provider with structured UIs and full-service integration plans used to be the gold standard, delivering a value proposition of a full-service partnership with full-service integration. As the industry and community has shifted towards automated microservices and product led growth, which deliver value through specific and targeted use cases, the idea of buying into a traditional integration approach has diminished substantially for the average API consumer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Decline of Sales-Led Growth
&lt;/h2&gt;

&lt;p&gt;What this has ultimately resulted in is a shift of power between the provider of an API product and the consumer. When integrating a service requires bringing a team on premises to install proprietary systems and launch custom server clusters, the power &lt;strong&gt;and cost&lt;/strong&gt; of this API management overwhelmingly lands in the camp of the software. As this has shifted, the calculus has changed substantially, with the adoptee becoming the powerhouse authority.&lt;/p&gt;

&lt;p&gt;This has undermined the traditional pathway of sales-led API monetization and, subsequently, product growth. Sales-led is conceptually based on the idea that you have something someone else wants, and as such, you should set the price and API monetization strategy as close to the upper limit as possible to extract value and revenue. The problem with this growth strategy is that it assumes the product has a clear value proposition and is an outstanding choice. Today, the consumer has never had so much choice and flexibility, so the idea that a product can extract value through this pathway is not really true in most industries anymore.&lt;/p&gt;

&lt;p&gt;All of this adds up to a simple truth – adoptees are more empowered than ever, and they need to have a solution that meets their specific needs with as little friction in adoption as possible. Each hiccup along the way reduces the likelihood of success and long-term retention of API users. When the authority was in the product, things could afford to be complex or high-friction – after all, the product was what the product was. In today's API marketplace, the product has to be fast to deliver and easy to integrate with as few initial blockers as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Self-Serve Model
&lt;/h2&gt;

&lt;p&gt;Enter the self-serve model. The basic idea of the self-serve model is to lower the friction to onboarding as much as possible by allowing the adoptee to decide on their API consumption and API usage: what they need, how they need it, and how they will pay for it. This can take several forms, but the core concept remains the same – low friction leads to high adoption. Self-serve delivers some major benefits – let’s take a look at a few of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reduced Onboarding Friction
&lt;/h3&gt;

&lt;p&gt;Self-service delivers reduced initial friction through a variety of mechanisms. Using click-through terms of service can help reduce initial legal paperwork and hurdles for an API business and the API developers using a given service alike. Automated integration of an API gateway with a simple step-by-step process can reduce the time to market for integration of the product. Simple payment options can reduce the need for client approvals and expenses. Even something as simple as allowing the user to decide between pre-pay and post-pay agreements can be provided far more simply and effectively through a self-serve business model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enhanced User Experience
&lt;/h3&gt;

&lt;p&gt;The self-serve API monetization model is all about prioritizing the experience of the user, and as such, it delivers the best user experience for both adoptees and developers. Self-serve delivers ease and autonomy – not only is the product easy to use, the adoptee has autonomy to choose how they use it. This autonomy can result in long-term increases in adoption and retention, but even more importantly, it results in a product that is easy to use and easy to advocate for in a competitive API ecosystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scalability and Extensibility
&lt;/h3&gt;

&lt;p&gt;The self-serve model boosts scalability and extensibility by allowing adoptees to choose exactly what services they need when they need it, increasing revenue growth without losing direct monetization upsell opportunities. Standard systems of old might require a full rollout before any benefit can be gained, but with a self-serve approach, this barrier is lowered substantially. If a user only needs a low tier of functionality now, but is likely to use a higher tier later, they will prioritize a solution that allows them to increase their services at will over a system that requires manual deployment or support. With tiered pricing options, a flexible usage based pricing model is attractive to users who may not know their true product usage requirements, where they are granted API access, and which then results in the SaaS company to ensuring API revenue generation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cost Effectiveness
&lt;/h3&gt;

&lt;p&gt;Self-serve systems are cost effective for all parties involved. Because you can tie business logic to the services being rendered at the developer level, costs can be controlled by scaling only to the demand your clients have placed on your self-serve product. For the adoptee, costs can be controlled by using only what API call volume (or whatever monetizable metric you've chosen) is needed at the moment and scaling over time. When an organization decides to open APIs up to the public, they not only join the API economy, but diversify their revenue recognition. This increased cost effectiveness positions your API to be a powerhouse option, as flexibility is almost always going to be valued quite highly on a ranked list of attributes for services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accelerating the Flywheel - How Self-Serve Feeds Product Led Growth
&lt;/h2&gt;

&lt;p&gt;Self-serve models unlock a massive potential benefit in the form of product led growth acceleration. product led growth is a simple concept – the product should sit at the center of your growth, driving user acquisition, conversion, and retention. A self-serve model is the first big step towards accelerating this process.&lt;/p&gt;

&lt;p&gt;Consider what a typical self-serve model looks like in practical terms. First, your user onboards and decides to check out the product. From that moment, everything rides on the product – all the user needs to experience is their “Aha” moment, the moment where they realize the product is a good fit for their use case. Flexible adoption through a self-service API business model increases the chances of this happening, and a good product should provide ample opportunity for multiple “Aha” moments.&lt;/p&gt;

&lt;p&gt;Once the user has had that moment, however, the self-service models kicks the product led growth model into high gear. As the adoptee accelerates their use, the sales team should engage their efforts, identifying new use cases and leading the charge on new product iteration. As the adoptee becomes an evangelist for the product, pushing for adoption and further iteration, the product itself becomes better and more attractive, bringing additional users into the fray, while accelerating the use of the existing client base.&lt;/p&gt;

&lt;p&gt;This strategy has proven itself time and time again for exceptional growth – and all it takes is the creation of a strong product and a low-friction self-serve approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Evangelists
&lt;/h3&gt;

&lt;p&gt;One big benefit of this model is the creation of product evangelists. A product evangelist is a dream come true for many developers – an adoptee who falls in love with the product to such an extent that they evangelize its use and accelerate user awareness and acquisition. In order to effectively creative product evangelists, you have to think like a product evangelist – so what specifically does this type of developer want?&lt;/p&gt;

&lt;p&gt;Product evangelists need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a product they trust,&lt;/li&gt;
&lt;li&gt;a product they can share widely and&lt;/li&gt;
&lt;li&gt;a product others can validate their experience with.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Effective self-serve models and product led growth strategies should meet these demands. A product that is low-friction and transparent in its pricing and functionality is a product that can be trusted – this can be bolstered even further through adequate and effective documentation. A product that is shareable is one that has clarity as to the value proposition – a product should have clear benefits and should document these benefits through blog posts, analytics, customer case studies, white papers, etc. Finally, a product that can validate experience is one where a new user can be influenced into adoption and immediately see the value in the product – in other words, a validating product is a low-friction product.&lt;/p&gt;

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

&lt;p&gt;Ultimately, a self-service model is an incredibly powerful way to enable product led growth in API monetization, and should be considered within the full context of the form and functionality of the API. With proper attention, developers can create an incredibly powerful product led growth flywheel that is powered by a self-serve model which compounds evangelists into new evangelists day by day – and all it takes is a great product and the right intent.&lt;/p&gt;

</description>
      <category>api</category>
      <category>productivity</category>
      <category>monetization</category>
      <category>apimonetization</category>
    </item>
  </channel>
</rss>
