<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Apitally</title>
    <description>The latest articles on DEV Community by Apitally (@apitally).</description>
    <link>https://dev.to/apitally</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%2F7959%2F09de52ee-e778-4749-b184-1dcfc997b6fe.png</url>
      <title>DEV Community: Apitally</title>
      <link>https://dev.to/apitally</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/apitally"/>
    <language>en</language>
    <item>
      <title>Getting started with logging in FastAPI</title>
      <dc:creator>Simon Gurcke</dc:creator>
      <pubDate>Mon, 03 Nov 2025 10:41:24 +0000</pubDate>
      <link>https://dev.to/apitally/getting-started-with-logging-in-fastapi-325d</link>
      <guid>https://dev.to/apitally/getting-started-with-logging-in-fastapi-325d</guid>
      <description>&lt;p&gt;Logging is critical for understanding and debugging FastAPI apps in production. When things go wrong, logs are usually the best starting point for any investigation.&lt;/p&gt;

&lt;p&gt;For APIs, logging can be broken down into two categories: &lt;strong&gt;request logs&lt;/strong&gt;, which record individual API requests and responses, and &lt;strong&gt;application logs&lt;/strong&gt;, which contain detailed messages from your application's code. Ideally, both types of logs are correlated, meaning each log record is linked to the API request.&lt;/p&gt;

&lt;p&gt;In this article, we'll cover the basics of logging and log correlation in FastAPI. We'll also introduce &lt;a href="https://apitally.io/fastapi" rel="noopener noreferrer"&gt;Apitally&lt;/a&gt; as a simple solution for capturing request logs and correlated application logs with minimal effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic request logging
&lt;/h2&gt;

&lt;p&gt;Request logs are a chronological record of every API request your application handles. A typical entry contains at least a timestamp, the HTTP method, request path, and response status code.&lt;/p&gt;

&lt;p&gt;If you're using uvicorn to run your FastAPI app, you actually get basic request logs in your console out of the box. However, these don't include timestamps or correlation IDs, which are necessary to link other log messages with requests.&lt;/p&gt;

&lt;p&gt;We can address these shortcomings with a custom logging middleware. That way we'll have more control over what is included in the logs. For example, we could add the response time to identify slow requests.&lt;/p&gt;

&lt;p&gt;Let's implement this in a simple FastAPI app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;

&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basicConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%(asctime)s [%(name)s] %(levelname)s: %(message)s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Example API&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_requests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;call_next&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;start_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;call_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response_time&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;say_hello&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Saying hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: You can disable uvicorn's built-in logs with the &lt;code&gt;--no-access-logs&lt;/code&gt; option.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The middleware logs request details after the route handler returns, while the route handler also logs during its execution. When two requests are processed concurrently, the log output could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2025-09-30 14:29:00,123 [main] INFO: Saying hello
2025-09-30 14:29:00,123 [main] INFO: Saying hello
2025-09-30 14:29:00,124 [main] INFO: GET /hello 200 0.001s
2025-09-30 14:29:00,124 [main] INFO: GET /hello 200 0.001s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see there is no way to tell which "Saying hello" message belongs to which request. Not a big deal in this simple example, but essential when debugging production issues, especially with high request volumes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Log correlation
&lt;/h2&gt;

&lt;p&gt;To link log messages with requests we need a correlation ID. I highly recommend using the &lt;a href="https://github.com/snok/asgi-correlation-id" rel="noopener noreferrer"&gt;asgi-correlation-id&lt;/a&gt; package for this purpose.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;asgi-correlation-id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The package provides a middleware we can add to our application. It automatically generates a correlation ID for each incoming request. Additionally, we need to configure a log filter and include the correlation ID in our log format. This can't be done using &lt;code&gt;basicConfig&lt;/code&gt;, so we need to use the more verbose &lt;code&gt;dictConfig&lt;/code&gt; instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;asgi_correlation_id&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CorrelationIdMiddleware&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;

