<?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: lamj</title>
    <description>The latest articles on DEV Community by lamj (@lamj).</description>
    <link>https://dev.to/lamj</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3835811%2Fff66b699-946c-401a-a93f-c671bdb0c9cd.jpg</url>
      <title>DEV Community: lamj</title>
      <link>https://dev.to/lamj</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lamj"/>
    <language>en</language>
    <item>
      <title>How I Validate API Keys Without Hitting the Database on Every Request</title>
      <dc:creator>lamj</dc:creator>
      <pubDate>Tue, 24 Mar 2026 14:48:35 +0000</pubDate>
      <link>https://dev.to/lamj/how-i-validate-api-keys-without-hitting-the-database-on-every-request-5cb3</link>
      <guid>https://dev.to/lamj/how-i-validate-api-keys-without-hitting-the-database-on-every-request-5cb3</guid>
      <description>&lt;p&gt;Free APIs come with a lot of challenges.&lt;/p&gt;

&lt;p&gt;One of the biggest ones is API key validation.&lt;/p&gt;

&lt;p&gt;If done poorly, it can lead to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;performance bottlenecks&lt;/li&gt;
&lt;li&gt;unnecessary database load&lt;/li&gt;
&lt;li&gt;potential security issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s how I approached this problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authorization and API Key Design
&lt;/h2&gt;

&lt;p&gt;I didn’t want to validate every API key with a database query.&lt;/p&gt;

&lt;p&gt;So I made the key &lt;strong&gt;self-contained&lt;/strong&gt;.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Authorization: PetProjects ppk_v1_1_nonce_signature
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ppk_version_userId_nonce_signature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;version&lt;/code&gt; — key version&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;userId&lt;/code&gt; — user identifier&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nonce&lt;/code&gt; — random value&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;signature&lt;/code&gt; — HMAC signature&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Validation Flow
&lt;/h2&gt;

&lt;p&gt;The validation process is split into two steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Fast Validation (No Database)
&lt;/h3&gt;

&lt;p&gt;First, the key is validated locally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;structure check&lt;/li&gt;
&lt;li&gt;data correctness&lt;/li&gt;
&lt;li&gt;HMAC signature verification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows us to reject invalid or garbage keys &lt;strong&gt;without touching the database&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. User Check
&lt;/h3&gt;

&lt;p&gt;If the key is valid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we extract &lt;code&gt;userId&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;then perform a single database query&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Validation Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;validateApiKey&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ApiKeyPayload&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ppk_&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="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;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[,&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userIdRaw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parts&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;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userIdRaw&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isInteger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-f0-9&lt;/span&gt;&lt;span class="se"&gt;]{64}&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expectedSignature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;API_KEY_SECRET&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;expectedSignature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Timing-safe comparison is used here.
     *
     * A regular string comparison (===) can be vulnerable to timing attacks:
     * the comparison stops at the first mismatched character,
     * which may allow an attacker to infer parts of the signature
     * based on response time differences.
     *
     * crypto.timingSafeEqual performs a constant-time comparison,
     * preventing leakage of information about matching characters.
     */&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timingSafeEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expectedSignature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nonce&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;
  
  
  Caching
&lt;/h2&gt;

&lt;p&gt;After successful validation, the user is cached:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKeyCache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;LRUCache&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CachedUserAuth&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&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;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no database hit on every request&lt;/li&gt;
&lt;li&gt;reduced latency&lt;/li&gt;
&lt;li&gt;lower database load&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why TTL = 5 Minutes
&lt;/h2&gt;

&lt;p&gt;The TTL is intentionally short.&lt;/p&gt;