&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dictConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;disable_existing_loggers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;filters&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;correlation_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;()&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;asgi_correlation_id.CorrelationIdFilter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uuid_length&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default_value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;formatters&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;standard&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;format&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%(asctime)s [%(correlation_id)s] [%(name)s] %(levelname)s: %(message)s&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;handlers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;console&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;class&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logging.StreamHandler&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;filters&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;correlation_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;formatter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;standard&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;root&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;level&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INFO&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;handlers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;console&lt;/span&gt;&lt;span class="sh"&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="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CorrelationIdMiddleware&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we made two concurrent requests to our API again, we'd get the following output. You can see how the correlation IDs allow us to tell which messages belong together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2025-09-30 14:36:00,123 [main] [50e2646d5e594cb197ae9f3d21de7a57] INFO: Saying hello
2025-09-30 14:36:00,123 [main] [e986f5451c4b4e0fb7fea90dda32608f] INFO: Saying hello
2025-09-30 14:36:00,124 [main] [50e2646d5e594cb197ae9f3d21de7a57] INFO: GET /hello 200 0.001s
2025-09-30 14:36:00,124 [main] [e986f5451c4b4e0fb7fea90dda32608f] INFO: GET /hello 200 0.001s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Beyond log files
&lt;/h2&gt;

&lt;p&gt;We now have correlated logs being written to the console or log files. That's a big step up. But if you've ever tried to debug a production issue by sifting through large log files, you know this approach doesn't scale. For many APIs, the volume of logs can quickly become too large to inspect manually.&lt;/p&gt;

&lt;p&gt;On top of that, request and response metadata from logs often isn't enough. You may need to inspect the full request and response headers and payloads to get to the bottom of an issue. These are impractical to capture in log files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing Apitally
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://apitally.io/fastapi" rel="noopener noreferrer"&gt;Apitally&lt;/a&gt; is designed to solve these exact challenges. It provides a user-friendly and searchable interface for request logs and automatically handles log correlation. You can configure exactly what gets logged, including headers and payloads, while masking rules make it easy to avoid capturing sensitive information.&lt;/p&gt;

&lt;p&gt;To get started, let's install the Apitally SDK for FastAPI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"apitally[fastapi]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, add the Apitally middleware to your FastAPI app. This replaces the need for &lt;span&gt;asgi-correlation-id&lt;/span&gt; and any custom middleware for request logging.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apitally.fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApitallyMiddleware&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ApitallyMiddleware&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-client-id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dev&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# or "prod" etc.
&lt;/span&gt;    &lt;span class="n"&gt;enable_request_logging&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;capture_logs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;# Configure what's included in logs
&lt;/span&gt;    &lt;span class="n"&gt;log_request_headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;log_request_body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;log_response_body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;# Mask headers or body fields using regex
&lt;/span&gt;    &lt;span class="n"&gt;mask_headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^X-Sensitive-Header$&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;mask_body_fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^sensitive_field$&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://docs.apitally.io/reference/python" rel="noopener noreferrer"&gt;this reference&lt;/a&gt; for all available parameters and default masking patterns.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With this configuration, the Apitally SDK automatically captures request and application logs, stores them in a temporary file, and periodically flushes them to Apitally's servers. All this happens asynchronously to not affect your app's performance.&lt;/p&gt;

&lt;p&gt;You can then find and inspect the logs in the Apitally dashboard, with various filtering options. For example, you can filter by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consumer or client IP address&lt;/li&gt;
&lt;li&gt;HTTP method, request URL or response status code&lt;/li&gt;
&lt;li&gt;Request and response body (free text search)&lt;/li&gt;
&lt;li&gt;Response time (find slow requests)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.apitally.io%2Fscreenshots%2Frequest-logs.webp" 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%2Fassets.apitally.io%2Fscreenshots%2Frequest-logs.webp" alt="Request logs in Apitally dashboard" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clicking on a request in the logs brings up a modal with rich details, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Path and query parameters&lt;/li&gt;
&lt;li&gt;Headers (with sensitive headers masked automatically)&lt;/li&gt;
&lt;li&gt;Request and response bodies (supports text and JSON up to 50 KB)&lt;/li&gt;
&lt;li&gt;Correlated application logs&lt;/li&gt;
&lt;li&gt;List of related requests from same client&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.apitally.io%2Fscreenshots%2Frequest-log-item-response-body.webp" 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%2Fassets.apitally.io%2Fscreenshots%2Frequest-log-item-response-body.webp" alt="Response body in request log item" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Apitally stores log data in a ClickHouse database hosted in the US and retains it for 15 days. Log volume limits apply based on the pricing tier, with the lowest tier allowing 1 million requests per month. The highest tier includes 25 million requests and users can enable usage-based pricing to go beyond that limit.&lt;/p&gt;

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

&lt;p&gt;With request logging and correlation IDs in place, we can trace through our logs effectively when troubleshooting issues. Our custom middleware and the asgi-correlation-id package provided a straightforward way to implement this in FastAPI.&lt;/p&gt;

&lt;p&gt;When logs are written only to the console or files, searching and filtering can become inefficient as the volume grows. &lt;a href="https://apitally.io/fastapi" rel="noopener noreferrer"&gt;Apitally&lt;/a&gt; solves this by indexing log data, making it easy to find and inspect requests, responses, and related log messages. Its SDK for FastAPI automates the entire logging pipeline and is ready for production with minimal configuration.&lt;/p&gt;

</description>
      <category>fastapi</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>What makes a good REST API?</title>
      <dc:creator>Simon Gurcke</dc:creator>
      <pubDate>Thu, 16 May 2024 05:59:04 +0000</pubDate>
      <link>https://dev.to/apitally/what-makes-a-good-rest-api-1eo7</link>
      <guid>https://dev.to/apitally/what-makes-a-good-rest-api-1eo7</guid>
      <description>&lt;p&gt;Today, anyone with basic programming skills can build an API.&lt;br&gt;
Frameworks like FastAPI, which provide an intuitive interface and are well documented, make it really easy.&lt;br&gt;
But what does it take to ship and maintain a robust REST API that other developers love using, that always works as expected and scales well?&lt;/p&gt;

&lt;p&gt;This article offers an opinionated overview of REST API best practices covering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API design&lt;/li&gt;
&lt;li&gt;OpenAPI&lt;/li&gt;
&lt;li&gt;Validation&lt;/li&gt;
&lt;li&gt;Rate limiting&lt;/li&gt;
&lt;li&gt;Asynchronous processing&lt;/li&gt;
&lt;li&gt;Monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I won’t go into too much detail in each section, but instead I’ll link to further resources on the topic for those that are interested to go deeper.&lt;/p&gt;
&lt;h2&gt;
  
  
  API design: Follow best practices
&lt;/h2&gt;

&lt;p&gt;A good API has a well thought out and logical design of endpoints, returns data in a manner that is easy to consume, and provides detailed feedback in case of errors.&lt;/p&gt;

&lt;p&gt;There are established best practices for designing API endpoints. These typically include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use plural nouns for resource collections in URIs, e.g. &lt;code&gt;/v1/posts/123&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Avoid using verbs in the endpoint URIs, use an appropriate HTTP method instead&lt;/li&gt;
&lt;li&gt;Keep all characters in the URI lowercase, using hyphens to separate words&lt;/li&gt;
&lt;li&gt;Logically nest endpoints to show relationships. For example, a comment on a blog post should be &lt;code&gt;/v1/posts/{postId}/comments/{commentId}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Include a version in the endpoint URIs, like &lt;code&gt;/v1/posts&lt;/code&gt;, to allow for future updates without breaking existing clients&lt;/li&gt;
&lt;li&gt;Ensure that naming conventions, response formats, and behavior are consistent across all API endpoints&lt;/li&gt;
&lt;li&gt;Use appropriate HTTP status codes in responses, e.g. &lt;code&gt;201 Created&lt;/code&gt; if a new resource was created or &lt;code&gt;403 Forbidden&lt;/code&gt; for an authorization error&lt;/li&gt;
&lt;li&gt;Allow users to filter, sort and paginate through datasets using query parameters and avoid returning excessively large API responses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Further resources:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://stackoverflow.blog/2020/03/02/best-practices-for-rest-api-design/" rel="noopener noreferrer"&gt;Best practices for REST API design&lt;/a&gt; - &lt;em&gt;Stack Overflow Blog&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://opensource.zalando.com/restful-api-guidelines/" rel="noopener noreferrer"&gt;RESTful API Guidelines&lt;/a&gt; - &lt;em&gt;Zalando&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://api.gov.au/" rel="noopener noreferrer"&gt;API Design Standard&lt;/a&gt; - &lt;em&gt;Australian Government&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  OpenAPI: Auto-generate documentation and SDKs
&lt;/h2&gt;