&lt;p&gt;If a key leaks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it only works for a limited time&lt;/li&gt;
&lt;li&gt;then requires revalidation via database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a trade-off between &lt;strong&gt;performance and security&lt;/strong&gt;.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;Don’t validate API keys with a database on every request.&lt;br&gt;&lt;br&gt;
Design them to be verifiable locally.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you're building a free API, this approach can significantly reduce load while keeping things simple.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;I’m using this approach in a free API platform I’m building:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pet-projects.io/en/apis" rel="noopener noreferrer"&gt;https://pet-projects.io/en/apis&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>backend</category>
      <category>api</category>
    </item>
    <item>
      <title>I Built Free APIs You Can Use in 1 Minute (No Setup Required)</title>
      <dc:creator>lamj</dc:creator>
      <pubDate>Fri, 20 Mar 2026 17:11:35 +0000</pubDate>
      <link>https://dev.to/lamj/i-built-free-apis-you-can-use-in-1-minute-no-setup-required-2gp8</link>
      <guid>https://dev.to/lamj/i-built-free-apis-you-can-use-in-1-minute-no-setup-required-2gp8</guid>
      <description>&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%2Fd64ywuknkrc9gaunb1p3.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%2Fd64ywuknkrc9gaunb1p3.png" alt="Preview" width="800" height="448"&gt;&lt;/a&gt;&lt;br&gt;
Every developer has faced this problem:&lt;/p&gt;

&lt;p&gt;You need an API for a pet project, testing, or learning…&lt;br&gt;&lt;br&gt;
and suddenly you’re stuck.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;APIs are overcomplicated
&lt;/li&gt;
&lt;li&gt;Documentation is confusing
&lt;/li&gt;
&lt;li&gt;Or everything is locked behind a paywall
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I decided to build something simpler.&lt;/p&gt;




&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;I created a set of &lt;strong&gt;free APIs with instant access&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No setup.
&lt;/li&gt;
&lt;li&gt;No waiting.
&lt;/li&gt;
&lt;li&gt;No friction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You open the page — and you can start using it immediately.&lt;/p&gt;

&lt;p&gt;👉 Explore the API instantly&lt;br&gt;
&lt;a href="https://pet-projects.io/en" rel="noopener noreferrer"&gt;https://pet-projects.io/en&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🔥 Instant Playground
&lt;/h2&gt;

&lt;p&gt;Instead of reading long docs first, you can just try it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ready-to-use requests
&lt;/li&gt;
&lt;li&gt;Code examples in 6 languages
&lt;/li&gt;
&lt;li&gt;Real responses
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Requests are sent in real-time, so you can see how the API behaves.&lt;/p&gt;

&lt;p&gt;To use it in your own projects, you’ll need an API key.&lt;/p&gt;

&lt;p&gt;Just &lt;strong&gt;sign in&lt;/strong&gt; (via OAuth or email) — your API key will be generated and applied automatically.&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%2F8mbbk0p8nj6g21nr7ph9.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%2F8mbbk0p8nj6g21nr7ph9.png" alt="Playground Screenshot" width="800" height="824"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Example APIs
&lt;/h2&gt;

&lt;p&gt;Here are a few things you can already use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JSON storage API &lt;/li&gt;
&lt;li&gt;REST Free API&lt;/li&gt;
&lt;li&gt;REST Todos API &lt;/li&gt;
&lt;li&gt;GraphQL Todos API&lt;/li&gt;
&lt;li&gt;(more coming soon — including WebSocket API)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why I built it this way
&lt;/h2&gt;

&lt;p&gt;Most platforms optimize for:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;monetization first, developer experience second&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I wanted the opposite:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Developer experience first&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Minimal time to first request
&lt;/li&gt;
&lt;li&gt;Clean UI
&lt;/li&gt;
&lt;li&gt;No unnecessary steps
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&gt;

&lt;p&gt;You can test everything here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pet-projects.io/en" rel="noopener noreferrer"&gt;https://pet-projects.io/en&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or jump directly into a playground and start sending requests.&lt;/p&gt;




&lt;h2&gt;
  
  
  Feedback
&lt;/h2&gt;

&lt;p&gt;If you have ideas or feedback — I’d love to hear it.&lt;/p&gt;

&lt;p&gt;This is just the beginning, and I’m actively improving the platform.&lt;/p&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>backend</category>
      <category>frontend</category>
    </item>
  </channel>
</rss>