&lt;p&gt;A good API has a complete OpenAPI specification that acts as a contract between the API provider and consumers. It is also used to create comprehensive documentation and SDKs.&lt;/p&gt;

&lt;p&gt;Many web frameworks offer support for auto-generating an OpenAPI specification from endpoint definitions in the API's codebase, either out of the box or via third-party libraries.&lt;br&gt;
You can use annotations to add additional details such as descriptions and examples.&lt;br&gt;
Keeping a single source of truth through a tight integration with the web framework helps keep the specification up-to-date and version-controlled alongside the codebase.&lt;/p&gt;

&lt;p&gt;There are &lt;a href="https://openapi.tools/" rel="noopener noreferrer"&gt;many tools&lt;/a&gt; that use the OpenAPI specification to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate interactive documentation&lt;/li&gt;
&lt;li&gt;Generate client libraries / SDKs in various languages&lt;/li&gt;
&lt;li&gt;Generate mock servers for testing&lt;/li&gt;
&lt;li&gt;Automatically test your API endpoints against the specification&lt;/li&gt;
&lt;li&gt;And more ...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Further resources:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.openapis.org/what-is-openapi" rel="noopener noreferrer"&gt;What is OpenAPI?&lt;/a&gt; - &lt;em&gt;OpenAPI Initiative&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learn.openapis.org/specification/" rel="noopener noreferrer"&gt;The OpenAPI Specification Explained&lt;/a&gt; - &lt;em&gt;OpenAPI Initiative&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openapi.tools/" rel="noopener noreferrer"&gt;OpenAPI.Tools&lt;/a&gt; - &lt;em&gt;APIs You Won't Hate&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Recommended tools&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Redocly/redoc" rel="noopener noreferrer"&gt;Redoc&lt;/a&gt;: Generate API documentation&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://mintlify.com/blog/steps-to-autogenerate" rel="noopener noreferrer"&gt;Mintlify&lt;/a&gt;: Generate API documentation&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hey-api/openapi-ts" rel="noopener noreferrer"&gt;openapi-ts&lt;/a&gt;: Generate API client for TypeScript&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/openapi-generators/openapi-python-client" rel="noopener noreferrer"&gt;openapi-python-client&lt;/a&gt;: Generate API client for Python&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Validation
&lt;/h2&gt;

&lt;p&gt;A good API meticulously validates user input and provides detailed and structured error messages when invalid input is received.&lt;/p&gt;

&lt;p&gt;Being a fundamental task, input validation is supported well by many web frameworks.&lt;br&gt;
FastAPI, for example, uses pydantic for this purpose, and automatically generates responses with well-structured details about validation errors (see example below).&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;"detail"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"missing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"loc"&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="s2"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"msg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Field required"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"input"&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="err"&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="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="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;When creating validation rules, consider the following criteria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type: Ensure input has the correct data type.&lt;/li&gt;
&lt;li&gt;Length: Check that the input has the expected length and define an upper limit (e.g. forbid extremely long string input, or arrays with millions of elements).&lt;/li&gt;
&lt;li&gt;Format: Check string inputs against expected patterns to ensure they are formatted correctly (e.g. dates).&lt;/li&gt;
&lt;li&gt;Range: Ensure that numeric values fall within the accepted range.&lt;/li&gt;
&lt;li&gt;Business logic: Check input against your own business rules (do this last).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Input validation should occur as early as possible and fail fast when handling invalid requests.&lt;br&gt;
Use an appropriate HTTP status code in the 4xx range for validation errors (e.g. &lt;code&gt;400 Bad Request&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Some web frameworks can also perform validation of API responses (e.g. FastAPI with pydantic), guaranteeing that the returned data always has the correct structure and type.&lt;br&gt;
Alternatively, the API endpoints should be covered by automated tests that ensure the response data is formatted correctly (according to the API’s specification) under all circumstances.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Further resources:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html" rel="noopener noreferrer"&gt;Input Validation Cheat Sheet&lt;/a&gt; - &lt;em&gt;OWASP&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Rate limiting
&lt;/h2&gt;

&lt;p&gt;A good API employs rate limiting to prevent overloading and ensure quality of service for all clients.&lt;/p&gt;

&lt;p&gt;Rate limiting can be implemented at different levels of your stack, including the network level, the web server, your application code, or a combination of these.&lt;/p&gt;

&lt;p&gt;As a first layer of protection, most web servers can be easily configured to rate limit requests by IP address.&lt;br&gt;
For more flexibility and better control, you might want to implement (additional) rate limiting logic in the application layer.&lt;br&gt;
For example, you could apply dynamic limits based on the users’ pricing tier.&lt;/p&gt;

&lt;p&gt;When rejecting requests due to rate limiting, it is good practice to use the &lt;code&gt;429 Too Many Requests&lt;/code&gt; response status code and include headers such as &lt;code&gt;Retry-After&lt;/code&gt; to give clients feedback they can use to handle the rate limit gracefully.&lt;/p&gt;

&lt;p&gt;Ensure your API documentation clearly states rate limiting rules and describes the rate limiting headers used.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Further resources:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://blog.hubspot.com/website/api-rate-limit" rel="noopener noreferrer"&gt;API Rate Limiting - Everything You Need to Know&lt;/a&gt; - &lt;em&gt;HubSpot Blog&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kinsta.com/knowledgebase/api-rate-limit" rel="noopener noreferrer"&gt;API Rate Limiting: The Ultimate Guide&lt;/a&gt; - &lt;em&gt;Kinsta Knowledge Base&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Asynchronous processing
&lt;/h2&gt;

&lt;p&gt;A good API performs longer running tasks in the background using a task queue and worker system and avoids keeping client connections open for extended periods.&lt;/p&gt;

&lt;p&gt;This typically involves accepting a task from a client, adding it to the task queue, and then immediately sending a response back acknowledging that the task was received and is being processed.&lt;br&gt;
The &lt;code&gt;202 Accepted&lt;/code&gt; response status code is often used in this scenario.&lt;/p&gt;

&lt;p&gt;This approach improves user experience and helps prevent timeouts as the client isn't left waiting for a response.&lt;br&gt;
It also allows the server to handle multiple requests concurrently and remain responsive while processing long-running tasks.&lt;/p&gt;

&lt;p&gt;APIs should also provide a way for clients to check the status of their tasks and fetch the result, if applicable.&lt;br&gt;
This could be implemented as separate endpoints which the client can poll at intervals.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Further resources:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://learn.microsoft.com/en-us/azure/architecture/patterns/async-request-reply" rel="noopener noreferrer"&gt;Asynchronous Request-Reply pattern&lt;/a&gt; - &lt;em&gt;Microsoft Learn&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Monitoring
&lt;/h2&gt;

&lt;p&gt;A good API is proactively monitored to ensure its consistent availability, reliability and performance.&lt;/p&gt;

&lt;p&gt;Having monitoring tools in place allows you to detect and respond to issues quickly, ideally before they impact users.&lt;br&gt;
It also helps you understand how the API is being used and empowers you to make data-driven product and engineering decisions.&lt;/p&gt;

&lt;p&gt;Monitoring should cover at least the following key aspects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Traffic: Number of requests (per minute)&lt;/li&gt;
&lt;li&gt;Errors: Failed requests due to client or server errors&lt;/li&gt;
&lt;li&gt;Response times / latencies: How long it takes API endpoints to return a response&lt;/li&gt;
&lt;li&gt;Uptime: Whether the API is available to consumers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Further resources:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://apitally.io/blog/four-facets-of-api-monitoring-you-should-implement" rel="noopener noreferrer"&gt;4 facets of API monitoring you should implement&lt;/a&gt; - &lt;em&gt;Apitally Blog&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Recommended tools&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://apitally.io" rel="noopener noreferrer"&gt;Apitally&lt;/a&gt;: Easy-to-use API monitoring for Python and Node.js&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;Is there anything else that you think is an important best practice for REST APIs? Please leave a comment!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>restapi</category>
    </item>
    <item>
      <title>4 facets of API monitoring you should implement</title>
      <dc:creator>Simon Gurcke</dc:creator>
      <pubDate>Sat, 02 Mar 2024 12:37:23 +0000</pubDate>
      <link>https://dev.to/apitally/4-facets-of-api-monitoring-you-should-implement-5afa</link>
      <guid>https://dev.to/apitally/4-facets-of-api-monitoring-you-should-implement-5afa</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Issues with APIs often have the potential to cause major disruptions to businesses. Proactive API monitoring is therefore essential for tech professionals who are responsible for maintaining the integrity and performance of business-critical APIs.&lt;/p&gt;

&lt;p&gt;In this blog post we'll take an in-depth look at the four fundamental aspects of API monitoring every tech professional should consider to implement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API traffic monitoring&lt;/li&gt;
&lt;li&gt;API performance monitoring&lt;/li&gt;
&lt;li&gt;API error monitoring&lt;/li&gt;
&lt;li&gt;API uptime monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Having these in place can empower teams to preemptively address potential issues, optimize API performance, make data-driven product and engineering decisions and ultimately deliver a seamless experience to end-users.&lt;/p&gt;

&lt;h2&gt;
  
  
  API traffic monitoring
&lt;/h2&gt;

&lt;p&gt;This aspect of API monitoring involves tracking the volume and type of requests an API receives. It allows developers and product owners to understand how their APIs are being utilized in real-world scenarios, enabling them to make informed decisions about product development and enhancements. If the APIs are being used for integration with other internal systems, analyzing API traffic sheds light onto the behaviors of these systems and can reveal issues or opportunities for optimization.&lt;/p&gt;

&lt;p&gt;An equally important benefit of API traffic monitoring is the ability to detect anomalies in usage patterns. Sudden spikes or drops in traffic to certain endpoints can indicate underlying issues, including malicious activities aiming to compromise the API. By setting up alerts for such irregularities, teams can quickly investigate and address the underlying causes, minimizing the risk of downtime or security breaches.&lt;/p&gt;

&lt;p&gt;In essence, API traffic monitoring is not just about keeping tabs on the volume of requests; it's about leveraging data to drive strategic decisions, enhance user experiences, and maintain the robustness and integrity of APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  API performance monitoring
&lt;/h2&gt;

&lt;p&gt;Performance is often a key differentiator for APIs. Fast response times not only enhance user experience but also increase the overall efficiency of applications that rely on your API. API performance monitoring involves measuring the time it takes for an API to respond to requests. This is done for the API as a whole, as well as for individual endpoints.&lt;/p&gt;

&lt;p&gt;By tracking these latencies over time, you can identify trends and patterns, such as endpoints that consistently take longer to respond. This can help you pinpoint performance bottlenecks and optimize your API for better responsiveness.&lt;/p&gt;

&lt;p&gt;Setting performance benchmarks based on these metrics is also crucial. They serve as a standard against which the API's ongoing performance can be measured. Any deviations from these benchmarks should trigger alerts to the API team to investigate and rectify potential issues. Thus, effective API performance monitoring leads to a faster, more reliable API and a smoother user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  API error monitoring
&lt;/h2&gt;

&lt;p&gt;When talking about errors in the context of APIs, it makes sense to distinguish two different categories: server errors (&lt;code&gt;5xx&lt;/code&gt; responses) and client errors (&lt;code&gt;4xx&lt;/code&gt; responses).&lt;/p&gt;

&lt;h3&gt;
  
  
  Server errors
&lt;/h3&gt;

&lt;p&gt;Server errors result from issues in your application or infrastructure. When API consumers hit server errors, all they can do is retry the request. Server errors can be temporary, for example, when there are intermittent networking issues within your stack. However, when a bug in your application consistently prevents certain requests from being handled successfully, there is nothing API consumers can do but to wait for you to fix the underlying issue. This is why it is vitally important that you have the right tools in place to alert you when these types of errors occur.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client errors
&lt;/h3&gt;

&lt;p&gt;Client errors are typically part of the API's regular operation. Monitoring these can provide valuable insights and identify ways to enhance the API's usability for end users. A sudden increase in client errors could indicate problems with your API, such as a new validation rule being too restrictive, or reveal issues with consumer systems providing malformatted input to the API.&lt;/p&gt;

&lt;p&gt;In essence, API error monitoring not only helps in pinpointing and fixing issues within the API but also aids in understanding the end-user's interaction with the system. By effectively tracking and analyzing both server and client errors, teams can create a more reliable and user-friendly API.&lt;/p&gt;

&lt;h2&gt;
  
  
  API uptime monitoring
&lt;/h2&gt;

&lt;p&gt;Uptime monitoring is another critical facet of API Monitoring. It refers to the act of ensuring that your API is available and functional at all times. Any downtime can lead to significant disruptions to connected systems, making uptime monitoring a crucial part of maintaining a high-quality user experience.&lt;/p&gt;

&lt;p&gt;API uptime monitoring involves checking the availability of your API at regular intervals. This can be done by sending requests to various endpoints and verifying the responses. In addition to simple availability checks, uptime monitoring could also consider the 'health' of the response. This might involve checking that the response time is within acceptable limits, or that the data returned in the response is as expected.&lt;/p&gt;

&lt;p&gt;Setting up robust alerting is another key aspect of uptime monitoring. Teams should be immediately notified when the API is down or experiencing problems. This allows them to quickly identify and rectify the issues, minimizing the impact on users.&lt;/p&gt;

&lt;p&gt;In essence, API uptime monitoring ensures that your API is consistently ready and accessible, providing the high-quality, reliable service that your users expect.&lt;/p&gt;

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

&lt;p&gt;In conclusion, effective API monitoring involves a comprehensive approach that takes into account API traffic, performance, errors, and uptime.&lt;/p&gt;

&lt;p&gt;Fortunately, the technological landscape is filled with tools to assist in these areas. We've gathered some of them in the list below as a starting point for your own research. They range from simple and easy to implement to comprehensive and complex, catering to different use cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recommended tools
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://apitally.io/" rel="noopener noreferrer"&gt;Apitally&lt;/a&gt;: Simple and easy-to-use API monitoring tool covering traffic, performance, errors, and uptime.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;: Error monitoring for applications, including APIs. Also offers application performance monitoring (APM).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.postman.com/api-monitoring/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt; Uptime and performance monitoring for APIs.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.datadoghq.com/" rel="noopener noreferrer"&gt;Datadog&lt;/a&gt;: Comprehensive monitoring platform.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://newrelic.com/" rel="noopener noreferrer"&gt;New Relic&lt;/a&gt;: Another comprehensive monitoring platform.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://prometheus.io/" rel="noopener noreferrer"&gt;Prometheus&lt;/a&gt;: Open-source monitoring system. Often used together with &lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt;. &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>api</category>
      <category>monitoring</category>
      <category>webdev</category>
    </item>
    <item>
      <title>FastAPI application monitoring made easy</title>
      <dc:creator>Simon Gurcke</dc:creator>
      <pubDate>Sat, 06 Jan 2024 13:05:52 +0000</pubDate>
      <link>https://dev.to/apitally/fastapi-application-monitoring-made-easy-340f</link>
      <guid>https://dev.to/apitally/fastapi-application-monitoring-made-easy-340f</guid>
      <description>&lt;p&gt;Let's assume your team has just shipped a new REST API using FastAPI. Now that it's live you want to know how it's being used and how it's performing. You may have questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the API working as expected?&lt;/li&gt;
&lt;li&gt;How many requests is the API handling, by how many unique consumers?&lt;/li&gt;
&lt;li&gt;Which features of the API are being used the most, and by whom?&lt;/li&gt;
&lt;li&gt;Are there any errors faced by consumers, and what types of errors?&lt;/li&gt;
&lt;li&gt;What does the performance look like?&lt;/li&gt;
&lt;li&gt;Is the API up &amp;amp; available to users at all times?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Out of the box, FastAPI doesn't provide any means for you to answer those kinds of questions. And it shouldn't. It's a web framework, not a monitoring solution, after all. There's many different ways to approach this, but let's dive into a couple of options.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hard way
&lt;/h2&gt;

&lt;p&gt;You might find recommendations on the internet for setting up Prometheus and Grafana to monitor your application. These are widely adopted open-source tools for monitoring all sorts of things, and they can certainly help you achieve what you're after.&lt;/p&gt;

&lt;p&gt;This approach can be powerful and versatile, allowing you to implement things like custom metrics and infrastructure monitoring all within the same platform while tailoring everything exactly to your needs.&lt;/p&gt;

&lt;p&gt;However, Prometheus and Grafana aren't exactly plug-and-play solutions. If you're new to them, you might need to invest a fair bit of time to get the answers you're looking for. You'll need to deploy and configure both tools, implement metrics logging using the Prometheus client in your application and then build dashboards in Grafana. Also, remember to consider the ongoing costs of hosting and maintaining these tools.&lt;/p&gt;

&lt;p&gt;You may have good reasons for going down this path, and if you do, I highly recommend &lt;a href="https://dev.to/ken_mwaura1/getting-started-monitoring-a-fastapi-app-with-grafana-and-prometheus-a-step-by-step-guide-3fbn"&gt;this article&lt;/a&gt; to get you started. On the other hand, if you'd prefer a simpler approach, read on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The easy way
&lt;/h2&gt;

&lt;p&gt;By using a purpose-built API monitoring solution, such as &lt;a href="https://apitally.io" rel="noopener noreferrer"&gt;Apitally&lt;/a&gt;, you can gain insights into how your API is being used and how it's performing within a matter of minutes. This is possible because Apitally provides seamless integration with FastAPI via a simple middleware that captures relevant metadata about all handled requests and responses. This includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP method and path of requests&lt;/li&gt;
&lt;li&gt;Response status codes&lt;/li&gt;
&lt;li&gt;Response times / latencies&lt;/li&gt;
&lt;li&gt;Consumer identifiers &lt;em&gt;(if configured)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This data syncs from your application to Apitally every minute and is then available for analysis on an intuitive dashboard that allows you to easily keep track of API requests, error rates and response times and provides insights into the usage and performance of each endpoint.&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%2Fv8vggk0cca5mamkpofts.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%2Fv8vggk0cca5mamkpofts.png" alt="Screenshot of Apitally traffic dashboard" width="800" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All you need to do is install a &lt;a href="https://pypi.org/project/apitally/" rel="noopener noreferrer"&gt;small dependency&lt;/a&gt; and add a couple of lines of code to your project, as shown in the example below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apitally.fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApitallyMiddleware&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ApitallyMiddleware&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-client-id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# provided by Apitally
&lt;/span&gt;    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# or "dev", "prod" etc. (optional)
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The middleware works asynchronously and does not impact the performance of your application. It also doesn't capture request and response headers and bodies, which means you don't have to worry about masking sensitive information that may be present in your API payloads.&lt;/p&gt;

&lt;h3&gt;
  
  
  Identifying consumers
&lt;/h3&gt;

&lt;p&gt;You might find it useful to analyze API requests by consumer, so you can understand the different usage patterns of individual consumers. To allow that, you can optionally associate requests with consumers in your application by setting the &lt;code&gt;apitally_consumer&lt;/code&gt; property on the &lt;code&gt;request.state&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;As an example, if your application is using authentication, you could set the &lt;code&gt;apitally_consumer&lt;/code&gt; based on the authenticated user.&lt;/p&gt;

&lt;p&gt;You could do this anywhere within your existing authentication code, or in a dedicated function, like in the example below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;identify_consumer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_current_user&lt;/span&gt;&lt;span class="p"&gt;)]):&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apitally_consumer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;identify_consumer&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;While not as versatile and customizable as managing your own monitoring stack, using a tool like Apitally can provide valuable insights into how your API is being used a lot quicker. And sometimes a simple solution is all you need.&lt;/p&gt;

&lt;p&gt;If you'd like to try Apitally out for your FastAPI project, you can follow the detailed &lt;a href="https://docs.apitally.io/frameworks/fastapi" rel="noopener noreferrer"&gt;setup guide for FastAPI&lt;/a&gt;. You should be good to go in less than 5 minutes.&lt;/p&gt;

&lt;p&gt;Happy monitoring! Feel free to ask any questions or let me know what you think in the comments below.&lt;/p&gt;

</description>
      <category>api</category>
      <category>monitoring</category>
      <category>fastapi</category>
    </item>
  </channel>
</rss>
