<?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: Zuplo</title>
    <description>The latest articles on DEV Community by Zuplo (@zuplo).</description>
    <link>https://dev.to/zuplo</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%2F9445%2F1cec1839-f678-42e3-9019-c2ce84a863f9.png</url>
      <title>DEV Community: Zuplo</title>
      <link>https://dev.to/zuplo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zuplo"/>
    <language>en</language>
    <item>
      <title>Building a Monetized API (Part 4 of 4)</title>
      <dc:creator>Martyn Davies</dc:creator>
      <pubDate>Thu, 16 Apr 2026 09:00:00 +0000</pubDate>
      <link>https://dev.to/zuplo/building-a-monetized-api-part-4-of-4-4ecc</link>
      <guid>https://dev.to/zuplo/building-a-monetized-api-part-4-of-4-4ecc</guid>
      <description>&lt;p&gt;This is Part 4 of the "Building a Monetized API" series, and the final installment. In Part 1, we set up the API gateway. In Part 2, we added monetization with meters, plans, and Stripe. In Part 3, we added an MCP server with plan-gated access. Now we're taking the developer portal from functional to polished: real documentation, a custom theme, and a production-ready layout.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/guNXiln5PYg"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Where we left off
&lt;/h2&gt;

&lt;p&gt;After three parts, we have a fully functional monetized API. The gateway handles authentication, consumer isolation, and rate limiting. The monetization layer manages meters, plans, Stripe checkout, and self-serve subscriptions. The MCP server is gated to paid plans only.&lt;/p&gt;

&lt;p&gt;The developer portal already works out of the box. The API reference is interactive, API keys are pre-populated in the playground, and the pricing table shows all three plans. But the documentation section is still the default placeholder content that Zuplo generates when the portal is first created. If we're asking developers to pay for this API, they deserve proper documentation.&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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-03-building-a-monetized-api-part-4%252Fportal-before.png%26w%3D1920%26q%3D80" 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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-03-building-a-monetized-api-part-4%252Fportal-before.png%26w%3D1920%26q%3D80" alt="The default developer portal with placeholder documentation and generic " width="1920" height="896"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Connecting the project to GitHub
&lt;/h2&gt;

&lt;p&gt;Up to this point in the series, all the work has been done directly in Zuplo's UI. That's fine for gateway configuration and monetization setup, but for writing documentation we want a proper local development workflow.&lt;/p&gt;

&lt;p&gt;In your Zuplo project, go to &lt;strong&gt;Code&lt;/strong&gt; and click &lt;strong&gt;GitHub&lt;/strong&gt; to connect to a repository. You can create a new repository or connect to an existing one. Zuplo will push the entire project codebase as the initial commit.&lt;/p&gt;

&lt;p&gt;Once connected, clone the repository locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone git@github.com:your-org/your-project.git
&lt;span class="nb"&gt;cd &lt;/span&gt;your-project
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; docs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Working on a branch is important here. The &lt;code&gt;main&lt;/code&gt; branch maps to your production environment in Zuplo, and we don't want documentation drafts going live before they're ready.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;a href="https://zuplo.com/docs/articles/source-control" rel="noopener noreferrer"&gt;Source Control&lt;/a&gt;&lt;/strong&gt; — How to connect your Zuplo project to GitHub, GitLab, or other providers for source-controlled deployments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 2: Generating documentation with Claude Code
&lt;/h2&gt;

&lt;p&gt;The Zuplo project contains a &lt;code&gt;docs&lt;/code&gt; folder that controls the developer portal via a Zudoku configuration file. It also has a &lt;code&gt;routes.oas.json&lt;/code&gt; file: the OpenAPI specification for the API. This is the source of truth for every endpoint the gateway serves.&lt;/p&gt;

&lt;p&gt;Rather than writing documentation from scratch, we can use Claude Code to generate it from the OpenAPI spec. This is the first time AI has been used in this series. Everything up to this point has been manual configuration and code.&lt;/p&gt;

&lt;p&gt;Open the project in your terminal and run Claude Code with a prompt like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use the &lt;code&gt;routes.oas.json&lt;/code&gt; as the basis for developer documentation. Create new docs in the &lt;code&gt;docs&lt;/code&gt; folder using Zudoku best practices. Use the Zudoku documentation at &lt;a href="https://zudoku.dev/docs/llms.txt" rel="noopener noreferrer"&gt;https://zudoku.dev/docs/llms.txt&lt;/a&gt; as a reference.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude Code reads the OpenAPI spec, understands the API's endpoints, request and response schemas, and generates documentation pages covering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An introduction and quickstart guide&lt;/li&gt;
&lt;li&gt;Authentication details&lt;/li&gt;
&lt;li&gt;Guides for each major resource (projects, changelogs)&lt;/li&gt;
&lt;li&gt;Filtering and search documentation&lt;/li&gt;
&lt;li&gt;Advanced topics like the MCP server and error handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This took about four minutes and produced seven documentation pages. Claude also updated the Zudoku config to set the correct title, metadata, and navigation structure, and changed the default landing page from the API reference to the documentation section.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;a href="https://zuplo.com/docs/dev-portal/zudoku/configuration/overview" rel="noopener noreferrer"&gt;Dev Portal Configuration&lt;/a&gt;&lt;/strong&gt; — Reference for the Zudoku configuration file that controls your developer portal's structure, metadata, and navigation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 3: Applying a custom theme
&lt;/h2&gt;

&lt;p&gt;The default developer portal looks clean, but if you're shipping a product, you want it to match your brand. Zudoku supports &lt;a href="https://ui.shadcn.com/" rel="noopener noreferrer"&gt;shadcn/ui&lt;/a&gt; themes, which means you can apply any compatible theme with just a URL.&lt;/p&gt;

&lt;p&gt;For this project, we used a theme from &lt;a href="https://tweakcn.com/" rel="noopener noreferrer"&gt;tweakcn&lt;/a&gt; with bold colors. In the same Claude Code prompt (or a follow-up), you can ask it to apply the theme by providing the URL. Claude adds the CSS variables to the Zudoku config, and the entire portal picks up the new color scheme.&lt;/p&gt;

&lt;p&gt;You can also generate a logo. We had Claude create a simple SVG with the service name to replace the default "My Dev Portal" text in the header.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;a href="https://zuplo.com/docs/dev-portal/zudoku/customization/colors-theme" rel="noopener noreferrer"&gt;Theme Customization&lt;/a&gt;&lt;/strong&gt; — Customize your developer portal's colors, typography, and styling with shadcn themes and custom CSS.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 4: Previewing locally
&lt;/h2&gt;

&lt;p&gt;Before pushing anything, preview the changes locally. Install the project dependencies and start the Zudoku dev server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run docs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The portal runs on &lt;code&gt;localhost:9200&lt;/code&gt;. Check each documentation page, verify the navigation structure, and make sure the API reference still looks correct alongside the new content.&lt;/p&gt;

&lt;p&gt;One thing to watch for: Claude may duplicate page titles in the sidebar navigation (the page title appearing both as the nav label and as a heading inside the page). If that happens, a quick follow-up prompt fixes it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;a href="https://zuplo.com/docs/dev-portal/local-development" rel="noopener noreferrer"&gt;Local Development&lt;/a&gt;&lt;/strong&gt; — Set up local development for your developer portal with live reloading.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 5: Pushing to GitHub and preview environments
&lt;/h2&gt;

&lt;p&gt;Once the documentation looks right locally, commit and push the branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add developer documentation and custom theme"&lt;/span&gt;
git push origin docs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Back in the Zuplo UI, go to the &lt;strong&gt;Environments&lt;/strong&gt; tab. A new preview environment has automatically been created for the &lt;code&gt;docs&lt;/code&gt; branch. This is one of the powerful aspects of connecting to source control: every branch gets its own deployed environment with a unique URL.&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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-03-building-a-monetized-api-part-4%252Fpreview-environment.png%26w%3D1920%26q%3D80" 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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-03-building-a-monetized-api-part-4%252Fpreview-environment.png%26w%3D1920%26q%3D80" alt="The Zuplo Environments tab showing Development (Working Copy), Production (main), and Preview (docs branch) environments each with Gateway and Dev Portal URLs" width="1920" height="944"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The preview environment builds and deploys the portal with the new documentation. You can share this URL with teammates for review before anything touches production.&lt;/p&gt;

&lt;p&gt;Note that the preview environment has its own monetization configuration. The meters, plans, and Stripe integration you set up on the working copy don't automatically carry over to preview environments. This is by design: you can experiment with different monetization setups across environments without affecting production.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;a href="https://zuplo.com/docs/articles/environments" rel="noopener noreferrer"&gt;Environments&lt;/a&gt;&lt;/strong&gt; — How Zuplo's development, preview, and production environments work with branch-based deployments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 6: Syncing back to the working copy
&lt;/h2&gt;

&lt;p&gt;The preview environment confirms the documentation looks great. Now we need to get those changes back into the working copy, which is where all the monetization configuration lives.&lt;/p&gt;

&lt;p&gt;In the Zuplo UI, go to the &lt;strong&gt;Code&lt;/strong&gt; tab and switch to the working copy. Open the &lt;strong&gt;Sync with GitHub&lt;/strong&gt; dialog by clicking the GitHub button at the bottom of the editor, or with the keyboard shortcut &lt;strong&gt;Cmd+Shift+P&lt;/strong&gt;. Select the &lt;code&gt;docs&lt;/code&gt; branch and click &lt;strong&gt;Pull from GitHub&lt;/strong&gt;. This merges the documentation and theme changes into the working copy without disturbing any of the monetization, metering, or environment variable configuration.&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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-03-building-a-monetized-api-part-4%252Fsync-github.png%26w%3D1920%26q%3D80" 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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-03-building-a-monetized-api-part-4%252Fsync-github.png%26w%3D1920%26q%3D80" alt="The Sync with GitHub dialog showing the docs branch selected, ready to pull changes into the working copy" width="1876" height="564"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the working copy rebuilds and redeploys, everything is unified: the API gateway, the monetization layer, the MCP server, and now a fully documented, custom-themed developer portal.&lt;/p&gt;

&lt;h2&gt;
  
  
  The finished developer portal
&lt;/h2&gt;

&lt;p&gt;With all four parts complete, the developer portal now includes:&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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-03-building-a-monetized-api-part-4%252Fportal-after.png%26w%3D1920%26q%3D80" 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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-03-building-a-monetized-api-part-4%252Fportal-after.png%26w%3D1920%26q%3D80" alt="The finished Changeloggle developer portal with custom branding, generated documentation covering changelogs, projects, authentication, and more" width="1920" height="1051"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt;: seven pages of generated content covering quickstart, authentication, resource guides, and advanced topics. The MCP server page even notes that access is restricted to paid plans, pulled directly from the project configuration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API reference&lt;/strong&gt;: the interactive reference that was already there, with the API playground and pre-populated API keys.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pricing table&lt;/strong&gt;: the three plans (Free, Starter, Pro) with self-serve subscription management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom theme&lt;/strong&gt;: branded colors and a logo that make it look like a real product, not a template.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What we built across the series
&lt;/h2&gt;

&lt;p&gt;Across four parts, we built a complete monetized API product:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[x] &lt;strong&gt;Part 1&lt;/strong&gt;: API gateway with origin auth, consumer isolation, and rate limiting&lt;/li&gt;
&lt;li&gt;[x] &lt;strong&gt;Part 2&lt;/strong&gt;: Monetization with meters, plans, Stripe integration, and self-serve subscriptions&lt;/li&gt;
&lt;li&gt;[x] &lt;strong&gt;Part 3&lt;/strong&gt;: MCP server with plan-gated access for paid subscribers only&lt;/li&gt;
&lt;li&gt;[x] &lt;strong&gt;Part 4&lt;/strong&gt;: Source control, generated documentation, custom theme, and a production-ready developer portal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is everything you need to take an API from "deployed somewhere" to "something developers can discover, evaluate, subscribe to, and pay for." The gateway handles the infrastructure. The monetization layer handles the business logic. The developer portal handles the experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to go from here
&lt;/h2&gt;

&lt;p&gt;There's a lot more you can do with monetization and developer portal customization beyond what this series covered. A few ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Custom pages&lt;/strong&gt;: add a landing page, changelog, or migration guide to the developer portal using Zudoku's MDX support.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhooks&lt;/strong&gt;: notify your backend when a subscriber upgrades or cancels, so you can trigger provisioning workflows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics&lt;/strong&gt;: use the metering data to understand which endpoints get the most traffic and optimize your pricing accordingly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature flags&lt;/strong&gt;: gate additional API capabilities beyond the MCP server by adding more boolean features to your plans.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out the &lt;a href="https://zuplo.com/docs/articles/monetization/quickstart" rel="noopener noreferrer"&gt;monetization documentation&lt;/a&gt; for the full range of what's possible. And if you have questions or need help getting this set up for your own API, &lt;a href="mailto:support@zuplo.com"&gt;get in touch&lt;/a&gt;. We'd love to hear what you're building.&lt;/p&gt;

</description>
      <category>api</category>
      <category>monetization</category>
      <category>apigateway</category>
    </item>
    <item>
      <title>Building a Monetized API (Part 3 of 4)</title>
      <dc:creator>Martyn Davies</dc:creator>
      <pubDate>Wed, 15 Apr 2026 09:00:00 +0000</pubDate>
      <link>https://dev.to/zuplo/building-a-monetized-api-part-3-of-4-2oml</link>
      <guid>https://dev.to/zuplo/building-a-monetized-api-part-3-of-4-2oml</guid>
      <description>&lt;p&gt;This is Part 3 of the "Building a Monetized API" series. In Part 1, we set up the API gateway with authentication, consumer isolation, and rate limiting. In Part 2, we added meters, plans, Stripe integration, and a self-serve developer portal. Now we're adding an MCP server on top of the same API and gating access so only paid subscribers can use it.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/tjZ3Sed6svQ"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Why gate MCP access behind a paid plan?
&lt;/h2&gt;

&lt;p&gt;In Part 2, we set up three subscription tiers: Free, Starter, and Pro. The Starter and Pro plans include a boolean feature called &lt;code&gt;mcp_server&lt;/code&gt; with an entitlement set to &lt;code&gt;true&lt;/code&gt;. The Free plan doesn't have it.&lt;/p&gt;

&lt;p&gt;This is a deliberate product decision. The API itself is available on every plan (with different usage limits), but MCP server access is a premium feature. Offering AI-powered tooling as a paid upgrade is a natural way to add value to higher tiers without changing the underlying API at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Adding the MCP server
&lt;/h2&gt;

&lt;p&gt;Zuplo has native MCP server support built into the gateway. Adding one takes about 30 seconds.&lt;/p&gt;

&lt;p&gt;Go to your project's route list and click the &lt;strong&gt;Add&lt;/strong&gt; tab. You'll see an &lt;strong&gt;MCP Server&lt;/strong&gt; option. Select it, and configure the basics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt;: &lt;code&gt;ChangeLoggle MCP Server&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version&lt;/strong&gt;: &lt;code&gt;0.0.1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description&lt;/strong&gt;: &lt;code&gt;Create, update, and discover team changelog info.&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That creates the MCP server endpoint at &lt;code&gt;/mcp&lt;/code&gt; on your gateway.&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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-02-building-a-monetized-api-part-3%252Fmcp-server-setup.png%26w%3D1920%26q%3D80" 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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-02-building-a-monetized-api-part-3%252Fmcp-server-setup.png%26w%3D1920%26q%3D80" alt="The MCP Server Options panel in Zuplo showing the server name, version, and description fields" width="1532" height="1054"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;a href="https://zuplo.com/docs/mcp-server/introduction" rel="noopener noreferrer"&gt;MCP Servers with Zuplo&lt;/a&gt;&lt;/strong&gt; — Learn how to turn your existing API gateway into a toolset that AI systems can discover and use.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 2: Selecting which routes to expose as tools
&lt;/h2&gt;

&lt;p&gt;After creating the MCP server, you need to choose which API routes become MCP tools. Click &lt;strong&gt;Select Tools&lt;/strong&gt; to see all available routes.&lt;/p&gt;

&lt;p&gt;You can be selective here. If you only want a read-only MCP experience, pick just the GET endpoints. If you want full read-write access (create projects, update changelogs, search entries), select everything.&lt;/p&gt;

&lt;p&gt;For our changelog API, we're exposing all 12 endpoints as tools. Every operation that's available via the REST API is also available through MCP.&lt;/p&gt;

&lt;p&gt;Be thoughtful about this in your own projects. Each tool adds to the context that AI assistants need to process. If you have dozens of endpoints, ask yourself whether all of them make sense as MCP tools, or whether a focused subset delivers a better experience.&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Update Tools&lt;/strong&gt; to save your selection.&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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-02-building-a-monetized-api-part-3%252Fmcp-tools-selection.png%26w%3D1920%26q%3D80" 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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-02-building-a-monetized-api-part-3%252Fmcp-tools-selection.png%26w%3D1920%26q%3D80" alt="The MCP Tools panel showing all 12 API routes selected as MCP tools with their HTTP methods" width="1526" height="1202"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Testing the MCP server
&lt;/h2&gt;

&lt;p&gt;Before adding access controls, verify that the MCP server works. You can test it with any MCP client. In the video, we use &lt;a href="https://www.mcpjam.com/" rel="noopener noreferrer"&gt;MCP Jam&lt;/a&gt;, but any compatible client works.&lt;/p&gt;

&lt;p&gt;To connect:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use your gateway's deployment URL with &lt;code&gt;/mcp&lt;/code&gt; appended as the endpoint&lt;/li&gt;
&lt;li&gt;Set the connection type to &lt;strong&gt;HTTP&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Set authentication to &lt;strong&gt;Bearer Token&lt;/strong&gt; and paste in an API key from the developer portal&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the connection succeeds, you can interact with your API through the MCP client. Try asking it to list projects or create a new one. Every request flows through the same gateway, uses the same API key authentication, and hits the same origin API.&lt;/p&gt;

&lt;p&gt;At this point there's no entitlement check on the MCP route, so any valid API key works regardless of plan. That's fine for testing. We'll lock it down in the next step.&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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-02-building-a-monetized-api-part-3%252Fmcp-jam-working.png%26w%3D1920%26q%3D80" 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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-02-building-a-monetized-api-part-3%252Fmcp-jam-working.png%26w%3D1920%26q%3D80" alt="MCP Jam showing a successful list projects request returning the Changeloggle project with its details" width="1920" height="1129"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Writing the access check policy
&lt;/h2&gt;

&lt;p&gt;To gate MCP access, we need a custom inbound policy that inspects the subscriber's entitlements and blocks the request if they don't have MCP access.&lt;/p&gt;

&lt;p&gt;The key is the &lt;code&gt;MonetizationInboundPolicy&lt;/code&gt; class from &lt;code&gt;@zuplo/runtime&lt;/code&gt;. Because the &lt;a href="https://zuplo.com/docs/policies/monetization-inbound" rel="noopener noreferrer"&gt;MonetizationInbound policy&lt;/a&gt; runs before this custom code, it has already authenticated the API key and loaded the subscriber's plan data. Calling &lt;code&gt;MonetizationInboundPolicy.getSubscriptionData(context)&lt;/code&gt; gives you the full subscription context for the current consumer, including their entitlements.&lt;/p&gt;

&lt;p&gt;Create a new module in your Zuplo project called &lt;code&gt;check-mcp-access&lt;/code&gt;:&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;MonetizationInboundPolicy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ZuploContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ZuploRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@zuplo/runtime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;policy&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;ZuploRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ZuploContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;policyName&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="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;subscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;MonetizationInboundPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSubscriptionData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="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;subscription&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;entitlements&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;mcp_server&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;hasAccess&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&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;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MCP access requires a Starter or Pro plan, please consider upgrading.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;403&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="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;request&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;Here's what's happening:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;MonetizationInboundPolicy.getSubscriptionData(context)&lt;/code&gt; retrieves the subscription for the consumer making the request. The MonetizationInbound policy that runs before this code has already authenticated the API key and populated the context with subscription data.&lt;/li&gt;
&lt;li&gt;The code checks &lt;code&gt;subscription.entitlements.mcp_server.hasAccess&lt;/code&gt;. The &lt;code&gt;mcp_server&lt;/code&gt; key matches the feature we set up in Part 2 when configuring the Starter and Pro plans. Entitlements are accessed as properties on the &lt;code&gt;entitlements&lt;/code&gt; object, keyed by the feature's identifier.&lt;/li&gt;
&lt;li&gt;If the entitlement doesn't exist (Free plan) or &lt;code&gt;hasAccess&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;, the request gets a 403 JSON response telling the consumer to upgrade.&lt;/li&gt;
&lt;li&gt;If the entitlement exists and &lt;code&gt;hasAccess&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;, the request passes through to the MCP server handler.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;a href="https://zuplo.com/docs/policies/monetization-inbound" rel="noopener noreferrer"&gt;Monetization Inbound Policy&lt;/a&gt;&lt;/strong&gt; — Full policy reference including metering options, subscription data access, and exposed functions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 5: Wiring the policies to the MCP endpoint
&lt;/h2&gt;

&lt;p&gt;Unlike the policies we added in Parts 1 and 2 (which apply to every route), the MCP access check only needs to go on one endpoint: the &lt;code&gt;/mcp&lt;/code&gt; route.&lt;/p&gt;

&lt;p&gt;Open the MCP server route and add two inbound policies in this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Monetization Inbound&lt;/strong&gt; (existing policy): handles API key authentication and request metering. This needs to run first so the consumer's identity and subscription are available to downstream policies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom Code Inbound&lt;/strong&gt; pointing to &lt;code&gt;check-mcp-access&lt;/code&gt;: runs the entitlement check we just wrote.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Custom Code Inbound policy just needs to point at the module:&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;"export"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$import(./modules/check-mcp-access)"&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;Save and deploy. The MCP endpoint now authenticates the request, meters it, and checks entitlements before the MCP server handler ever runs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;a href="https://zuplo.com/docs/policies/custom-code-inbound" rel="noopener noreferrer"&gt;Custom Code Inbound Policy&lt;/a&gt;&lt;/strong&gt; — Write custom request handling logic that runs as part of your policy pipeline.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 6: Testing the access gate
&lt;/h2&gt;

&lt;p&gt;With the policies in place, we can verify that free-plan subscribers are blocked and paid-plan subscribers get through.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Free plan test.&lt;/strong&gt; Connect an MCP client using an API key from a free-plan subscription. The connection attempt fails with a 403 and the message telling the consumer to upgrade. The MCP server never processes the request.&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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-02-building-a-monetized-api-part-3%252Fmcp-jam-free-plan-blocked.png%26w%3D1920%26q%3D80" 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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-02-building-a-monetized-api-part-3%252Fmcp-jam-free-plan-blocked.png%26w%3D1920%26q%3D80" alt="MCP Jam showing a failed connection with a 403 error: MCP access requires a Starter or Pro plan" width="1544" height="780"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Paid plan test.&lt;/strong&gt; Either upgrade the same subscription to Starter or Pro through the developer portal, or use an API key from an existing paid subscription. Nothing changes about the connection setup: same URL, same API key. This time, the connection succeeds and the MCP client can list tools and make requests.&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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-02-building-a-monetized-api-part-3%252Fmcp-jam-paid-plan-connected.png%26w%3D1920%26q%3D80" 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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-02-building-a-monetized-api-part-3%252Fmcp-jam-paid-plan-connected.png%26w%3D1920%26q%3D80" alt="MCP Jam showing a successful connection on a paid plan with the tools list response visible" width="1920" height="698"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The API key itself doesn't change when a subscriber upgrades plans. The entitlement check happens at request time against the current subscription data, so the moment someone upgrades, their existing API key starts working with the MCP server.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we built in Part 3
&lt;/h2&gt;

&lt;p&gt;The monetized API now has an MCP server with plan-gated access:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[x] MCP server added to the gateway with all API routes exposed as tools&lt;/li&gt;
&lt;li&gt;[x] Custom access policy using &lt;code&gt;MonetizationInboundPolicy.getSubscriptionData&lt;/code&gt; to check entitlements&lt;/li&gt;
&lt;li&gt;[x] Free plan subscribers blocked from MCP access with a clear upgrade message&lt;/li&gt;
&lt;li&gt;[x] Paid plan subscribers (Starter and Pro) get full MCP server access&lt;/li&gt;
&lt;li&gt;[x] MCP requests are metered the same way as standard API requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The MCP server sits on a single endpoint, so the access check only needed to be added once. Every other route in the project continues to work exactly as before. And because the MonetizationInbound policy handles both authentication and metering, MCP requests count toward the subscriber's usage just like any other API call.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;In Part 4, we'll finish the developer portal. Right now it has the default documentation that Zuplo generates from the OpenAPI spec, but it needs polish: better descriptions, branded styling, and a layout that makes the API easy to explore. That's the last step before this is production-ready.&lt;/p&gt;

</description>
      <category>api</category>
      <category>monetization</category>
      <category>apigateway</category>
    </item>
    <item>
      <title>Building a Monetized API (Part 2 of 4)</title>
      <dc:creator>Martyn Davies</dc:creator>
      <pubDate>Tue, 14 Apr 2026 09:00:00 +0000</pubDate>
      <link>https://dev.to/zuplo/building-a-monetized-api-part-2-of-4-59ie</link>
      <guid>https://dev.to/zuplo/building-a-monetized-api-part-2-of-4-59ie</guid>
      <description>&lt;p&gt;This is Part 2 of the "Building a Monetized API" series. In Part 1, we set up the Zuplo API gateway for our Vercel-hosted changelog API, imported endpoints, added authentication, and configured rate limiting. In this post, we're adding the monetization layer on top of that.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/jKDroS-xqe0"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Meters and Features
&lt;/h2&gt;

&lt;p&gt;Everything starts in the Zuplo monetization service. Before you create any plans, you need to define what you're actually charging for. That means setting up &lt;strong&gt;meters&lt;/strong&gt; (the things you count) and &lt;strong&gt;features&lt;/strong&gt; (the things customers get access to).&lt;/p&gt;

&lt;p&gt;For our changelog API, we're metering API requests and gating access to an MCP server (which we'll add in Part 3).&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a Meter
&lt;/h3&gt;

&lt;p&gt;Go to &lt;strong&gt;Services &amp;gt; Monetization Service&lt;/strong&gt; in your Zuplo project. Add a blank meter and call it &lt;code&gt;requests&lt;/code&gt;, with an event name of &lt;code&gt;requests&lt;/code&gt;. This meter will count every API request that gets made.&lt;/p&gt;

&lt;h3&gt;
  
  
  Define Features
&lt;/h3&gt;

&lt;p&gt;Features map to what shows up on your pricing table. Not all features work the same way. Some are metered (counted against a usage limit), some are boolean (on or off), and some are purely for display on the pricing page. We need three:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Requests&lt;/strong&gt;: linked to the &lt;code&gt;requests&lt;/code&gt; meter. This is the usage-based feature that gets counted against a limit. When a subscriber makes an API call, this feature's counter increments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP Server&lt;/strong&gt;: not linked to any meter. This is a boolean feature: you either have access or you don't. Plans can grant or deny it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monthly Fee&lt;/strong&gt;: not linked to any meter. This represents the flat subscription cost on paid plans. It shows the price on the pricing table but doesn't gate anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Plans
&lt;/h2&gt;

&lt;p&gt;With meters and features in place, you can start defining plans. We're building three: Free, Starter, and Pro.&lt;/p&gt;

&lt;h3&gt;
  
  
  Free Plan
&lt;/h3&gt;

&lt;p&gt;Add a new plan called &lt;code&gt;free&lt;/code&gt; with monthly billing. This is a single-phase plan that runs indefinitely (or until the subscriber cancels).&lt;/p&gt;

&lt;p&gt;Add the &lt;code&gt;requests&lt;/code&gt; feature to the phase with a &lt;strong&gt;free pricing model&lt;/strong&gt;. Set the usage limit to 20. That's intentionally low for demonstration purposes, but it shows how hard limits work on free plans. Once a free user hits 20 requests in a billing period, they're cut off.&lt;/p&gt;

&lt;h3&gt;
  
  
  Starter Plan ($29.99/month)
&lt;/h3&gt;

&lt;p&gt;The Starter plan introduces two concepts: a flat monthly fee and graduated overage pricing.&lt;/p&gt;

&lt;p&gt;First, add the &lt;strong&gt;monthly fee&lt;/strong&gt; feature as a flat fee of $29.99, paid in advance once per month.&lt;/p&gt;

&lt;p&gt;Then add the &lt;strong&gt;requests&lt;/strong&gt; feature using a &lt;strong&gt;tiered pricing model&lt;/strong&gt; with &lt;strong&gt;graduated&lt;/strong&gt; mode. Set up two tiers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First 5,000 requests: $0 (included in the monthly fee)&lt;/li&gt;
&lt;li&gt;5,001 to unlimited: $0.10 per request&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Set the usage limit to 5,000 and toggle on &lt;strong&gt;soft limit&lt;/strong&gt;. This is important. A soft limit means that when a subscriber hits 5,000 requests, access doesn't stop. Instead, every request beyond 5,000 gets billed at $0.10 each. At the end&lt;br&gt;
of the billing cycle, Stripe charges the subscriber for $29.99 plus whatever overage they incurred.&lt;/p&gt;

&lt;p&gt;Finally, add the &lt;strong&gt;MCP server&lt;/strong&gt; feature with a free pricing model and a &lt;strong&gt;boolean entitlement&lt;/strong&gt; set to &lt;code&gt;true&lt;/code&gt;. Starter subscribers get MCP server access included in their monthly fee.&lt;/p&gt;
&lt;h3&gt;
  
  
  Pro Plan ($99.99/month)
&lt;/h3&gt;

&lt;p&gt;The Pro plan follows the exact same setup steps as Starter, just with different values: $99.99/month, 50,000 included requests, and $0.01 per request overage (also with a soft limit). MCP server access is included. There's nothing new to configure here. If you set up the Starter plan, you already know how to do this one.&lt;/p&gt;
&lt;h3&gt;
  
  
  Publish and Reorder
&lt;/h3&gt;

&lt;p&gt;Once all three plans are created as drafts, reorder them in the pricing table so they display as Free, Starter, then Pro. Then publish them. These become the live plans available on your developer portal.&lt;/p&gt;

&lt;p&gt;If you want to change plans later, you create new drafts and publish them when ready. They'll replace the existing versions or sit alongside them as additional options.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;a href="https://zuplo.com/docs/articles/monetization/plans" rel="noopener noreferrer"&gt;Monetization Plans and Pricing&lt;/a&gt;&lt;/strong&gt; — Details on phases, free trials, pricing models, and advanced plan configuration.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Connecting Stripe
&lt;/h2&gt;

&lt;p&gt;Before monetization can work end to end, you need a payment provider. Go to the monetization service settings and configure your Stripe integration by pasting in your Stripe secret key. You can find this in the &lt;a href="https://dashboard.stripe.com/apikeys" rel="noopener noreferrer"&gt;Stripe Dashboard&lt;/a&gt; under &lt;strong&gt;Developers &amp;gt;&lt;br&gt;
API keys&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Use your &lt;strong&gt;Stripe test key&lt;/strong&gt; (&lt;code&gt;sk_test_...&lt;/code&gt;) during development. The sandbox environment lets you simulate the full checkout flow with fake credit card numbers from the Stripe documentation. Swap to your production key when you go live.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;a href="https://zuplo.com/docs/articles/monetization/stripe-integration" rel="noopener noreferrer"&gt;Stripe Integration&lt;/a&gt;&lt;/strong&gt; — Full setup instructions for connecting Stripe to the Zuplo monetization service.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Adding the Monetization Policy to Endpoints
&lt;/h2&gt;

&lt;p&gt;With plans, meters, and Stripe configured, you still need to tell your endpoints to actually meter requests. This is done with the &lt;strong&gt;monetization inbound policy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The monetization policy does double duty. It handles both API key authentication and request metering in a single policy, so you don't need a separate API key auth policy anymore. If you had one set up previously (like we did in Part 1 for testing), remove it.&lt;/p&gt;

&lt;p&gt;The policy configuration is straightforward. The only thing you need to specify is the meter name and the increment value:&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;"handler"&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;"export"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MonetizationInboundPolicy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$import(@zuplo/runtime)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"options"&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;"meters"&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;"requests"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&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;Apply this policy to all your endpoints. In the policy chain, make sure monetization comes first (before rate limiting and any other policies) since it needs to authenticate the API key and meter the request before anything else happens.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;a href="https://zuplo.com/docs/articles/monetization/monetization-policy" rel="noopener noreferrer"&gt;Monetization Policy&lt;/a&gt;&lt;/strong&gt; — Full policy reference including metering options, caching, and advanced configuration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Enabling Monetization on the Developer Portal
&lt;/h2&gt;

&lt;p&gt;The developer portal doesn't know about monetization by default. You need to add the monetization plugin to your Zudoku configuration.&lt;/p&gt;

&lt;p&gt;In your project's &lt;code&gt;docs&lt;/code&gt; folder, open the Zudoku config file and import the monetization plugin:&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;zuploMonetizationPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@zuplo/zudoku-plugin-monetization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add it to your plugins array:&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="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="c1"&gt;// ...other plugins&lt;/span&gt;
  &lt;span class="nf"&gt;zuploMonetizationPlugin&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;Once saved, the developer portal pulls in all the plan data, pricing tables, and subscription management UI from the monetization service automatically.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;a href="https://zuplo.com/docs/articles/monetization/developer-portal" rel="noopener noreferrer"&gt;Developer Portal Setup&lt;/a&gt;&lt;/strong&gt; — Full setup instructions for enabling monetization in the developer portal.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Testing the Full Flow
&lt;/h2&gt;

&lt;p&gt;With everything wired up, the developer portal now shows a pricing table with all three plans. Here's what the subscriber experience looks like:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sign up and subscribe.&lt;/strong&gt; A new user logs in (or signs up) on the developer portal. They see the pricing table and can select a plan. Subscribing to the free plan skips Stripe checkout entirely. Paid plans redirect to a Stripe checkout page.&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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-01-building-a-monetized-api-part-2%252Fpricing-table.png%26w%3D1920%26q%3D80" 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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-01-building-a-monetized-api-part-2%252Fpricing-table.png%26w%3D1920%26q%3D80" alt="The developer portal pricing table showing Free, Starter, and Pro plans" width="1920" height="609"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get API keys.&lt;/strong&gt; After subscribing, the user lands on a "My Subscriptions" page with their subscription details, usage analytics, and API keys. Keys are provisioned automatically and can be rolled or deleted from this page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make requests.&lt;/strong&gt; API keys work immediately. The user can test directly from the interactive API reference in the developer portal, where their key is pre-populated in the auth dropdown.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Track usage.&lt;/strong&gt; Every request increments the meter. The subscription page shows real-time usage against the plan's limit. On the free plan with a hard limit of 20, the 21st request gets blocked. On paid plans with soft limits, requests beyond the included amount get billed as overage.&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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-01-building-a-monetized-api-part-2%252Fsubscriptions.png%26w%3D1920%26q%3D80" 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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-01-building-a-monetized-api-part-2%252Fsubscriptions.png%26w%3D1920%26q%3D80" alt="The My Subscriptions page showing plan details, usage tracking, and API key management" width="1920" height="1106"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Upgrade plans.&lt;/strong&gt; Subscribers can switch plans from the subscription management page. Upgrading to a paid plan triggers Stripe checkout. Downgrading is available too. Plan changes take effect immediately, and the previous plan shows&lt;br&gt;
as expired in the subscription history.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;View subscribers.&lt;/strong&gt; Back in the Zuplo monetization service, the subscribers table shows every customer, their subscription history, and their current plan status.&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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-01-building-a-monetized-api-part-2%252Fsubscribers.png%26w%3D1920%26q%3D80" 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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-04-01-building-a-monetized-api-part-2%252Fsubscribers.png%26w%3D1920%26q%3D80" alt="The subscriber detail view in the Zuplo monetization service showing subscription history" width="1920" height="996"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What we built in Part 2
&lt;/h2&gt;

&lt;p&gt;At this point, the monetization layer is fully wired up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[x] Meters tracking every API request&lt;/li&gt;
&lt;li&gt;[x] Three plans (Free, Starter, Pro) with hard and soft limits&lt;/li&gt;
&lt;li&gt;[x] Stripe connected for checkout and billing&lt;/li&gt;
&lt;li&gt;[x] Monetization policy replacing the standalone API key auth&lt;/li&gt;
&lt;li&gt;[x] Developer portal with a self-serve pricing table and subscription management&lt;/li&gt;
&lt;li&gt;[x] End-to-end flow tested: sign up, subscribe, get keys, make requests, track usage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything from Part 1 (origin auth, consumer isolation, rate limiting) still works. The monetization policy took over API key authentication, but the custom header policy that sets the gateway secret and consumer ID didn't need to change at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;In Part 3, we'll add an MCP server to the project and write custom code to feature-gate it so that only paid subscribers can access it. After that, in Part 4, we'll polish the developer portal to make it look production-ready.&lt;/p&gt;

</description>
      <category>api</category>
      <category>monetization</category>
      <category>apigateway</category>
    </item>
    <item>
      <title>Building a Monetized API (Part 1 of 4)</title>
      <dc:creator>Martyn Davies</dc:creator>
      <pubDate>Mon, 13 Apr 2026 12:43:00 +0000</pubDate>
      <link>https://dev.to/zuplo/building-a-monetized-api-part-1-of-4-2po3</link>
      <guid>https://dev.to/zuplo/building-a-monetized-api-part-1-of-4-2po3</guid>
      <description>&lt;p&gt;This is Part 1 of a four-part series on building a fully &lt;a href="https://zuplo.com/solutions/monetize-and-control" rel="noopener noreferrer"&gt;monetized API&lt;/a&gt; with &lt;a href="https://zuplo.com" rel="noopener noreferrer"&gt;Zuplo&lt;/a&gt;, from gateway to self-serve API keys, usage-based plans, MCP server, and developer portal.&lt;/p&gt;

&lt;p&gt;Before any of that works, you need the gateway set up right. That's what we're covering here.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/FMcYofeBoxE"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What we're building
&lt;/h2&gt;

&lt;p&gt;The API behind all of this is a changelog and release notes service. Teams can create projects, publish changelog entries into a database, and query them with full-text search, filtering, and pagination. Think of it as a storage layer for release notes that you can query programmatically.&lt;/p&gt;

&lt;p&gt;It's built with Hono and TypeScript, deployed on Vercel with a Supabase backend. Nothing exotic, but it has enough endpoints (12) and enough complexity (project scoping, search, pagination) to make the monetization and MCP parts of this series actually interesting. A to-do list API wouldn't cut it here.&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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-03-31-building-a-monetized-api-part-1%252Fchangeloggle-portal.png%26w%3D1920%26q%3D80" 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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-03-31-building-a-monetized-api-part-1%252Fchangeloggle-portal.png%26w%3D1920%26q%3D80" alt="The Changeloggle developer portal showing the API reference with all endpoints" width="1671" height="900"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a four-part series. After the gateway setup in this post, we'll cover adding monetization, meters, and plans (Part 2), an MCP server with plan-gated access (Part 3), and developer portal polish (Part 4).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Importing the OpenAPI spec
&lt;/h2&gt;

&lt;p&gt;Starting from a blank Zuplo project, the fastest way to get going is to import the OpenAPI specification that describes the existing API. This pulls in all 12 endpoints with their verbs, paths, and schema definitions.&lt;/p&gt;

&lt;p&gt;After the import, every route's request handler points at &lt;code&gt;api.example.com/v1&lt;/code&gt;, which is the placeholder from the spec. We need to point these at the real API.&lt;/p&gt;

&lt;p&gt;The cleanest way to do this is with an environment variable. In Zuplo's settings, create a new variable called &lt;code&gt;BASE_URL&lt;/code&gt; and set it to the actual API URL. Apply it across all environments (working copy, staging, production).&lt;/p&gt;

&lt;p&gt;Then update the service URL in any one route to use &lt;code&gt;$env(BASE_URL)&lt;/code&gt;. Because Zuplo's route designer shares the service configuration across routes, this single change applies to all 12 endpoints.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;a href="https://zuplo.com/docs/articles/environment-variables" rel="noopener noreferrer"&gt;Environment Variables&lt;/a&gt;&lt;/strong&gt; — Learn more about managing configuration across environments in Zuplo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 2: Origin authentication with a shared secret
&lt;/h2&gt;

&lt;p&gt;Here's where the first important architectural decision comes in. The destination Changeloggle API (hosted on Vercel) is publicly accessible. Anyone who discovers the URL could bypass the gateway entirely. We need to lock that&lt;br&gt;
down.&lt;/p&gt;

&lt;p&gt;The approach: a shared secret. The Changeloggle API checks for an &lt;code&gt;x-gateway-secret&lt;/code&gt; header on every incoming request. If it's missing or wrong, the request gets a 401. Only Zuplo knows the secret, so only Zuplo can talk to the origin.&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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-03-31-building-a-monetized-api-part-1%252Fshared-secret.png%26w%3D1920%26q%3D80" 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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-03-31-building-a-monetized-api-part-1%252Fshared-secret.png%26w%3D1920%26q%3D80" alt="Shared secret flow: the API gateway forwards requests with the secret, while direct requests without it are rejected" width="1920" height="1080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To implement this, add &lt;code&gt;GATEWAY_SECRET&lt;/code&gt; as a secret environment variable in Zuplo's settings. Secrets are encrypted and not visible in the dashboard after creation. We'll wire this into a custom policy alongside consumer isolation in the next step.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 3: Consumer isolation via API key subjects
&lt;/h2&gt;

&lt;p&gt;With the origin locked down, the next step is making sure every request carries the identity of the consumer making it.&lt;/p&gt;

&lt;p&gt;Every API key in Zuplo has a "subject", which is a unique identifier for the consumer. When a request comes in with a valid API key, &lt;code&gt;request.user.sub&lt;/code&gt; contains that subject. By forwarding it to the origin as an &lt;code&gt;x-consumer-id&lt;/code&gt; header, the backend can scope all data operations to that specific consumer.&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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-03-31-building-a-monetized-api-part-1%252Fconsumer-isolation.png%26w%3D1920%26q%3D80" 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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-03-31-building-a-monetized-api-part-1%252Fconsumer-isolation.png%26w%3D1920%26q%3D80" alt="Consumer isolation: each API key's subject is forwarded as x-consumer-id, scoping data to individual customers in Supabase" width="1920" height="1080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This means each API key can only create and access its own projects and changelog entries. There's no way for one consumer to read another consumer's data, because the origin uses that consumer ID as a partition key for all database queries.&lt;/p&gt;

&lt;p&gt;To implement both the shared secret and consumer isolation, first add the built-in API Key Authentication policy to every route. This handles key validation and populates &lt;code&gt;request.user&lt;/code&gt; with the consumer's identity. Then create a custom inbound policy that runs after it. In Zuplo, create a new module called &lt;code&gt;set-request-headers&lt;/code&gt;:&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ZuploContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ZuploRequest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@zuplo/runtime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &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;ZuploRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ZuploContext&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="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-gateway-secret&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GATEWAY_SECRET&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;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-consumer-id&lt;/span&gt;&lt;span class="dl"&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;request&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 API key policy upstream has already validated the request and populated &lt;code&gt;request.user&lt;/code&gt;, so this policy just sets two headers on the outgoing request to the origin: the gateway secret for authentication, and the consumer ID for isolation.&lt;/p&gt;

&lt;p&gt;Why does this matter for monetization? When we add metering and plans in Part 2, Zuplo's monetization system will handle API key creation and consumer identity&lt;br&gt;
automatically. The &lt;code&gt;request.user.sub&lt;/code&gt; value will still be there, and it will still flow through this same policy to the origin. We won't need to change this code at all.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;a href="https://zuplo.com/docs/policies/api-key-inbound" rel="noopener noreferrer"&gt;API Key Authentication&lt;/a&gt;&lt;/strong&gt; — Details on API key subjects, metadata, and key management in Zuplo.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Step 4: Rate limiting
&lt;/h2&gt;

&lt;p&gt;Add a rate limiting inbound policy set to 100 requests per minute, keyed by IP address. Apply it to all routes, and make sure it executes &lt;strong&gt;before&lt;/strong&gt; the header-setting policy so abusive traffic is rejected as cheaply as possible.&lt;/p&gt;

&lt;p&gt;This is distinct from the request limits that monetization adds in Part 2.&lt;/p&gt;

&lt;p&gt;Request limits are business logic: a free tier gets 20 requests/month, a paid tier gets 50,000. Rate limiting is infrastructure protection: no single IP should be able to send 1,000 requests in a minute regardless of their plan. You need both.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 5: Testing end to end
&lt;/h2&gt;

&lt;p&gt;To verify the full chain works, we need a temporary API key. In Zuplo's API key service, create a consumer with a UUID as the subject. Generate a key for that consumer.&lt;/p&gt;

&lt;p&gt;Then make a test request to the create project endpoint with the API key as a Bearer token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://your-gateway.zuplo.dev/v1/projects &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer zpka_your_api_key"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"name": "My Project", "description": "A test project", "url": "https://example.com"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A 201 response confirms the full chain is working: Zuplo received the request, validated the API key, set the gateway secret and consumer ID headers, forwarded it to the Changeloggle API on Vercel, and the origin accepted it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: this API key setup is temporary. When we add monetization in the next post, API key creation and management moves to the self-serve developer portal.&lt;br&gt;
Consumers will sign up, choose a plan, and generate their own keys. The manual key service configuration goes away.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What we built in Part 1
&lt;/h2&gt;

&lt;p&gt;At this point, the gateway is set up with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[x] All 12 endpoints imported from the OpenAPI spec&lt;/li&gt;
&lt;li&gt;[x] Environment-based upstream URL configuration&lt;/li&gt;
&lt;li&gt;[x] Origin authentication via a shared secret&lt;/li&gt;
&lt;li&gt;[x] Consumer isolation via API key subjects forwarded as headers&lt;/li&gt;
&lt;li&gt;[x] IP-based rate limiting as an abuse backstop&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every one of these decisions was made with monetization in mind. The shared secret locks down the origin. The consumer ID enables per-customer data isolation. The rate limiting sits in a position where it can coexist with per-plan metering. And none of this needs to be rewritten when we add billing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next up: Adding Monetization
&lt;/h2&gt;

&lt;p&gt;In Part 2, we'll add metering, create subscription plans (free, starter, and pro), configure the developer portal's pricing page, and test the full flow of a user signing up, choosing a plan, and making authenticated requests with&lt;br&gt;
usage tracking.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Configure metering on API requests&lt;/li&gt;
&lt;li&gt;[ ] Create subscription plans (free, starter, pro)&lt;/li&gt;
&lt;li&gt;[ ] Connect Stripe for checkout and billing&lt;/li&gt;
&lt;li&gt;[ ] Set up the developer portal pricing page&lt;/li&gt;
&lt;li&gt;[ ] Test the full sign-up and usage tracking flow&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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-03-31-building-a-monetized-api-part-1%252Fchangeloggle-pricing.png%26w%3D1920%26q%3D80" 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%2Fzuplo.com%2F_vercel%2Fimage%3Furl%3D%252Fmedia%252Fposts%252F2026-03-31-building-a-monetized-api-part-1%252Fchangeloggle-pricing.png%26w%3D1920%26q%3D80" alt="The Changeloggle pricing page with Free, Starter, and Pro plans" width="1663" height="648"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>monetization</category>
      <category>apigateway</category>
    </item>
    <item>
      <title>Implementing Idempotency Keys in REST APIs</title>
      <dc:creator>Adrian Machado</dc:creator>
      <pubDate>Thu, 04 Sep 2025 06:15:56 +0000</pubDate>
      <link>https://dev.to/zuplo/implementing-idempotency-keys-in-rest-apis-3e3n</link>
      <guid>https://dev.to/zuplo/implementing-idempotency-keys-in-rest-apis-3e3n</guid>
      <description>&lt;p&gt;&lt;strong&gt;Idempotency keys ensure your REST APIs handle duplicate requests safely and predictably.&lt;/strong&gt; This prevents issues like double charges, duplicate accounts, or inconsistent data caused by retries or network failures.&lt;/p&gt;

&lt;p&gt;Here’s what you need to know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What are Idempotency Keys?&lt;/strong&gt; Unique identifiers sent with API requests to
prevent duplicate processing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why are they important?&lt;/strong&gt; They ensure consistent outcomes for critical
operations like payments or resource creation, even if the same request is sent multiple times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How do they work?&lt;/strong&gt; Clients generate a key (e.g., UUID) and include it in
the request. Servers store the key and response, skipping duplicate processing if the key is reused.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key considerations:&lt;/strong&gt; Handle concurrent requests, set expiration periods
(e.g., 24 hours), and validate requests for consistency.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide explores idempotency principles, implementation examples in Python, TypeScript, and Go, and best practices to avoid common mistakes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Video: Idempotency in APIs Explained | Why It Matters + Code Example
&lt;/h2&gt;

&lt;p&gt;Here's a quick video to get you up to speed on what idempotency means:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/dZTkBeB_dmg"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  How Idempotency Keys Work
&lt;/h2&gt;

&lt;p&gt;Idempotency keys serve as unique identifiers for API operations, helping servers recognize and manage duplicate requests. By understanding how these keys function, developers can create systems that handle retries and network failures more effectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating and Sending Idempotency Keys
&lt;/h3&gt;

&lt;p&gt;Clients are responsible for generating idempotency keys, often using a UUID version 4 or another random string with enough variability to avoid collisions. These keys are included with API requests, typically in HTTP headers like &lt;code&gt;Idempotency-Key&lt;/code&gt; or as part of the request payload.&lt;/p&gt;

&lt;p&gt;For instance, a payment API might require an &lt;code&gt;IdempotencyKey&lt;/code&gt; to ensure that retrying a request doesn’t accidentally charge a customer twice. When a payment request is made, the client includes this key in the request options. If the initial request times out and gets retried, the server uses the same key to ensure the customer isn’t billed again. This approach protects both the merchant and the customer from unintended duplicate transactions.&lt;/p&gt;

&lt;p&gt;Timing is critical when generating these keys. They should be created &lt;em&gt;before&lt;/em&gt; the first request is sent, not during retries. This ensures that every attempt of the same operation uses the same key, allowing the server to detect duplicate requests properly.&lt;/p&gt;

&lt;p&gt;Once the client sends the key, the server takes over to ensure consistent handling.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server-Side Processing of Idempotency Keys
&lt;/h3&gt;

&lt;p&gt;When a server receives a request containing an idempotency key, it checks its storage - usually a database or cache - to see if the key already exists.&lt;/p&gt;

&lt;p&gt;If the key is new, the server processes the request and stores the key along with the result, including the &lt;strong&gt;status code and response body&lt;/strong&gt;. This storage happens regardless of whether the operation succeeds or fails, ensuring that any retries will return the same response.&lt;/p&gt;

&lt;p&gt;If a duplicate request arrives with the same key, the server skips the operation entirely. Instead, it retrieves the previously stored result and sends it back to the client. This prevents repeated processing while maintaining the appearance of a normal API response.&lt;/p&gt;

&lt;p&gt;The server also verifies that repeated requests match the original request parameters. If a client sends the same idempotency key with different data, the server rejects the request with an error. This prevents accidental misuse of keys across unrelated operations.&lt;/p&gt;

&lt;p&gt;How long these keys are stored is another important consideration. They need to persist long enough to cover typical retry periods - usually between &lt;strong&gt;24 hours and 7 days&lt;/strong&gt; - but not indefinitely. Storing keys for too long can lead to performance issues and increased costs.&lt;/p&gt;

&lt;p&gt;Handling concurrent requests with the same key adds another layer of complexity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Managing Concurrent Requests
&lt;/h3&gt;

&lt;p&gt;When multiple requests with the same idempotency key arrive at the same time, the system must ensure only one of them executes the operation. The others should either wait for the result or receive the &lt;a href="https://zuplo.com/learning-center/2025/02/28/how-developers-can-use-caching-to-improve-api-performance" rel="noopener noreferrer"&gt;cached response&lt;/a&gt; once it’s available.&lt;/p&gt;

&lt;p&gt;To handle this, most systems use &lt;strong&gt;database-level locking&lt;/strong&gt; or &lt;strong&gt;distributed locks&lt;/strong&gt;. The first request to acquire the lock proceeds with the operation, while subsequent requests either wait or retrieve the stored result once the operation is complete. Race conditions can occur during the brief moment between checking for an existing key and saving the result. To avoid this, atomic database transactions are essential. These transactions combine the key check and result storage into a single step, ensuring only one request is treated as the first attempt.&lt;/p&gt;

&lt;p&gt;Timeout policies are also critical in these scenarios. If the initial request fails or takes too long, waiting requests need clear rules on how long to wait before timing out. Some systems use progressive timeouts to limit how long requests are held before returning an error.&lt;/p&gt;

&lt;p&gt;The choice between blocking and non-blocking approaches depends on system needs. Blocking ensures stronger consistency but can slow response times. Non-blocking methods return faster responses but require more complex client-side handling to resolve temporary conflicts.&lt;/p&gt;

&lt;p&gt;Monitoring the usage of idempotency keys can help identify problems, such as excessive duplicate requests caused by client retry logic or issues with load balancing. High levels of concurrent requests with the same key may indicate inefficiencies in the client’s implementation or network setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation in Python, TypeScript, and Go
&lt;/h2&gt;

&lt;p&gt;This section dives into practical examples of implementing idempotency in Python, TypeScript, and Go. Each language has its own strengths and tools that make managing idempotency efficient and straightforward.&lt;/p&gt;

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

&lt;p&gt;In Python, frameworks like &lt;a href="https://flask.palletsprojects.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;Flask&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://www.djangoproject.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;Django&lt;/strong&gt;&lt;/a&gt; provide excellent support for handling idempotency keys. Below is an example using Flask, where a UUIDv4 key is generated and sent via the &lt;code&gt;Idempotency-Key&lt;/code&gt; header. Middleware is used to intercept requests and ensure no duplicate processing occurs.&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;uuid&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&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;jsonify&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;functools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;wraps&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;Flask&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;redis_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;6379&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;idempotent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nd"&gt;@wraps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decorated_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;idempotency_key&lt;/span&gt; &lt;span class="o"&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;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Idempotency-Key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;idempotency_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&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;Idempotency-Key header required&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;

        &lt;span class="c1"&gt;# Check if the key exists in Redis
&lt;/span&gt;        &lt;span class="n"&gt;cached_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;idempotent:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;idempotency_key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cached_response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;response_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cached_response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;response_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="c1"&gt;# Process the request and cache the result
&lt;/span&gt;        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response_data&lt;/span&gt; &lt;span class="o"&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;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get_json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&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="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&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="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;# Cache the response for 24 hours
&lt;/span&gt;        &lt;span class="n"&gt;redis_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setex&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="s"&gt;idempotent:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;idempotency_key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response_data&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;decorated_function&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/payments&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&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;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nd"&gt;@idempotent&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_payment&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;payment_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;# Simulate payment processing
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;payment_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&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;completed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Django, developers often use database models to store idempotency keys, benefiting from built-in persistence and atomic operations. Asynchronous frameworks like &lt;a href="https://fastapi.tiangolo.com/" rel="noopener noreferrer"&gt;FastAPI&lt;/a&gt; can also improve performance for high-traffic scenarios.&lt;/p&gt;

&lt;p&gt;Check out the following guides to get started with each framework&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://zuplo.com/learning-center/2025/01/26/fastapi-tutorial" rel="noopener noreferrer"&gt;FastAPI API Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zuplo.com/learning-center/2025/03/29/flask-api-tutorial" rel="noopener noreferrer"&gt;Flask API Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;When working with &lt;a href="https://nodejs.org/en" rel="noopener noreferrer"&gt;&lt;strong&gt;Node.js&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://expressjs.com/" rel="noopener noreferrer"&gt;Express&lt;/a&gt; in TypeScript, middleware patterns simplify idempotency handling. Using storage solutions like &lt;a href="https://redis.io/" rel="noopener noreferrer"&gt;Redis&lt;/a&gt; or &lt;a href="https://www.mongodb.com/" rel="noopener noreferrer"&gt;MongoDB&lt;/a&gt; ensures responses are cached effectively.&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;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;v4&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;uuidv4&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uuid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Redis&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ioredis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&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;redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6379&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CachedResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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;idempotencyMiddleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;express&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;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NextFunction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;idempotencyKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;idempotency-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&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;idempotencyKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Idempotency-Key header required&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cachedResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`idempotent:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;idempotencyKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cachedResponse&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="na"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CachedResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cachedResponse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsed&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="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsed&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="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Intercept res.json to cache the response&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originalJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &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="kr"&gt;any&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="na"&gt;responseData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CachedResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&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="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="p"&gt;};&lt;/span&gt;

      &lt;span class="c1"&gt;// Cache for 24 hours&lt;/span&gt;
      &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`idempotent:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;idempotencyKey&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="mi"&gt;86400&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="nx"&gt;responseData&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="nf"&gt;originalJson&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="p"&gt;};&lt;/span&gt;

    &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Idempotency middleware 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="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/orders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;idempotencyMiddleware&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orderData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// Simulate order processing&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orderId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;uuidv4&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;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;orderData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;orderData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;confirmed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&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;Frameworks like &lt;a href="https://nestjs.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;NestJS&lt;/strong&gt;&lt;/a&gt; provide additional support through decorators and dependency injection, offering a structured way to handle idempotency. TypeScript's type system ensures consistent response formats, reducing errors.&lt;/p&gt;

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

&lt;p&gt;Go is ideal for high-performance idempotency implementations due to its concurrency capabilities and efficient standard libraries. Here’s an example using Go’s HTTP library and a simple in-memory store:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"encoding/json"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
    &lt;span class="s"&gt;"sync"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/google/uuid"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/gorilla/mux"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CachedResponse&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;StatusCode&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;         &lt;span class="s"&gt;`json:"status_code"`&lt;/span&gt;
    &lt;span class="n"&gt;Body&lt;/span&gt;       &lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="s"&gt;`json:"body"`&lt;/span&gt;
    &lt;span class="n"&gt;Timestamp&lt;/span&gt;  &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;   &lt;span class="s"&gt;`json:"timestamp"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;IdempotencyStore&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mu&lt;/span&gt;    &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RWMutex&lt;/span&gt;
    &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="n"&gt;CachedResponse&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewIdempotencyStore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;IdempotencyStore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;IdempotencyStore&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="n"&gt;CachedResponse&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cleanup&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;store&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;IdempotencyStore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CachedResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RLock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RUnlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Since&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;24&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hour&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nb"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&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;CachedResponse&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="no"&gt;false&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;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;IdempotencyStore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;statusCode&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CachedResponse&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Timestamp&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;IdempotencyStore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;cleanup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ticker&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTicker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hour&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Since&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;24&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hour&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&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;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&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;var&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NewIdempotencyStore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c"&gt;// IdempotencyMiddleware checks for the Idempotency-Key header and returns a cached response if available.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;IdempotencyMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handler&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;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandlerFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Idempotency-Key"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Idempotency-Key header required"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusBadRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go’s simplicity and performance make it a great choice for handling idempotent operations, especially in systems where speed and reliability are critical.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices and Common Mistakes
&lt;/h2&gt;

&lt;p&gt;When implementing idempotency keys, it's crucial to focus on security, performance, and reliability. Even seasoned developers can make mistakes that compromise an API's functionality or create frustrating user experiences.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Practices for Idempotency Keys
&lt;/h3&gt;

&lt;p&gt;To ensure your API remains dependable and secure, follow these key practices:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generate secure keys:&lt;/strong&gt; Use UUIDv4 or random strings with at least 128 bits of entropy. Let client applications generate these keys before sending requests. This way, clients maintain control over retry logic and can safely resend requests using the same key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set expiration times tailored to your needs:&lt;/strong&gt; Choose expiration windows that align with your business requirements. For instance, a 24-hour expiration balances storage limitations with reliable retries, while critical operations might call for longer durations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Store keys using atomic operations:&lt;/strong&gt; Leverage atomic operations, like database transactions or Redis commands, to prevent race conditions when storing idempotency keys.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Incorporate request fingerprinting:&lt;/strong&gt; Alongside the idempotency key, hash key request details (e.g., transaction amount, recipient info, timestamp) to confirm that repeated key usage matches the original request data. This prevents unauthorized or unintentional actions if a key is reused incorrectly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implement cleanup processes:&lt;/strong&gt; Use background tasks to remove expired keys and their associated responses, ensuring your storage system remains efficient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Return consistent responses for cached results:&lt;/strong&gt; When serving responses from cache, ensure the HTTP status codes and response bodies are identical to the original output.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Implementation Mistakes
&lt;/h3&gt;

&lt;p&gt;To maintain secure and consistent idempotent operations, avoid these frequent errors:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using weak key generation:&lt;/strong&gt; Avoid predictable patterns like sequential numbers or timestamps. These can be exploited by attackers to guess valid keys and replay operations. For instance, auto-incrementing database IDs pose a significant security risk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Neglecting concurrent request handling:&lt;/strong&gt; Failing to manage concurrent identical requests can lead to duplicate processing, undermining the purpose of idempotency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caching error responses:&lt;/strong&gt; Storing failure responses - especially those caused by temporary issues like network timeouts - can confuse clients and block successful retries. Only cache successful operations or errors that won't change upon retry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skipping request validation when storing keys:&lt;/strong&gt; Simply checking for a key's existence without verifying that the accompanying request data matches the original can leave your API vulnerable to misuse. Always validate that the current request's parameters align with the original data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choosing inadequate storage solutions:&lt;/strong&gt; Select storage backends that are both fast and reliable. In-memory stores risk data loss on restart, while unindexed databases may struggle under heavy traffic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Overlooking key scope and isolation:&lt;/strong&gt; Ensure idempotency keys are uniquely scoped by adding contextual identifiers, such as user IDs, API versions, or endpoint details. This prevents data from leaking between users or operations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://zuplo.com?utm_source=dev.to&amp;amp;utm_medium=inline-cta"&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%2Fiikcuf7axhu4gkw0jmqa.png" width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Zuplo for Idempotency Key Management
&lt;/h2&gt;

&lt;p&gt;Creating a system to manage idempotency keys from the ground up can be a complex and error-prone task. Zuplo's &lt;a href="https://zuplo.com/learning-center/2025/05/06/api-management-vs-api-gateway" rel="noopener noreferrer"&gt;API management platform&lt;/a&gt; simplifies this challenge by providing a &lt;a href="https://zuplo.com/features/programmable" rel="noopener noreferrer"&gt;programmable API gateway&lt;/a&gt; combined with features like authentication and rate limiting. This all-in-one solution makes implementing idempotent operations more straightforward while ensuring your APIs perform reliably. Let’s take a closer look at the key features that make this possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Zuplo Features for Idempotent APIs
&lt;/h3&gt;

&lt;p&gt;Zuplo stands out with its &lt;strong&gt;unlimited extensibility&lt;/strong&gt;, allowing developers to craft custom idempotency logic and reusable policies. These policies can be consistently applied across various endpoints and API versions, streamlining operations. Its &lt;strong&gt;edge deployment&lt;/strong&gt; ensures low-latency responses, a critical factor when handling duplicate requests efficiently.&lt;/p&gt;

&lt;p&gt;Another valuable feature is its &lt;a href="https://zuplo.com/blog/2024/07/19/what-is-gitops" rel="noopener noreferrer"&gt;&lt;strong&gt;GitOps integration&lt;/strong&gt;&lt;/a&gt;, which enables version control for API configurations and policies. This makes it easier to track changes, reduce configuration drift, and audit updates across development, staging, and production environments.&lt;/p&gt;

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

&lt;p&gt;Integrating idempotency keys into REST APIs is a must for creating dependable, production-ready systems. In this guide, we’ve looked at how these keys help prevent duplicate operations, handle network issues gracefully, and deliver a consistent experience for users.&lt;/p&gt;

&lt;p&gt;For businesses in the U.S., idempotency keys are especially important. They safeguard data integrity and eliminate duplicate operations, which directly influences customer satisfaction and your bottom line. In industries like finance, where precision is critical, idempotency has become a standard practice to ensure secure processing during retries or user errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Takeaways
&lt;/h3&gt;

&lt;p&gt;Here’s a quick recap of the key points from this guide:&lt;/p&gt;

&lt;p&gt;At its core, &lt;strong&gt;idempotency keys address real-world business challenges&lt;/strong&gt;. By implementing unique keys (such as UUIDs), scoping them per client, and leveraging tools like Redis for distributed caching, you create a reliable safety net for your systems and users.&lt;/p&gt;

&lt;p&gt;Implementation best practices are crucial. &lt;strong&gt;Using distributed locks to prevent race conditions, validating request payloads to ensure data accuracy, and setting optimal cache durations&lt;/strong&gt; are all essential steps. For example, a 24-48 hour cache duration for payment operations is widely accepted as effective for most retry scenarios.&lt;/p&gt;

&lt;p&gt;While the technical details vary across languages like Python, TypeScript, and Go, the principles remain consistent. &lt;strong&gt;Thread-safe operations, robust error handling, and clear response headers&lt;/strong&gt; (e.g., &lt;code&gt;X-Idempotent-Replay: true&lt;/code&gt;) help clients easily distinguish between cached responses and freshly processed ones.&lt;/p&gt;

&lt;p&gt;Platforms like Zuplo offer a streamlined approach to idempotency management. With features such as &lt;strong&gt;key validation hooks, integration with authentication systems, and distributed caching support&lt;/strong&gt;, these platforms simplify implementation while ensuring high reliability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next Steps for Developers
&lt;/h3&gt;

&lt;p&gt;Here’s how you can start integrating idempotency into your APIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identify critical operations that require idempotency, such as POST or PATCH
requests for creating resources, processing payments, or modifying important data. Focus on high-value, high-risk operations first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explore API management platforms like Zuplo&lt;/strong&gt;, especially if you’re managing
multiple APIs or working in a team. The time saved on development and testing can make these tools well worth the investment.&lt;/li&gt;
&lt;li&gt;Test your implementation thoroughly. Simulate scenarios like network timeouts,
duplicate requests, and concurrent operations to ensure your system handles them correctly. Monitor production for duplicate operations and adjust cache durations based on real-world usage.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do idempotency keys ensure reliable payment processing in REST APIs?
&lt;/h3&gt;

&lt;p&gt;Idempotency keys are essential for ensuring reliable payment processing in REST APIs by preventing duplicate transactions. When a request is retried - perhaps due to network timeouts or errors - the server uses the idempotency key to identify it as a repeat and responds with the original outcome, rather than processing the payment again.&lt;/p&gt;

&lt;p&gt;This mechanism safeguards against problems like double billing or multiple charges, even if the same request is sent multiple times. By keeping transactions consistent and accurate, idempotency keys enhance the reliability of payment systems and minimize errors in critical financial operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  What challenges can arise when using idempotency keys in high-concurrency environments, and how can developers address them?
&lt;/h3&gt;

&lt;h2&gt;
  
  
  Implementing Idempotency Keys in High-Concurrency Environments
&lt;/h2&gt;

&lt;p&gt;When dealing with high-concurrency systems, implementing idempotency keys can be tricky. Challenges often include &lt;strong&gt;performance overhead&lt;/strong&gt; from storing and validating these keys, along with the &lt;strong&gt;complexity of managing distributed systems&lt;/strong&gt; to identify duplicate requests. These hurdles are particularly noticeable in systems that handle a large number of simultaneous operations.&lt;/p&gt;

&lt;p&gt;To tackle these issues, developers can take a few practical steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Optimize storage&lt;/strong&gt;: Use fast and scalable databases or caching systems to
handle idempotency keys more efficiently. This ensures quick access and minimizes delays.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coordinate distributed systems&lt;/strong&gt;: Techniques like &lt;strong&gt;distributed locking&lt;/strong&gt; or
&lt;strong&gt;consensus algorithms&lt;/strong&gt; can help maintain proper synchronization between services, avoiding conflicts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streamline key-checking&lt;/strong&gt;: Lightweight mechanisms for verifying keys can cut
down on performance bottlenecks while still ensuring the system remains reliable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By focusing on these strategies, developers can better manage the demands of high-concurrency environments without sacrificing efficiency or reliability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is it important to validate request parameters when using idempotency keys, and what could go wrong if you don’t?
&lt;/h3&gt;

&lt;p&gt;Validating request parameters alongside &lt;strong&gt;idempotency keys&lt;/strong&gt; plays a critical role in ensuring API operations run smoothly and predictably. This process ensures that repeated requests using the same idempotency key remain consistent in both intent and content, effectively preventing unexpected outcomes.&lt;/p&gt;

&lt;p&gt;Skipping parameter validation can lead to problems like inconsistent data updates or even security loopholes. For example, imagine a client resending a request with the same idempotency key but tweaking the parameters. Without proper validation, the server might mistakenly reuse the original response, causing inaccurate results or bypassing essential checks. By thoroughly verifying parameters, you protect against these risks and uphold the dependability of your API.&lt;/p&gt;

</description>
      <category>api</category>
    </item>
    <item>
      <title>Optimizing REST APIs with Conditional Requests and ETags</title>
      <dc:creator>Adrian Machado</dc:creator>
      <pubDate>Thu, 04 Sep 2025 06:15:39 +0000</pubDate>
      <link>https://dev.to/zuplo/optimizing-rest-apis-with-conditional-requests-and-etags-2po8</link>
      <guid>https://dev.to/zuplo/optimizing-rest-apis-with-conditional-requests-and-etags-2po8</guid>
      <description>&lt;p&gt;&lt;strong&gt;Want faster APIs and less wasted bandwidth?&lt;/strong&gt; Conditional requests and ETags can make that happen. These tools ensure your REST API only sends updated data when needed, cutting down on unnecessary transfers and improving speed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Takeaways
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Conditional Requests&lt;/strong&gt;: Use HTTP headers like &lt;code&gt;If-None-Match&lt;/code&gt; to check if
data has changed before sending it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ETags&lt;/strong&gt;: Unique resource identifiers (like fingerprints) that servers use to
track changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How it Works&lt;/strong&gt;: Clients store ETags and send them back with requests. If
data hasn’t changed, the server replies with a quick &lt;strong&gt;304 Not Modified&lt;/strong&gt; instead of sending the full resource.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Benefits&lt;/strong&gt;: Saves bandwidth, reduces server load, and speeds up API
responses.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://flask.palletsprojects.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;Python Flask&lt;/strong&gt;&lt;/a&gt;: Tools like
&lt;code&gt;Blueprint.etag&lt;/code&gt; or &lt;code&gt;Werkzeug&lt;/code&gt; simplify ETag handling.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://expressjs.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;Node.js Express&lt;/strong&gt;&lt;/a&gt;: Auto-generates ETags with
built-in support for conditional requests.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://zuplo.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;Zuplo API Gateway&lt;/strong&gt;&lt;/a&gt;: Offloads ETag logic to the edge
for better performance and scalability.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pro Tips
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Pair ETags with &lt;code&gt;Cache-Control&lt;/code&gt; for smarter caching.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;strong ETags&lt;/strong&gt; for exact matches and &lt;strong&gt;weak ETags&lt;/strong&gt; for less strict
scenarios.&lt;/li&gt;
&lt;li&gt;Test thoroughly to ensure your API handles all client states effectively.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These techniques are simple yet powerful for optimizing REST APIs, improving performance, and ensuring efficient data delivery.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are Conditional Requests and ETags
&lt;/h2&gt;

&lt;p&gt;Conditional requests and ETags play a crucial role in making client-server interactions more efficient. A conditional request is an HTTP mechanism that tells the server to deliver a resource only if certain conditions are met - such as whether the resource has changed since the client last accessed it. ETags, short for entity tags, are unique identifiers assigned by servers to specific versions of resources. These identifiers update whenever the content changes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Conditional requests optimize web performance by reducing bandwidth usage and &amp;gt; server load while improving user experience through efficient HTTP caching &amp;gt; mechanisms." - Azion&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here’s how it works: when a client requests a resource for the first time, the server includes an ETag in its response. For subsequent requests, the client sends back this ETag. If the resource hasn’t changed, the server responds with a 304 status code, signaling that no new data needs to be sent. This saves bandwidth and speeds up interactions. Let’s break down the HTTP headers that make this process possible and explore the differences between strong and weak ETags.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTP Headers for Conditional Requests
&lt;/h3&gt;

&lt;p&gt;Several HTTP headers are used to implement conditional requests, each serving a specific purpose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;If-Match&lt;/code&gt;: Ensures an operation proceeds only if the resource matches a
specific ETag. This is particularly useful for avoiding conflicts during updates.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;If-Modified-Since&lt;/code&gt;: Relies on timestamps rather than ETags, asking the server
to send the resource only if it has been updated after a given date.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;If-Unmodified-Since&lt;/code&gt;: Works as the reverse, ensuring operations only occur if
the resource hasn’t changed since a specified time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These headers are essential for preventing update conflicts. For instance, when two clients attempt to modify the same resource simultaneously, these conditional headers help avoid the "lost update problem", where one client’s changes could accidentally overwrite another’s.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strong vs. Weak ETags
&lt;/h3&gt;

&lt;p&gt;ETags come in two flavors, each serving different validation needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strong ETags&lt;/strong&gt;: These ensure that two resources are identical down to the
last byte. Even the smallest change in the resource will result in a new ETag. For example:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  ETag: "abc123"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Weak ETags&lt;/strong&gt;: These indicate that two resources are semantically the same,
even if they differ slightly at the byte level. Weak ETags are marked with a &lt;code&gt;W/&lt;/code&gt; prefix, like this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  ETag: W/"abc123"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The choice between strong and weak ETags depends on what you need. Strong ETags are ideal for resources where strict version control is required or when generating precise content hashes is feasible. Weak ETags are easier to implement and work well when minor changes - like formatting tweaks - don’t affect the resource’s core value. However, weak ETags can interfere with caching for byte-range requests, whereas strong ETags support proper caching in these scenarios.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Conditional Request Workflow Works
&lt;/h3&gt;

&lt;p&gt;The workflow behind conditional requests ensures efficient data transfer by revalidating resources only when necessary. This minimizes redundant data transfers, saving bandwidth and processing power.&lt;/p&gt;

&lt;p&gt;This approach is especially useful for APIs that handle frequently accessed but rarely updated data. In fact, the ETag header is used in about 25% of web responses, highlighting its importance in improving web performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Implement ETags and Conditional Requests
&lt;/h2&gt;

&lt;p&gt;ETags and conditional requests are implemented differently depending on the framework or platform you use. Here's a look at how you can handle them in &lt;strong&gt;Python Flask&lt;/strong&gt;, &lt;strong&gt;Node.js Express&lt;/strong&gt;, and &lt;strong&gt;Zuplo's API Gateway&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://flask.palletsprojects.com/" rel="noopener noreferrer"&gt;Python Flask&lt;/a&gt; Implementation
&lt;/h3&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%2Fs1p5v9fc034ldhd1rv81.jpg" 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%2Fs1p5v9fc034ldhd1rv81.jpg" alt="Python Flask" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Flask simplifies ETag handling with tools like the &lt;code&gt;flask-rest-api&lt;/code&gt; library. This library includes a &lt;code&gt;Blueprint.etag&lt;/code&gt; decorator, which helps generate and validate ETags automatically. It works by computing ETags based on your API response data using schema serialization. To ensure consistency, you’ll need to define an explicit schema.&lt;/p&gt;

&lt;p&gt;For more advanced use cases, such as responses with HATEOAS links, you can create a dedicated ETag schema that zeroes in on the relevant data. Alternatively, you can manually compute ETags with &lt;code&gt;Blueprint.set_etag&lt;/code&gt; and validate them using &lt;code&gt;Blueprint.check_etag&lt;/code&gt; before performing resource updates or deletions.&lt;/p&gt;

&lt;p&gt;If you need even more control, Flask’s &lt;a href="https://werkzeug.palletsprojects.com/" rel="noopener noreferrer"&gt;Werkzeug&lt;/a&gt; library provides the &lt;code&gt;ETagResponseMixin&lt;/code&gt;. This allows you to add an ETag to your response with &lt;code&gt;response.add_etag()&lt;/code&gt; and use &lt;code&gt;response.make_conditional()&lt;/code&gt; to automatically return a &lt;strong&gt;304 Not Modified&lt;/strong&gt; status if the client’s cached version matches the ETag.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://expressjs.com/" rel="noopener noreferrer"&gt;Node.js Express&lt;/a&gt; Implementation
&lt;/h3&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%2Ftt8wj1fvo6bwhzjleva3.jpg" 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%2Ftt8wj1fvo6bwhzjleva3.jpg" alt="Node.js Express" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Express makes ETag handling straightforward by automatically generating them using a SHA1 hash of the response body. This works seamlessly with Express's built-in support for conditional requests. When a client sends an &lt;code&gt;If-None-Match&lt;/code&gt; header with a cached ETag, Express compares it to the current response. If they match, the server responds with a &lt;strong&gt;304 Not Modified&lt;/strong&gt; status, reducing bandwidth usage without requiring extra code.&lt;/p&gt;

&lt;p&gt;If you need to disable this default behavior - for example, when hashing large responses becomes inefficient - you can use &lt;code&gt;app.set("etag", false)&lt;/code&gt;. For custom validation, you can manually inspect headers in your route handlers to decide whether to send updated content or a &lt;strong&gt;304&lt;/strong&gt; response.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Zuplo's API Gateway
&lt;/h3&gt;

&lt;p&gt;Zuplo offers a different approach by handling ETags and conditional requests directly at the gateway level. This means you don’t have to modify your backend services. With Zuplo, you can implement caching and conditional logic using its &lt;strong&gt;Custom Code Inbound Policy&lt;/strong&gt;, where TypeScript modules let you validate request headers and manage ETags.&lt;/p&gt;

&lt;p&gt;Zuplo also includes tools like the &lt;strong&gt;Request Size Limit Policy&lt;/strong&gt;, which ensures that large ETag values or excessive headers don’t cause issues. Its globally distributed architecture minimizes latency across the United States, making ETag-based caching even more effective. Additionally, Zuplo provides analytics to help you monitor how well your conditional request setup is performing and where improvements can be made.&lt;/p&gt;

&lt;p&gt;Another feature, the &lt;strong&gt;Rate Limiting Policy&lt;/strong&gt;, works hand-in-hand with caching by dynamically adjusting limits based on API key activity and cache performance. Zuplo’s flexibility allows you to implement dynamic ETag strategies that adapt to traffic patterns, server load, and user behavior. By offloading caching logic to Zuplo’s gateway, you can deliver faster and more reliable API responses without overloading your backend.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://zuplo.com?utm_source=dev.to&amp;amp;utm_medium=inline-cta"&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%2Fiikcuf7axhu4gkw0jmqa.png" width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for REST API Optimization
&lt;/h2&gt;

&lt;p&gt;Using ETags and conditional requests can significantly improve performance while maintaining data accuracy. These techniques help avoid common challenges, ensuring your API functions efficiently and effectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Generate Effective ETags
&lt;/h3&gt;

&lt;p&gt;Creating effective ETags begins with selecting the right generation strategy. &lt;strong&gt;Strong ETags&lt;/strong&gt; are ideal when you need an exact, byte-for-byte match, making them perfect for critical data requiring precision. However, they can be resource-intensive to produce. &lt;strong&gt;Weak ETags&lt;/strong&gt;, on the other hand, are easier to generate and work well for most use cases, as they still provide reliable cache validation.&lt;/p&gt;

&lt;p&gt;To ensure security and consistency, always generate ETags on the server side. Avoid accepting client-generated ETags, as they could be tampered with. Instead, compute them using trusted methods like content hashes, SHA-256 hashes, or revision numbers. For example, if you're using &lt;a href="https://learn.microsoft.com/en-us/ef/core/" rel="noopener noreferrer"&gt;Entity Framework Core&lt;/a&gt; with &lt;a href="https://www.microsoft.com/en-us/sql-server" rel="noopener noreferrer"&gt;SQL Server&lt;/a&gt;, the built-in &lt;code&gt;rowversion&lt;/code&gt; feature can simplify version tracking and ETag generation by automatically reflecting database changes.&lt;/p&gt;

&lt;p&gt;Separating ETag generation into a dedicated service layer is another best practice. This approach prevents your hashing logic from being too tightly linked to your data models, making your code easier to maintain and test. Additionally, when implementing updates, ensure your API supports PATCH requests with ETag validation for more efficient data handling.&lt;/p&gt;

&lt;p&gt;By combining these ETag strategies with effective caching methods, you can further enhance your API's performance, as detailed in the next section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Combining Cache-Control and Validation Headers
&lt;/h3&gt;

&lt;p&gt;A robust caching strategy pairs &lt;strong&gt;Cache-Control directives&lt;/strong&gt; with ETag validation to optimize performance. Cache-Control reduces server requests by defining how long resources can be cached, while ETags verify data freshness when the cache expires.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It's the synergy between the 'how long to cache' of Cache-Control and the &amp;gt; 'has this changed' of ETag that delivers the best results in web performance." &amp;gt; &amp;gt; - Andreas Bergstrom&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Set Cache-Control &lt;code&gt;max-age&lt;/code&gt; values based on how often your resources are updated. For relatively static data, like user profiles or configuration settings, longer cache durations (e.g., 300–600 seconds) work well. For dynamic content that changes frequently, shorter cache periods or &lt;code&gt;no-cache&lt;/code&gt; directives combined with ETag validation are more suitable.&lt;/p&gt;

&lt;p&gt;A practical approach is to set a reasonable &lt;code&gt;max-age&lt;/code&gt; to minimize requests during busy periods and rely on ETag validation once the cache expires. This method balances the bandwidth savings of ETags with the reduced request load provided by time-based caching.&lt;/p&gt;

&lt;p&gt;When designing your API, consider cachability from the beginning. Structure endpoints to return data that can be easily cached, avoiding unnecessary dynamic elements that might frequently invalidate ETags. This thoughtful design can lead to noticeable performance gains across your API.&lt;/p&gt;

&lt;h3&gt;
  
  
  ETags vs. Last-Modified Headers
&lt;/h3&gt;

&lt;p&gt;Choosing the right validation mechanism is essential for effective API design. Here's a comparison of ETags and Last-Modified headers to help determine the best fit for your needs:&lt;/p&gt;

&lt;p&gt;| &lt;strong&gt;Aspect&lt;/strong&gt;                | &lt;strong&gt;ETags&lt;/strong&gt;                                 | &lt;strong&gt;Last-Modified Headers&lt;/strong&gt;               | | ------------------------- | ----------------------------------------- | --------------------------------------- | | &lt;strong&gt;Precision&lt;/strong&gt;             | Exact content-based validation            | Second-level timestamp precision        | | &lt;strong&gt;Concurrency Safety&lt;/strong&gt;    | Excellent for high-frequency updates      | Risk of lost updates with rapid changes | | &lt;strong&gt;Generation Complexity&lt;/strong&gt; | Requires hash generation                  | Uses timestamps                         | | &lt;strong&gt;Bandwidth Efficiency&lt;/strong&gt;  | Highly efficient for unchanged content    | Efficient but less precise validation   | | &lt;strong&gt;Best Use Cases&lt;/strong&gt;        | Frequent updates, critical data integrity | Infrequent changes, simple content      |&lt;/p&gt;

&lt;p&gt;Both options have minimal header overhead, but their applications differ. ETags are particularly useful for APIs requiring optimistic concurrency control. By including the ETag in the &lt;code&gt;If-Match&lt;/code&gt; header, you can ensure updates only apply to the expected version, avoiding race conditions and preserving data integrity. This is especially important for APIs managing high-frequency updates or mission-critical data.&lt;/p&gt;

&lt;p&gt;In contrast, Last-Modified headers are better suited for simpler scenarios, such as file-based resources or systems where changes are infrequent and timestamp precision is sufficient.&lt;/p&gt;

&lt;p&gt;To ensure reliable performance, thoroughly test your chosen validation method. Include scenarios where ETags match, mismatch, or are missing to confirm that your API handles all client states while maintaining data integrity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Conditional Requests and ETags with Zuplo
&lt;/h2&gt;

&lt;p&gt;Zuplo simplifies the handling of conditional requests and ETags, letting you focus on your API's business logic while it takes care of the complexities of HTTP caching.&lt;/p&gt;

&lt;h3&gt;
  
  
  Zuplo's Edge Gateway for Faster API Responses
&lt;/h3&gt;

&lt;p&gt;Zuplo's edge gateway is designed to bring API responses closer to users, improving speed and efficiency in processing conditional requests. By 2025, an estimated 75% of enterprise data is expected to originate outside centralized data centers, making edge-based optimizations increasingly critical.&lt;/p&gt;

&lt;p&gt;With its &lt;strong&gt;Cache API&lt;/strong&gt;, Zuplo supports both &lt;code&gt;ETag&lt;/code&gt; and &lt;code&gt;Last-Modified&lt;/code&gt; headers. The &lt;code&gt;cache.match()&lt;/code&gt; function automatically evaluates conditional requests. For example, when clients send requests with &lt;code&gt;If-None-Match&lt;/code&gt; headers, Zuplo’s edge gateway checks the ETags against cached content. If the content remains unchanged, the gateway responds with a &lt;strong&gt;304 Not Modified&lt;/strong&gt; status directly from the edge, skipping the need to contact the origin server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring and Debugging Conditional Requests
&lt;/h3&gt;

&lt;p&gt;Zuplo logs every request that hits your gateway. These logs integrate seamlessly with monitoring tools like &lt;a href="https://www.datadoghq.com/" rel="noopener noreferrer"&gt;DataDog&lt;/a&gt;, enabling you to set up alerts that notify you of spikes in error rates. This makes it easier to identify and address problems with ETag generation or conditional request handling.&lt;/p&gt;

&lt;p&gt;For developers, Zuplo’s dashboards offer an intuitive way to manage complex API setups. To troubleshoot issues, you can implement health check endpoints for different network configurations, ensuring that your ETag logic works consistently across all deployments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scaling with Zuplo's Features
&lt;/h3&gt;

&lt;p&gt;Zuplo’s &lt;a href="https://zuplo.com/blog/2024/07/19/what-is-gitops" rel="noopener noreferrer"&gt;&lt;strong&gt;GitOps integration&lt;/strong&gt;&lt;/a&gt; ensures that your ETag policies and conditional request settings are version-controlled across development, staging, and production environments. Meanwhile, its &lt;strong&gt;OpenAPI synchronization&lt;/strong&gt; keeps your API documentation up to date, making it easier for client developers to work with your APIs.&lt;/p&gt;

&lt;p&gt;The platform is built to support serverless environments, allowing APIs to scale efficiently at the edge without the burden of managing caching infrastructure. For APIs requiring advanced security measures - like &lt;a href="https://zuplo.com/features/api-key-management" rel="noopener noreferrer"&gt;API keys&lt;/a&gt;, JWTs, or mTLS - Zuplo ensures conditional requests work seamlessly alongside these protocols.&lt;/p&gt;

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

&lt;p&gt;Conditional requests and ETags play a key role in building efficient and scalable REST APIs. By reducing unnecessary data transfers and lowering server load, they help improve performance and deliver a smoother user experience. As Reetesh Kumar, Software Engineer, puts it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"ETags (Entity Tags) are a mechanism in the HTTP protocol used to optimize web &amp;gt; traffic and enhance data integrity by managing resource caching and &amp;gt; concurrency."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When it comes to practical implementation, frameworks like Python Flask and Node.js Express demonstrate how to integrate these optimization techniques effectively. Strong ETags offer accurate validation, and when combined with proper cache-control settings, they minimize redundant checks. For content with unpredictable changes, ETags are ideal, while Last-Modified headers are better suited for timestamp-driven updates.&lt;/p&gt;

&lt;p&gt;Taking it a step further, Zuplo's API gateway leverages edge architecture and GitOps integration to enhance these benefits. It ensures consistent ETag policies, which is essential in a landscape where over 83% of developers prioritize API quality and consistency when evaluating third-party services. Zuplo also provides monitoring tools and dynamic scaling features, meeting the demands of modern applications by delivering fast and reliable API experiences.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do ETags and conditional requests make REST APIs more efficient?
&lt;/h3&gt;

&lt;p&gt;ETags and conditional requests play a key role in making REST APIs more efficient by cutting down on unnecessary data transfers between clients and servers. An &lt;strong&gt;ETag&lt;/strong&gt; acts as a unique identifier tied to a specific version of a resource. When a client requests a resource, it can include the ETag in a conditional header, such as &lt;code&gt;If-None-Match&lt;/code&gt;, to check whether the resource has been updated.&lt;/p&gt;

&lt;p&gt;If the resource remains unchanged, the server replies with a &lt;code&gt;304 Not Modified&lt;/code&gt; status instead of re-sending the entire dataset. This approach helps conserve bandwidth, eases the server's workload, and speeds up response times. It's particularly useful for mobile users and high-traffic applications, where performance and resource optimization are crucial.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s the difference between strong and weak ETags, and when should you use them?
&lt;/h3&gt;

&lt;p&gt;When it comes to ETags, &lt;strong&gt;strong ETags&lt;/strong&gt; ensure that a resource is identical down to the last byte. This makes them ideal for situations where precise matching is a must, like validating caches for static files or assets. In contrast, &lt;strong&gt;weak ETags&lt;/strong&gt; prioritize &lt;strong&gt;semantic equivalence&lt;/strong&gt; over exact matches. They’re a better fit for resources where small changes don’t alter the overall meaning, such as dynamically generated pages.&lt;/p&gt;

&lt;p&gt;To sum it up, go with &lt;strong&gt;strong ETags&lt;/strong&gt; for cases where exact matches are essential, and opt for &lt;strong&gt;weak ETags&lt;/strong&gt; when performance takes priority over byte-level precision, especially for content that updates frequently but remains consistent in meaning.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I use ETags and conditional requests to optimize my API in Python Flask or Node.js Express?
&lt;/h3&gt;

&lt;p&gt;To implement &lt;strong&gt;ETags&lt;/strong&gt; and &lt;strong&gt;conditional requests&lt;/strong&gt; in Python Flask, you can use the &lt;code&gt;add_etag&lt;/code&gt; method to generate ETags for your responses. Combine this with &lt;code&gt;make_conditional&lt;/code&gt; to handle conditional requests. This setup allows your API to return a &lt;code&gt;304 Not Modified&lt;/code&gt; status when the resource hasn't changed, cutting down on unnecessary data transfer and boosting efficiency.&lt;/p&gt;

&lt;p&gt;In Node.js Express, ETags are automatically generated for responses. However, if you need custom ETags, you can manually set them using &lt;code&gt;res.setHeader('ETag', etag)&lt;/code&gt;. To handle conditional requests, inspect the &lt;code&gt;If-None-Match&lt;/code&gt; header in incoming requests. If the resource matches the provided ETag, return a &lt;code&gt;304&lt;/code&gt; status.&lt;/p&gt;

&lt;p&gt;Both methods help optimize API performance by reducing bandwidth usage and speeding up interactions between the client and server.&lt;/p&gt;

</description>
      <category>performance</category>
      <category>api</category>
    </item>
    <item>
      <title>API discoverability: Why its important + the risk of Shadow and Zombie APIs</title>
      <dc:creator>Adrian Machado</dc:creator>
      <pubDate>Thu, 04 Sep 2025 06:15:24 +0000</pubDate>
      <link>https://dev.to/zuplo/api-discoverability-why-its-important-the-risk-of-shadow-and-zombie-apis-16ff</link>
      <guid>https://dev.to/zuplo/api-discoverability-why-its-important-the-risk-of-shadow-and-zombie-apis-16ff</guid>
      <description>&lt;p&gt;APIs are the backbone of modern digital systems, but managing them effectively is a challenge. Here’s the key takeaway: &lt;strong&gt;hidden or unmanaged APIs - like shadow and zombie APIs - pose serious security risks and hinder productivity.&lt;/strong&gt; Without proper discoverability, businesses face vulnerabilities, inefficiency, and compliance issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Points
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API discoverability&lt;/strong&gt; ensures APIs are easy to locate, understand, and use,
improving developer efficiency and security.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shadow APIs&lt;/strong&gt; (undocumented, bypassing oversight) and &lt;strong&gt;Zombie APIs&lt;/strong&gt;
(deprecated but still active) expand attack surfaces, making businesses vulnerable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stats to know&lt;/strong&gt;:&lt;/li&gt;
&lt;li&gt;62% of companies manage 100+ APIs.&lt;/li&gt;
&lt;li&gt;36% experienced
&lt;a href="https://zuplo.com/learning-center/2025/04/02/how-to-set-up-api-security-framework" rel="noopener noreferrer"&gt;API security&lt;/a&gt; incidents last year.&lt;/li&gt;
&lt;li&gt;31% of attacks targeted shadow APIs.&lt;/li&gt;
&lt;li&gt;High-profile breaches (e.g., Meta fined €251M, Geico fined $9.75M) highlight
the risks of unmanaged APIs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Solutions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Maintain an up-to-date API inventory with automated tools.&lt;/li&gt;
&lt;li&gt;Assign clear ownership and implement lifecycle management policies.&lt;/li&gt;
&lt;li&gt;Use platforms like &lt;a href="https://zuplo.com/" rel="noopener noreferrer"&gt;Zuplo&lt;/a&gt; for centralized monitoring,
documentation, and security enforcement.&lt;/li&gt;
&lt;li&gt;Collaborate across teams and automate processes to reduce risks and streamline
API management.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Bottom line:&lt;/strong&gt; Proper API discoverability is critical for security, compliance, and operational efficiency. Without it, businesses risk costly breaches, inefficiency, and missed opportunities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Benefits of API Discoverability
&lt;/h2&gt;

&lt;p&gt;API discoverability reshapes how teams work, collaborate, and innovate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Better Developer Productivity
&lt;/h3&gt;

&lt;p&gt;When APIs are easy to find, developers can spend less time hunting for existing functionality and more time building meaningful solutions. This streamlining of development workflows prevents unnecessary duplication and allows teams to focus on delivering new features and improvements.&lt;/p&gt;

&lt;p&gt;The results speak for themselves. With a forked collection, developers complete API calls up to &lt;strong&gt;56 times faster&lt;/strong&gt;. Developers also report being &lt;strong&gt;50% more innovative&lt;/strong&gt; when equipped with user-friendly tools and processes.&lt;/p&gt;

&lt;p&gt;Take &lt;a href="https://www.paypal.com/" rel="noopener noreferrer"&gt;PayPal&lt;/a&gt;, for example. By improving API discoverability, they slashed their time-to-first-call from &lt;strong&gt;60 minutes to just one minute&lt;/strong&gt; and cut testing time from hours to mere minutes. Accessible APIs also encourage reuse across teams, ensuring consistency and eliminating redundant efforts across departments.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The best way to help developers achieve more is not by expecting more, but by &amp;gt; improving their experience." - Nicole Forsgren, Founder of DORA metrics and &amp;gt; Partner Research Manager, Microsoft&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Auto-generated API documentation further simplifies workflows by offering clear, accessible details about interfaces. With less mental effort spent deciphering unclear APIs, teams can focus on solving complex problems. These efficiencies also contribute to stronger security practices by clarifying how APIs should be used and accessed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stronger Security Posture
&lt;/h3&gt;

&lt;p&gt;API discoverability doesn’t just boost productivity - it also strengthens security by exposing hidden vulnerabilities. It’s a straightforward concept: you can’t secure what you don’t know exists. By gaining full visibility into their API landscape, organizations can close security gaps and eliminate blind spots.&lt;/p&gt;

&lt;p&gt;The stakes are high. A staggering &lt;strong&gt;92% of organizations&lt;/strong&gt; reported API-related security incidents in the past year, with &lt;strong&gt;58% identifying APIs as a security risk&lt;/strong&gt; due to their role in expanding the attack surface. Shadow and zombie APIs - those unknown or forgotten by teams - are often the culprits, creating vulnerabilities that evade security measures.&lt;/p&gt;

&lt;p&gt;Comprehensive API discovery maps out the entire API ecosystem, helping security teams identify risks, enforce governance policies, and meet compliance requirements. Considering that only &lt;strong&gt;10% of organizations fully document their APIs&lt;/strong&gt;, maintaining an accurate inventory is crucial for understanding functionality, managing permissions, and meeting regulatory standards.&lt;/p&gt;

&lt;h3&gt;
  
  
  Better Collaboration and Ecosystem Growth
&lt;/h3&gt;

&lt;p&gt;Discoverable APIs break down silos and improve collaboration, both internally and with external partners. By making APIs easier to find and understand, teams can reuse central APIs as shared resources, reducing duplication and ensuring consistent practices. This visibility also aids in managing untracked APIs, further reducing security risks.&lt;/p&gt;

&lt;p&gt;The benefits extend beyond individual organizations. For instance, &lt;a href="https://expediagroup.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;Expedia&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;generates over 90% of its revenue&lt;/strong&gt; from APIs, while &lt;a href="https://www.salesforce.com/" rel="noopener noreferrer"&gt;Salesforce&lt;/a&gt;’s &lt;a href="https://appexchange.salesforce.com/" rel="noopener noreferrer"&gt;AppExchange&lt;/a&gt; creates over &lt;strong&gt;$17 billion in revenue opportunities&lt;/strong&gt; for its partners annually.&lt;/p&gt;

&lt;p&gt;The broader market reflects this growth. The global API management market is expected to reach &lt;strong&gt;$8.36 billion by 2028&lt;/strong&gt;, with a compound annual growth rate (CAGR) of 10.9%. As APIs become more discoverable, organizations can better engage with developers, build thriving ecosystems, and drive continuous innovation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Shadow and Zombie APIs
&lt;/h2&gt;

&lt;p&gt;APIs are essential for modern businesses, but not all of them are properly managed or even accounted for. Some remain hidden or forgotten, creating serious blind spots that can compromise security and disrupt operations. Let’s dig into what shadow and zombie APIs are, and why they’re a growing concern.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Are Shadow APIs?
&lt;/h3&gt;

&lt;p&gt;Shadow APIs are essentially rogue APIs that operate outside the oversight of IT and security teams. They often emerge during fast-paced development cycles where speed takes precedence over process. These APIs bypass formal practices like authentication, rate limiting, and logging, making them invisible to API gateways and monitoring tools. The lack of documentation and oversight creates vulnerabilities that attackers can exploit. At their core, shadow APIs reflect organizational gaps - especially the absence of a robust documentation culture.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Are Zombie APIs?
&lt;/h3&gt;

&lt;p&gt;Zombie APIs, on the other hand, are leftovers from the past. These are APIs that were once active and properly managed but have since been deprecated or abandoned, yet they remain operational. Over time, as systems evolve, these APIs are forgotten, leaving behind outdated functionality that can be exploited. According to the &lt;a href="https://content.salt.security/state-api-report.html" rel="noopener noreferrer"&gt;Salt Security&lt;/a&gt; State of API Security report, zombie APIs have been the top API security concern for four consecutive surveys. Unlike shadow APIs, zombie APIs were documented at some point but are no longer actively tracked or maintained. This lack of attention results in old vulnerabilities, such as outdated SSL configurations or obsolete authentication methods.&lt;/p&gt;

&lt;p&gt;Both shadow and zombie APIs represent more than just technical oversights - they highlight deeper organizational issues, such as poor &lt;a href="https://zuplo.com/blog/2024/10/24/deprecating-rest-apis" rel="noopener noreferrer"&gt;deprecation standards&lt;/a&gt; and incomplete cleanup processes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Risks of Shadow and Zombie APIs
&lt;/h3&gt;

&lt;p&gt;The risks posed by these unmanaged APIs are substantial. Shadow APIs often skip essential security measures, such as proper authentication and encryption. Meanwhile, zombie APIs are no longer patched, leaving them riddled with outdated protocols and known vulnerabilities. Both types expand the attack surface, providing attackers with easy entry points.&lt;/p&gt;

&lt;p&gt;Recent data underscores the severity of these risks. A staggering 92% of organizations reported API-related security incidents in the past year, with 58% identifying APIs as a key security risk due to their role in expanding the attack surface. Another survey revealed that 37% of respondents experienced an API security incident in the past year, a significant jump from 17% in 2023.&lt;/p&gt;

&lt;p&gt;There’s also a compliance angle to consider. Shadow and zombie APIs can lead to violations of strict data protection regulations, particularly in industries like healthcare (&lt;a href="https://en.wikipedia.org/wiki/Health_Insurance_Portability_and_Accountability_Act" rel="noopener noreferrer"&gt;HIPAA&lt;/a&gt;) and finance (&lt;a href="https://en.wikipedia.org/wiki/Payment_Card_Industry_Data_Security_Standard" rel="noopener noreferrer"&gt;PCI DSS&lt;/a&gt;). Shadow APIs, for instance, can expose sensitive information without proper monitoring, creating risks under &lt;a href="https://en.wikipedia.org/wiki/General_Data_Protection_Regulation" rel="noopener noreferrer"&gt;GDPR&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/California_Consumer_Privacy_Act" rel="noopener noreferrer"&gt;CCPA&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;| Aspect               | Shadow APIs                                    | Zombie APIs                                    | | -------------------- | ---------------------------------------------- | ---------------------------------------------- | | &lt;strong&gt;Lifecycle Stage&lt;/strong&gt;  | Active but bypasses official processes         | Deprecated but still operational               | | &lt;strong&gt;Security Posture&lt;/strong&gt; | Lacks authentication, rate limits, and logging | Contains outdated SSL or known vulnerabilities | | &lt;strong&gt;Typical Risks&lt;/strong&gt;    | Undetected data leaks, unauthorized access     | Exploitation of old, insecure functionality    | | &lt;strong&gt;Detection&lt;/strong&gt;        | Hard to detect due to minimal logging          | Visible but requires detailed analysis         |&lt;/p&gt;

&lt;p&gt;The consequences of these vulnerabilities are costly. Data breaches linked to shadow or zombie APIs can result in millions of dollars in fines under regulations like GDPR. Operationally, zombie APIs add to technical debt and complicate monitoring, while shadow APIs create unreliable functionality outside of expected channels.&lt;/p&gt;

&lt;p&gt;The scope of this issue is massive. On average, a single business application relies on 26 to 50 APIs, and enterprises often manage over 1,000 APIs. Alarmingly, only 10% of organizations fully document their APIs, making the spread of unmanaged shadow and zombie APIs a significant threat.&lt;/p&gt;

&lt;p&gt;The real challenge lies in addressing these hidden risks. Shadow and zombie APIs aren’t easily spotted by traditional security tools, yet they dramatically increase the attack surface. Without proper oversight, they weaken an organization’s ability to enforce comprehensive API security across its digital landscape.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://zuplo.com?utm_source=dev.to&amp;amp;utm_medium=inline-cta"&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%2Fiikcuf7axhu4gkw0jmqa.png" width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategies for Better API Discoverability and Managing Hidden APIs
&lt;/h2&gt;

&lt;p&gt;Making APIs easier to find and managing hidden ones requires strong governance, supported by the right tools and processes. Here's how organizations can approach this challenge effectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Practices for API Inventory Management
&lt;/h3&gt;

&lt;p&gt;Keeping an up-to-date API inventory (ex. Using &lt;a href="https://zuplo.com/learning-center/2024/09/25/mastering-api-definitions" rel="noopener noreferrer"&gt;API definitions&lt;/a&gt; like OpenAPI) is the cornerstone of discoverability. But it's not just about having a list - it's about creating a structured system to document, track, and manage APIs throughout their lifecycle.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automate the process&lt;/strong&gt;: Use discovery tools that scan API traffic and crawl
systems to detect APIs automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assign ownership&lt;/strong&gt;: Designate clear API owners to ensure documentation stays
accurate and security measures evolve as needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standardize documentation&lt;/strong&gt;: Use templates to clearly outline an API’s
purpose, version, security requirements, dependencies, and lifecycle stage. This applies to APIs built with REST, GraphQL, and anything else you expose externally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrate with CI/CD pipelines&lt;/strong&gt;: Automatically log API changes during
development and deployment to keep the inventory current.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a searchable catalog&lt;/strong&gt;: Organize APIs by functionality, technology,
or business domain to make them easy to discover and encourage reuse.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These practices help establish centralized platforms that improve API visibility and governance.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Zuplo Supports API Discoverability
&lt;/h3&gt;

&lt;p&gt;Zuplo offers tools that align with these best practices, providing a comprehensive approach to API management. Its &lt;a href="https://zuplo.com/features/programmable" rel="noopener noreferrer"&gt;programmable API gateway&lt;/a&gt; acts as a central hub, monitoring all requests and making it harder for shadow APIs to bypass security controls. You can use the built-in analytics to track usage of individual endpoints, so you can deprecate dead ones before they become zombies.&lt;/p&gt;

&lt;p&gt;Zuplo is &lt;a href="https://www.openapis.org/" rel="noopener noreferrer"&gt;OpenAPI&lt;/a&gt;-native which ensures that documentation stays in sync with actual implementations, reducing the risk of shadow and zombie APIs. Developers benefit from a portal that provides detailed documentation, usage examples, and testing tools, which helps eliminate duplicate or undocumented APIs. Zuplo's API portal is great for cataloging all of your APIs, and is open-sourced as &lt;a href="https://zudoku.dev" rel="noopener noreferrer"&gt;&lt;strong&gt;Zudoku&lt;/strong&gt;&lt;/a&gt;. Check it out:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/zRHBsuXSAWY"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Zuplo also integrates with &lt;a href="https://zuplo.com/learning-center/2023/11/09/time-for-gitops-to-come-to-apis" rel="noopener noreferrer"&gt;GitOps&lt;/a&gt; to enforce version control and review processes for API changes, maintaining a full audit trail and preventing ad-hoc deployments. Features like advanced authentication and rate limiting further enhance security by identifying and controlling unauthorized API usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools for Monitoring and Governance
&lt;/h3&gt;

&lt;p&gt;Strong &lt;a href="https://zuplo.com/learning-center/2025/07/14/what-is-api-governance-and-why-is-it-important" rel="noopener noreferrer"&gt;API governance&lt;/a&gt; depends on a combination of monitoring tools and well-defined policies to maintain oversight.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Endpoint detection&lt;/strong&gt;: Use tools to flag unauthorized activity and enforce
lifecycle policies to keep ungoverned APIs in check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sunset policies&lt;/strong&gt;: Define clear workflows for API deprecation and deletion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attack surface mapping&lt;/strong&gt;: Regularly assess the API ecosystem to identify
endpoints that may have slipped through monitoring processes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service meshes&lt;/strong&gt;: Gain detailed insights into API communications, especially
in distributed systems and microservices architectures where API sprawl can become a problem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure monitoring&lt;/strong&gt;: Track historical usage trends and set up
automated alerts to catch unusual behavior early.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Best Practices for Maintaining a Healthy API Ecosystem
&lt;/h2&gt;

&lt;p&gt;Managing APIs effectively requires ongoing effort and strategic planning. With API attacks surging by over 400% and zombie APIs becoming prime targets, it's clear that a well-maintained API ecosystem is more important than ever. Below are actionable strategies to help ensure your APIs remain secure and resilient.&lt;/p&gt;

&lt;h3&gt;
  
  
  Continuous API Lifecycle Management
&lt;/h3&gt;

&lt;p&gt;Proper lifecycle management is key to keeping APIs secure and up-to-date. This involves creating formal policies with scheduled reviews and clear deprecation timelines to avoid APIs lingering indefinitely.&lt;/p&gt;

&lt;p&gt;One effective approach is introducing a &lt;a href="https://zuplo.com/learning-center/2025/08/17/how-to-sunset-an-api" rel="noopener noreferrer"&gt;&lt;strong&gt;formal sunset policy&lt;/strong&gt;&lt;/a&gt; that includes well-defined deprecation and deletion workflows. Regular audits and compliance checks ensure all active endpoints meet current security and regulatory standards. Publishing quarterly reports on retired APIs can also provide transparency, detailing why specific endpoints were deprecated and confirming their removal.&lt;/p&gt;

&lt;p&gt;Consider these real-world examples: &lt;a href="https://www.slhn.org/" rel="noopener noreferrer"&gt;St. Luke's Health System&lt;/a&gt; suffered a breach exposing 450,000 patient records because an outdated SOAP API remained active. The vulnerability had been patched in newer services, but the deprecated API went unnoticed for six months, leading to regulatory fines and reputational harm. Similarly, a major US retailer experienced a breach affecting 14 million credit card records after an old checkout API was left active post-migration. The four-month delay in detecting the issue resulted in multimillion-dollar losses and a blow to public trust.&lt;/p&gt;

&lt;h3&gt;
  
  
  Promoting Cross-Team Collaboration
&lt;/h3&gt;

&lt;p&gt;Breaking down silos between development, security, and operations teams is essential to prevent shadow APIs and reduce the lifespan of zombie APIs. When teams operate in isolation, governance policies are harder to enforce, and oversight weakens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-functional collaboration&lt;/strong&gt; ensures that governance practices are aligned with organizational goals and consistently applied. Clear communication protocols and tools that support both real-time and asynchronous communication are crucial.&lt;/p&gt;

&lt;p&gt;Regular meetings, workshops, and training sessions can further enhance collaboration, offering opportunities to align on governance standards and address challenges. Providing targeted training on API lifecycle management, security, and compliance ensures everyone understands their role in maintaining API health.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Automation for API Maintenance
&lt;/h3&gt;

&lt;p&gt;While collaboration sets the foundation for API health, automation takes it to the next level. Managing APIs manually becomes impractical at scale, but automation can streamline processes, reduce risks, and free up developers to focus on innovation.&lt;/p&gt;

&lt;p&gt;Incorporating API discovery and security scanning into your &lt;a href="https://zuplo.com/docs/articles/custom-ci-cd" rel="noopener noreferrer"&gt;CI/CD pipeline&lt;/a&gt; helps catch issues before they reach production. Automated cataloging can identify active APIs, including undocumented ones, while continuous monitoring with risk scoring detects unusual access patterns or suspicious behavior. A great tool for analyzing API security across your entire API is &lt;a href="https://ratemyopenapi.com" rel="noopener noreferrer"&gt;&lt;strong&gt;RateMyOpenAPI&lt;/strong&gt;&lt;/a&gt; which scans your API for security inconsistencies and vulnerabilities, as well as APIs that don't conform to standards.&lt;/p&gt;

&lt;p&gt;The speed advantage is undeniable. AI-powered tools allow developers to ship code many times faster, making robust API security solutions a necessity. Proactive API discovery can operationalize systems in as little as 15 minutes.&lt;/p&gt;

&lt;p&gt;Automation should cover the entire API lifecycle - from design and deployment to testing, publishing, and consumption. Automated testing ensures APIs meet their specifications by verifying functionality, efficiency, compatibility, and security. Additionally, automated security processes can identify vulnerabilities, classify sensitive data, and establish baselines for normal behavior.&lt;/p&gt;

&lt;p&gt;Assigning clear ownership for each API enhances accountability, while automated tools help maintain independent test cases and minimize dependencies. Regular monitoring ensures that automation efforts are effective and highlights areas needing improvement. This is especially critical given that 92% of organizations reported experiencing an API-related security incident in the past year - yet only 10% fully document their APIs. With the average cost of an API-related breach exceeding $4 million, the stakes couldn't be higher.&lt;/p&gt;

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

&lt;p&gt;API discoverability isn’t just a technical feature - it’s a critical business necessity that directly influences security, efficiency, and overall success. With API-related attacks on the rise and the cost of a single API security breach averaging $6.1 million - projected to nearly double by 2030 - this is a risk no organization can afford to ignore. Currently, these breaches affect 60% of organizations and contribute to annual losses estimated at a staggering $75 billion. The rapid growth of APIs, coupled with insufficient governance, has created a dangerous gap that businesses must urgently address.&lt;/p&gt;

&lt;p&gt;When APIs are properly cataloged, documented, and managed, they become a powerful asset rather than a liability. Organizations that prioritize discoverability can boost developer efficiency, strengthen security measures, and foster better collaboration across teams. This is why 93% of organizations acknowledge APIs as essential to their operations. Visibility is the cornerstone of control, enabling organizations to mitigate risks while unlocking opportunities.&lt;/p&gt;

&lt;p&gt;Unmanaged APIs, such as shadow and zombie APIs, pose serious threats. Shadow APIs are undocumented and bypass security protocols, while zombie APIs are outdated endpoints that can serve as entry points for attackers. Tackling these vulnerabilities requires a robust strategy that includes comprehensive lifecycle management, teamwork across departments, and scalable automation tailored to evolving API ecosystems.&lt;/p&gt;

&lt;p&gt;Modern solutions, like Zuplo, are designed to meet these challenges head-on. These platforms provide centralized management, precise access control, automated versioning, and seamless OpenAPI synchronization. Tom Carden, Head of Engineering at &lt;a href="https://www.rewiringamerica.org/about-us" rel="noopener noreferrer"&gt;Rewiring America&lt;/a&gt;, highlights the benefits:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Zuplo is the ultimate one-stop shop for all your API needs. With rate &amp;gt; limiting, &lt;a href="https://zuplo.com/features/api-key-management" rel="noopener noreferrer"&gt;API key management&lt;/a&gt;, &amp;gt; and documentation hosting, it saved us weeks of engineering time and let us &amp;gt; focus on solving problems unique to our mission."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To stay ahead, organizations must adopt proactive governance and continuous monitoring while leveraging the right tools to maintain visibility across their API landscape. This approach not only ensures security but also paves the way for ongoing innovation and sustainable scaling. Businesses that invest in API discoverability today are positioning themselves to thrive in an increasingly API-driven world.&lt;/p&gt;

&lt;p&gt;The health of your API ecosystem directly impacts your ability to deliver value securely and efficiently. By prioritizing discoverability, addressing shadow and zombie APIs, and committing to strong governance, you’ll set the stage for secure growth and long-term success.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What’s the difference between shadow APIs and zombie APIs, and why are they a security concern?
&lt;/h3&gt;

&lt;p&gt;Shadow APIs refer to &lt;strong&gt;undocumented APIs&lt;/strong&gt; that are created without proper approval or oversight. Since they aren't officially tracked, they often lack the necessary security measures, leaving them open to potential exploitation. Meanwhile, zombie APIs are &lt;strong&gt;outdated or abandoned APIs&lt;/strong&gt; that remain accessible but are no longer actively maintained. These neglected APIs can become easy prey for attackers, as they often contain unpatched vulnerabilities.&lt;/p&gt;

&lt;p&gt;Both shadow and zombie APIs expand your system's &lt;strong&gt;attack surface&lt;/strong&gt;, exposing sensitive information and introducing compliance risks. Because they're frequently overlooked, they can lead to severe security breaches if not addressed. To counter these risks, it's crucial to prioritize API visibility and conduct regular audits to keep your systems secure.&lt;/p&gt;

&lt;h3&gt;
  
  
  How can organizations keep their API inventory up-to-date to improve discoverability and reduce risks from unmanaged APIs?
&lt;/h3&gt;

&lt;p&gt;To keep your API inventory accurate and current, the key lies in &lt;strong&gt;automation and consistent updates&lt;/strong&gt;. Tools like runtime monitoring and OpenAPI specifications can simplify automated API discovery and documentation. Make it a habit to review and organize your APIs regularly, keeping track of dependencies and usage to maintain transparency and relevance.&lt;/p&gt;

&lt;p&gt;Steer clear of static tools like spreadsheets - they become outdated too fast. Instead, opt for &lt;a href="https://zuplo.com/blog/2021/09/01/a-new-hope-for-api-management" rel="noopener noreferrer"&gt;dynamic API management platforms&lt;/a&gt; that offer real-time updates and actionable insights. Taking this proactive approach not only boosts API discoverability and operational efficiency but also reduces risks, such as security vulnerabilities or compliance gaps, tied to unmanaged APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does API discoverability boost developer productivity and improve team collaboration?
&lt;/h3&gt;

&lt;p&gt;API discoverability is crucial for helping developers work smarter and faster. When APIs are easy to find and integrate, developers can save time and avoid the headaches of tracking down hidden or poorly documented APIs.&lt;/p&gt;

&lt;p&gt;It also encourages smoother teamwork. A well-organized and transparent API ecosystem ensures that everyone on the team has access to the same resources. This not only helps prevent redundant work but also promotes consistency across projects, making collaboration more seamless.&lt;/p&gt;

</description>
      <category>security</category>
      <category>api</category>
    </item>
    <item>
      <title>Troubleshooting Broken Function Level Authorization</title>
      <dc:creator>Adrian Machado</dc:creator>
      <pubDate>Thu, 04 Sep 2025 06:15:08 +0000</pubDate>
      <link>https://dev.to/zuplo/troubleshooting-broken-function-level-authorization-3d9p</link>
      <guid>https://dev.to/zuplo/troubleshooting-broken-function-level-authorization-3d9p</guid>
      <description>&lt;p&gt;&lt;strong&gt;APIs are under attack, and Broken Function Level Authorization (BFLA) is a major culprit.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;BFLA happens when APIs fail to enforce proper permission checks, letting users access restricted functions. It ranks #5 on the &lt;a href="https://owasp.org/" rel="noopener noreferrer"&gt;OWASP&lt;/a&gt; API Top 10 (2023) and has led to breaches at companies like &lt;a href="https://www.uber.com/us/en/about/" rel="noopener noreferrer"&gt;Uber&lt;/a&gt;, Instagram, and &lt;a href="https://github.com/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s what you need to know upfront:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What is BFLA?&lt;/strong&gt; It allows attackers to exploit API functions (not just
individual objects) to bypass permissions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why does it happen?&lt;/strong&gt; Common causes include misconfigured roles,
over-reliance on client-side controls, and flawed &lt;a href="https://zuplo.com/blog/2024/12/16/api-gateway-hosting-options" rel="noopener noreferrer"&gt;API gateway setups&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How to fix it?&lt;/strong&gt; Use tools like &lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt; and
&lt;a href="https://www.zaproxy.org/" rel="noopener noreferrer"&gt;OWASP ZAP&lt;/a&gt; to test APIs, enforce server-side authorization, and adopt least privilege access.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key takeaway:&lt;/strong&gt; APIs need robust, server-side authorization checks at every function level to prevent BFLA.&lt;/p&gt;

&lt;p&gt;Read on for detailed examples, testing techniques, and long-term strategies to secure your APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Broken Function Level Authorization?
&lt;/h2&gt;

&lt;p&gt;Broken Function Level Authorization (BFLA) is a security flaw that occurs when APIs fail to enforce proper permission checks, allowing users to perform actions they shouldn't have access to. Unlike &lt;a href="https://zuplo.com/learning-center/2025/07/27/troubleshooting-broken-object-level-authorization" rel="noopener noreferrer"&gt;object-level issues&lt;/a&gt; that target specific API objects, BFLA focuses on entire API functions. Imagine a scenario where someone with a visitor badge can stroll into the CEO's office and access confidential files - this is essentially what happens when BFLA vulnerabilities exist. The system doesn't properly verify whether the user has the right level of access.&lt;/p&gt;

&lt;p&gt;BFLA is listed as API5 in the &lt;a href="https://zuplo.com/learning-center/2025/05/07/OWASP-Cheat-Sheet-Guide" rel="noopener noreferrer"&gt;OWASP API Security Top 10&lt;/a&gt;, ranking fifth in severity as of 2023. This vulnerability is particularly dangerous because it allows attackers to exploit legitimate API calls to access restricted resources, bypassing standard user permissions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Complex &amp;gt; &lt;a href="https://zuplo.com/docs/policies/acl-policy-inbound" rel="noopener noreferrer"&gt;access control policies&lt;/a&gt; &amp;gt; with different hierarchies, groups, and roles, and an unclear separation &amp;gt; between administrative and regular functions, tend to lead to authorization &amp;gt; flaws. By exploiting these issues, attackers gain access to other users' &amp;gt; resources and/or administrative functions." - OWASP API Security Top 10 2019 &amp;gt; Report&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;BFLA can be thought of as a broader version of Broken Object Level Authorization (BOLA). While BOLA focuses on individual API objects, BFLA targets overarching API functions, making its potential impact even greater. APIs are particularly vulnerable because their structured nature often makes it easier for attackers to identify and exploit flaws.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Identify BFLA
&lt;/h3&gt;

&lt;p&gt;Spotting BFLA vulnerabilities requires a keen eye for unusual API behavior. One of the clearest signs is when users can access functions or endpoints that should be restricted based on their role or permission level.&lt;/p&gt;

&lt;p&gt;Some common red flags include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users accessing administrative endpoints or functions meant for higher
privilege levels.&lt;/li&gt;
&lt;li&gt;Bypassing role restrictions by changing HTTP methods, like switching from
&lt;code&gt;GET&lt;/code&gt; to &lt;code&gt;POST&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Successful API calls to endpoints that should require elevated permissions.&lt;/li&gt;
&lt;li&gt;Unauthorized actions, such as creating, modifying, or deleting resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Attackers often reverse engineer client-side code or intercept application traffic to uncover these vulnerabilities. For example, during security testing, a simple change in an HTTP method or endpoint parameter might reveal unauthorized access to sensitive functions. This happens when APIs rely too heavily on client-side controls or lack robust server-side authorization checks. Recognizing these warning signs is critical because attackers frequently exploit these flaws in real-world scenarios.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Attack Examples
&lt;/h3&gt;

&lt;p&gt;Take the example of an Invitation Hijack Attack. In this scenario, an application that requires an invitation to join uses the following API call to retrieve invitation details:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /api/invites/{invite_guid}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An attacker, however, manipulates the request by changing the method to &lt;code&gt;POST&lt;/code&gt; and targeting a different endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /api/invites/new
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This endpoint, meant only for administrators, allows the attacker to send a payload 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;POST /api/invites/new
{ "email": "attacker@somehost.com", "role": "admin" }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If proper authorization checks are missing, the attacker can create an administrative invitation for themselves, effectively taking over the application.&lt;/p&gt;

&lt;p&gt;Real-world examples highlight how damaging BFLA can be. For instance, breaches at a state insurance department and a major telecommunications provider involved attackers exploiting these vulnerabilities. In another case, &lt;a href="https://newrelic.com/" rel="noopener noreferrer"&gt;New Relic&lt;/a&gt; Synthetics faced a privilege escalation issue where restricted users could modify alerts on monitors without proper permissions.&lt;/p&gt;

&lt;p&gt;These types of attacks pose serious risks to businesses, including data exposure, data loss, corruption, and service interruptions. These examples emphasize just how critical it is to perform rigorous testing to identify and address BFLA vulnerabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Function Level Authorization Breaks
&lt;/h2&gt;

&lt;p&gt;To secure APIs effectively, it’s crucial to understand why function level authorization often fails. These failures usually arise from poor design and flawed implementations. According to OWASP's 2021 findings, 94% of applications were tested for some form of broken access control, with a 3.81% incidence rate for such issues. This places broken access control as the &lt;strong&gt;#1 application security risk&lt;/strong&gt;. Let’s dive into the specific misconfigurations that weaken function level authorization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Role Permission Setup Errors
&lt;/h3&gt;

&lt;p&gt;Errors in Role-Based Access Control (RBAC) are a frequent cause of function level authorization vulnerabilities. These issues arise when roles are misconfigured, hierarchies are poorly designed, or access to specific functions is not properly restricted. Missteps like overly broad permissions or inconsistent role structures can result in users gaining more access than intended, leaving critical functions exposed.&lt;/p&gt;

&lt;p&gt;Real-world examples highlight the consequences of these errors. For instance, in 2022, GitHub faced a privilege escalation bug that allowed users to gain unauthorized access to higher-level repository functions. A common culprit is the failure to follow the principle of least privilege - starting users with minimal permissions and granting additional access only when necessary. Overlooking this principle creates exploitable gaps in backend functions or APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trusting Client-Side Controls
&lt;/h3&gt;

&lt;p&gt;Another major vulnerability lies in misplaced trust in client-side controls. Relying on mechanisms like JavaScript validation, hidden form fields, or disabled buttons for enforcing access controls is a risky practice. These client-side methods can be easily bypassed by users, as everything on the client side is under their control.&lt;/p&gt;

&lt;p&gt;Access control decisions must always be enforced on the server, where they cannot be tampered with. CWE-602 specifically warns against delegating security enforcement to the client. While client-side validation can help catch simple errors and provide immediate feedback, it should only complement server-side controls, never replace them.&lt;/p&gt;

&lt;h3&gt;
  
  
  API Gateway Configuration Problems
&lt;/h3&gt;

&lt;p&gt;Misconfigured API gateways are another common source of function level authorization weaknesses. According to OWASP, security misconfiguration ranks as the fifth most common API vulnerability risk. Problems like default settings, insufficient CORS protection, missing authentication, and exposed admin APIs can all lead to unauthorized access.&lt;/p&gt;

&lt;p&gt;The impact of these misconfigurations can be devastating. For example, in late 2022, &lt;a href="https://www.t-mobile.com/" rel="noopener noreferrer"&gt;T-Mobile&lt;/a&gt; suffered a data breach that exposed the personal information of 37 million customers due to a misconfigured API with inadequate authorization settings. Around the same time, &lt;a href="https://www.optus.com.au/" rel="noopener noreferrer"&gt;Optus&lt;/a&gt; experienced a breach affecting 10 million customer accounts because an API endpoint didn’t require credentials.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"In the instance where a public API endpoint did not require authentication, &amp;gt; anyone on the internet with knowledge of that endpoint URL could use it." &amp;gt; &amp;gt; - Corey J Ball, Senior Manager of Cyber Security Consulting for Moss Adams&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Common gateway misconfigurations include improper CORS settings, excessive access permissions, lack of rate limiting, and missing request validation. Exposed admin APIs and absent firewall rules further widen the attack surface. Modern API gateway setups are often complex, and when multiple teams manage different components, inconsistent policies and overlooked security settings can create persistent vulnerabilities for attackers to exploit.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Find and Fix Authorization Problems
&lt;/h2&gt;

&lt;p&gt;Once you've identified potential BFLA (Broken Function Level Authorization) vulnerabilities, the next step is tackling authorization weaknesses head-on. This requires a blend of technical tools and a hacker's mindset - thinking about how an attacker might exploit your system to access functions meant for higher-privilege users. The trick? Simulate real-world attack methods and test thoroughly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using &lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt; and &lt;a href="https://www.zaproxy.org/" rel="noopener noreferrer"&gt;OWASP ZAP&lt;/a&gt; for Testing
&lt;/h3&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%2Fio8uf59ld73x2tzbtwbn.jpg" 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%2Fio8uf59ld73x2tzbtwbn.jpg" alt="Postman"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To uncover authorization flaws, leverage powerful tools like &lt;strong&gt;Postman&lt;/strong&gt; and &lt;strong&gt;OWASP ZAP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Postman is a great starting point for manual testing. Begin by documenting your API endpoints and testing them with tokens from different user roles. Specifically, capture requests made by privileged users and replay them using lower-privilege tokens. This mirrors how attackers might attempt to access restricted functionality that isn’t visible in the user interface.&lt;/p&gt;

&lt;p&gt;For automated testing, &lt;strong&gt;OWASP ZAP&lt;/strong&gt; steps in with advanced features. Its active scanner can test combinations of headers and tokens, helping identify endpoints that bypass proper authorization checks. It’s particularly effective at uncovering endpoints vulnerable to unauthorized access.&lt;/p&gt;

&lt;p&gt;Take, for example, a 2018 case where cyber researcher Jon Bottarini found a flaw in New Relic Synthetics. He discovered that a restricted user could modify alerts on monitors without proper permissions. Using &lt;a href="https://portswigger.net/burp" rel="noopener noreferrer"&gt;Portswigger Burp Suite&lt;/a&gt;, he intercepted privileged session traffic and manipulated API requests to expose hidden vulnerabilities. This highlights how intercepting and replaying requests can shine a light on authorization issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Checking Tokens and Server Logs
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;JWT (JSON Web Token) analysis&lt;/strong&gt; is another crucial step in authorization testing. Decode the token payload and verify that its claims accurately reflect the user's role and permissions. Pay close attention to fields like &lt;code&gt;aud&lt;/code&gt; (audience), scopes, and any custom permission claims.&lt;/p&gt;

&lt;p&gt;A common problem arises when tokens are formatted correctly but carry incorrect permissions for the requested function. Improper scope verification is often at the root of such issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server logs&lt;/strong&gt; can also uncover patterns that automated tools might miss. Look for unusual activity, such as non-admin users attempting to access admin endpoints or users performing actions outside their normal behavior. Standardizing log formats with key-value pairs makes analysis easier. Ensure logs capture essential details like user IDs, event categories, outcomes, and IP addresses.&lt;/p&gt;

&lt;p&gt;In 2020, &lt;a href="https://www.datadoghq.com/" rel="noopener noreferrer"&gt;Datadog&lt;/a&gt; emphasized the importance of monitoring authentication logs to identify security threats. They suggested tracking failed login attempts from a single user within short timeframes to detect brute force attacks, as well as monitoring logins from multiple user IDs originating from the same IP address to catch credential stuffing attempts.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"An API log is a comprehensive record generated by an API that documents all &amp;gt; the requests sent to and responses received from the API." - Daniel Olaogun, &amp;gt; &lt;a class="mentioned-user" href="https://dev.to/merge"&gt;@merge&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Tools like Datadog or the &lt;a href="https://www.elastic.co/elastic-stack" rel="noopener noreferrer"&gt;ELK Stack&lt;/a&gt; are invaluable for collecting and analyzing API logs. Establishing baselines for typical HTTP access patterns - both per endpoint and per user - allows you to spot deviations that might signal an authorization bypass. From there, test edge scenarios to further challenge your API's defenses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Edge Cases
&lt;/h3&gt;

&lt;p&gt;Edge case testing is where you’ll often find the hidden cracks in your authorization logic. These scenarios explore how your API behaves under rare or unexpected conditions, which is often where vulnerabilities lurk.&lt;/p&gt;

&lt;p&gt;Start by testing token lifecycle scenarios, such as expired tokens, revoked permissions, or modified claims. For multi-tenant systems, try using a valid token from one tenant to access resources in another. Also, test boundary values at the edges of your role hierarchy or system ranges.&lt;/p&gt;

&lt;p&gt;Other edge case tests include using corrupted tokens or omitting essential headers to ensure your system fails securely. For example, entering special characters in user roles can disrupt authorization logic if the system doesn’t handle these inputs properly. Similarly, feeding the system extreme input values - like unusually large user IDs - can reveal flaws or even crash the system if validation is inadequate.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Fix API Authorization Issues
&lt;/h2&gt;

&lt;p&gt;Once you've identified vulnerabilities using tools like Postman, OWASP ZAP, and log reviews, the next step is addressing these issues. Rather than treating security as an afterthought, it's essential to integrate authorization directly into the API design and deployment process. Here's how to do it effectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Function-Level Rules with &lt;a href="https://www.openapis.org/" rel="noopener noreferrer"&gt;OpenAPI&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;OpenAPI specifications are a powerful way to define and enforce authorization rules at the function level. By embedding security directly into your API documentation, you create a single source of truth that both developers and security tools can rely on.&lt;/p&gt;

&lt;p&gt;To start, define your security schemes in the &lt;code&gt;components/securitySchemes&lt;/code&gt; section of your OpenAPI document. OpenAPI supports several types of authentication and authorization schemes, including HTTP, &lt;code&gt;apiKey&lt;/code&gt;, &lt;code&gt;oauth2&lt;/code&gt;, and &lt;code&gt;openIdConnect&lt;/code&gt;, each with its specific properties.&lt;/p&gt;

&lt;p&gt;After defining the security schemes, apply them using the &lt;code&gt;security&lt;/code&gt; keyword. You can do this at the root level to cover the entire API or at the operation level for more granular control. This setup allows you to safeguard sensitive functions while keeping public endpoints accessible.&lt;/p&gt;

&lt;p&gt;For OAuth 2 and OpenID Connect, scope-based permissions offer a detailed way to manage access. This ensures that only users with the correct privileges can perform administrative tasks, aligning with best practices for integrating security into OpenAPI.&lt;/p&gt;

&lt;p&gt;The real strength of OpenAPI lies in its ability to combine multiple authentication types using logical OR and AND operations in the security section. This flexibility supports different client types while maintaining strict authorization controls, helping to prevent Broken Function Level Authorization (BFLA) vulnerabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing Between RBAC and ABAC
&lt;/h3&gt;

&lt;p&gt;Once you've defined your authorization rules, the next step is selecting the right access control model. The choice between Role-Based Access Control (RBAC) and Attribute-Based Access Control (ABAC) significantly impacts both security and operational complexity.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RBAC&lt;/strong&gt;: Assigns permissions based on predefined roles. It's straightforward
to set up and works well for small to medium-sized organizations with clear hierarchies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ABAC&lt;/strong&gt;: Provides finer control by using attributes like user roles,
location, or time of access. While more complex to configure initially, it scales better for larger organizations with more nuanced access requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Smaller organizations often start with RBAC due to its simplicity. However, as companies grow and require more granular access controls, maintaining RBAC can become cumbersome. ABAC, on the other hand, offers the flexibility needed for diverse and evolving scenarios.&lt;/p&gt;

&lt;p&gt;A hybrid approach can be particularly effective. Use RBAC for broad user categories and ABAC for more sensitive operations that require contextual decision-making. This combination creates robust authorization controls that help prevent BFLA vulnerabilities.&lt;/p&gt;

&lt;h4&gt;
  
  
  Implementing RBAC
&lt;/h4&gt;

&lt;p&gt;Here's a tutorial on how to implement RBAC on your API using Zuplo. There's also a &lt;a href="https://zuplo.com/learning-center/2025/01/28/how-rbac-improves-api-permission-management" rel="noopener noreferrer"&gt;written version&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/Xo02a8rzQKc"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Authorization Tests to CI/CD
&lt;/h3&gt;

&lt;p&gt;Integrating authorization testing into your &lt;a href="https://zuplo.com/docs/articles/custom-ci-cd" rel="noopener noreferrer"&gt;CI/CD pipeline&lt;/a&gt; ensures that vulnerabilities are caught before they reach production. This proactive approach addresses issues when they are cheaper and easier to fix.&lt;/p&gt;

&lt;p&gt;Despite its importance, many organizations lag in this area. For example, a 2024 &lt;a href="https://about.gitlab.com/" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt; survey revealed that only 29% of companies fully integrate security into their DevOps processes. Meanwhile, &lt;a href="https://www.ibm.com/" rel="noopener noreferrer"&gt;IBM&lt;/a&gt;'s 2023 report highlighted that the average cost of a breach has climbed to $4.88 million.&lt;/p&gt;

&lt;p&gt;To avoid these risks, make security a continuous effort. Use tools like &lt;a href="https://support.postman.com/hc/en-us/articles/115003703325-How-to-install-Newman" rel="noopener noreferrer"&gt;Newman&lt;/a&gt; (to run Postman collections) or OWASP ZAP for automated scanning to verify that your authorization rules work as intended across various user roles and scenarios.&lt;/p&gt;

&lt;p&gt;Set clear thresholds for blocking builds when critical authorization vulnerabilities are detected. Lower-severity issues can pass with alerts, but critical flaws should halt deployment. Treat authorization policies as first-class code by implementing unit and integration tests, and maintain detailed logs of every authorization decision. This approach not only catches vulnerabilities early but also lays the groundwork for long-term security, reducing the risk of BFLA attacks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://zuplo.com?utm_source=dev.to&amp;amp;utm_medium=inline-cta"&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%2Fiikcuf7axhu4gkw0jmqa.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Long-Term Authorization Security
&lt;/h2&gt;

&lt;p&gt;Strengthening API security for the long haul requires more than quick fixes. It demands a strategic approach that adapts to your organization’s needs and the ever-evolving threat landscape. The idea is to weave security practices into your development process, making them a natural part of your workflow rather than an afterthought.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Least Privilege Access
&lt;/h3&gt;

&lt;p&gt;The principle of least privilege is a cornerstone of effective authorization systems. By limiting access rights to only what’s necessary, you significantly reduce the potential attack surface. This isn’t just theory - &lt;strong&gt;removing local admin rights and controlling execution can mitigate 75% of Microsoft’s critical vulnerabilities&lt;/strong&gt;. That’s a statistic you can’t afford to ignore.&lt;/p&gt;

&lt;p&gt;Start by conducting a privilege audit to identify unused accounts, shadow admin credentials, and outdated permissions. Transition all users to standard privileges by default, granting elevated access only when absolutely necessary. For high-risk API functions, implement time-bound privileges - temporary permissions granted for specific tasks. This approach ensures that administrative access isn’t left open indefinitely.&lt;/p&gt;

&lt;p&gt;Hardcoded credentials should be replaced with API-based authentication systems that can be monitored and revoked instantly. This not only improves security but also provides better control over who has access to what, and when.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Authorization issues are typically difficult to detect in an automated &amp;gt; fashion. The structure of the codebase should be set up in a way that it is &amp;gt; difficult to make authorization errors on specific endpoints. To achieve this, &amp;gt; authorization measures should be implemented as far up the stack as possible. &amp;gt; Potentially at a class level, or using middleware." – Hakluke and Farah Hawa&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As organizations scale, managing access becomes exponentially complex, especially with machine identities growing at twice the rate of human identities. This makes implementing and maintaining least privilege access a critical step in long-term security planning.&lt;/p&gt;

&lt;h3&gt;
  
  
  Managing Policies with GitOps
&lt;/h3&gt;

&lt;p&gt;GitOps offers a systematic way to manage authorization policies by treating them as code stored in Git repositories. This approach provides a single source of truth, enabling version control, automated deployments, and quick rollbacks when needed.&lt;/p&gt;

&lt;p&gt;The benefits of GitOps shine during crises. For instance, when &lt;a href="https://github.com/weaveworks" rel="noopener noreferrer"&gt;Weaveworks&lt;/a&gt; faced a system outage caused by a risky change, they restored their entire system - including clusters, applications, and monitoring tools - in just 40 minutes, thanks to their Git-based configuration files.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"GitOps is a set of best practices encompassing using Git repositories as the &amp;gt; single source of truth to deliver infrastructure as code." – Hossein Ashtari&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To ensure security, use pull requests for all changes to API access controls. This creates an audit trail and allows for automated reviews. Role-based access control can also be integrated into your GitOps workflow, defining who has permission to make changes to specific parts of your API configuration.&lt;/p&gt;

&lt;p&gt;Even small adjustments to permissions should go through feature branches, ensuring proper review and testing before deployment. Automated testing for API configurations can catch errors early, preventing them from becoming vulnerabilities.&lt;/p&gt;

&lt;p&gt;By separating API code from configuration releases, GitOps allows for faster updates and bug fixes while maintaining strict security oversight. If something goes wrong, you can quickly roll back changes without disrupting the application itself. This method also integrates seamlessly with CI/CD pipelines, reinforcing security at every stage.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"With GitOps, you can implement continuous deployment from any environment &amp;gt; without having to switch tools. It's self-documenting, as changes are all &amp;gt; recorded in the repo." – Cerbos&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Regular policy reviews complement this automated approach, ensuring that your security measures remain effective over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Regular Permission Reviews
&lt;/h3&gt;

&lt;p&gt;Even the most well-designed authorization systems can drift without ongoing maintenance. Human error and stolen credentials remain leading causes of security breaches. Regular permission reviews are essential to prevent privilege creep and ensure that access rights align with current needs.&lt;/p&gt;

&lt;p&gt;For most organizations, quarterly reviews are sufficient, but environments with higher security demands may require monthly audits. These reviews should examine not just user permissions but also API calls and the privileges granted to automated systems.&lt;/p&gt;

&lt;p&gt;Key areas to focus on include removing access for former employees, revoking temporary privileges that have outlived their purpose, and ensuring that current employees don’t retain permissions from previous roles. &lt;strong&gt;In 2024, 61% of organizations reported cloud security issues&lt;/strong&gt;, underscoring the importance of thorough permission reviews.&lt;/p&gt;

&lt;p&gt;Involve multiple stakeholders in the process - not just the security team. Employees and managers who understand the business context behind access requirements can provide valuable insights. Document every step of the review process to create a record that supports continuous improvement.&lt;/p&gt;

&lt;p&gt;Between formal reviews, continuous monitoring can help catch issues like failed login attempts or unusual access patterns. This proactive approach complements scheduled reviews and helps identify problems before they escalate.&lt;/p&gt;

&lt;p&gt;The goal isn’t to achieve perfect security - it’s to build a system that evolves and improves over time. Regular permission reviews create the feedback loop necessary for continuous improvement, helping you address issues before they turn into costly mistakes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: Better API Security Through Proper Authorization
&lt;/h2&gt;

&lt;p&gt;Broken Function Level Authorization (BFLA) continues to pose a serious risk. Recent data reveals that 41% of organizations have faced an API security incident, with 63% of these incidents leading to data breaches - even though 90% already had authentication policies in place. This highlights a critical gap: while authentication may be in place, effective authorization controls are often lacking, leaving room for BFLA vulnerabilities to flourish.&lt;/p&gt;

&lt;p&gt;Real-world cases show that no organization is completely safe from BFLA. Its combination of being hard to detect and easy to exploit makes it particularly dangerous.&lt;/p&gt;

&lt;p&gt;Addressing this requires embedding robust authorization checks into every layer of your API architecture. Every API endpoint must enforce authorization by verifying user identity, roles, and permissions before granting access to sensitive functions or data. This goes beyond simply implementing Role-Based Access Control (RBAC) or Attribute-Based Access Control (ABAC) - it’s about adopting a mindset where security is treated as a core part of development, just like writing clean, efficient code.&lt;/p&gt;

&lt;p&gt;Incorporating tools such as Postman and OWASP ZAP into your CI/CD pipeline can help ensure that authorization checks are consistently validated.&lt;/p&gt;

&lt;p&gt;For long-term protection, strategies like enforcing the principle of least privilege, &lt;a href="https://zuplo.com/blog/2024/07/19/what-is-gitops" rel="noopener noreferrer"&gt;managing policies through GitOps&lt;/a&gt;, and conducting regular reviews of your authorization configurations are essential. These practices, combined with the testing and role configuration methods discussed earlier, create a stronger defense against threats.&lt;/p&gt;

&lt;p&gt;BFLA is ranked #5 on the OWASP API Top 10. By implementing thorough function-level validation, maintaining server-side authorization controls, and adopting a zero-trust approach to API access, you’re not just patching vulnerabilities - you’re building systems designed to handle both current and emerging threats.&lt;/p&gt;

&lt;p&gt;Use the techniques outlined in this guide as a starting point, and remember: API security is an ongoing process that demands constant vigilance and improvement.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What steps can organizations take to prevent Broken Function Level Authorization (BFLA) vulnerabilities in APIs?
&lt;/h3&gt;

&lt;h2&gt;
  
  
  Preventing Broken Function Level Authorization (BFLA) in APIs
&lt;/h2&gt;

&lt;p&gt;Protecting APIs from &lt;strong&gt;Broken Function Level Authorization (BFLA)&lt;/strong&gt; vulnerabilities requires a focus on well-implemented access controls and diligent security testing. Start by ensuring that every API function has strict, role-based authorization checks in place. These checks should prevent unauthorized users from accessing sensitive operations and must be applied consistently across all API endpoints.&lt;/p&gt;

&lt;p&gt;Regular security assessments are equally important. Use tools like security scanners or conduct manual testing to uncover vulnerabilities before attackers can exploit them. Additionally, stay informed about the latest security practices, such as those outlined in the OWASP guidelines. By continuously reviewing and improving your authorization processes, you can strengthen your API's defenses and reduce the risk of breaches.&lt;/p&gt;

&lt;h3&gt;
  
  
  How can I identify if my API has Broken Function Level Authorization (BFLA) vulnerabilities, and what are the best ways to detect them?
&lt;/h3&gt;

&lt;h2&gt;
  
  
  Broken Function Level Authorization (BFLA)
&lt;/h2&gt;

&lt;p&gt;BFLA vulnerabilities occur when users gain access to restricted functions or data by manipulating API requests. For instance, if altering a request parameter allows someone to access sensitive operations or resources they shouldn't, that's a clear sign of a BFLA issue. Another red flag is when access controls are inconsistent or missing across various API endpoints.&lt;/p&gt;

&lt;p&gt;To identify these vulnerabilities, start by thoroughly reviewing your API's authorization logic. Use tools to simulate unauthorized access attempts and analyze the results. Combining manual code reviews with automated security testing can be particularly effective. Detailed logging of access patterns also helps in spotting potential weaknesses. Tools like &lt;strong&gt;Postman&lt;/strong&gt; and &lt;strong&gt;OWASP ZAP&lt;/strong&gt; are great for crafting test requests and examining responses to pinpoint gaps in your authorization setup. API gateways like Zuplo help you implement fixes at scale. Taking these steps can go a long way in strengthening your API's security.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why should authorization testing be part of your CI/CD pipeline, and what tools can help?
&lt;/h3&gt;

&lt;p&gt;Incorporating &lt;strong&gt;authorization testing&lt;/strong&gt; into your CI/CD pipeline is a smart way to identify security vulnerabilities early, ensuring that sensitive functions are only accessible to authorized users. By addressing these issues proactively, you can reduce risks, block unauthorized access, and maintain compliance with security standards. In the fast-paced world of CI/CD, relying solely on manual testing can leave gaps, but automated testing provides consistent and dependable results.&lt;/p&gt;

&lt;p&gt;Tools like &lt;strong&gt;OWASP ZAP&lt;/strong&gt; are excellent for dynamic application security testing, while &lt;a href="https://www.sonarsource.com/products/sonarqube/" rel="noopener noreferrer"&gt;&lt;strong&gt;SonarQube&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://checkmarx.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;Checkmarx&lt;/strong&gt;&lt;/a&gt; specialize in static security testing. These tools integrate seamlessly into your pipeline, automating checks and enabling you to catch and resolve issues quickly - before they ever make it to production.&lt;/p&gt;

</description>
      <category>security</category>
      <category>api</category>
    </item>
    <item>
      <title>Troubleshooting Broken Object Level Authorization</title>
      <dc:creator>Adrian Machado</dc:creator>
      <pubDate>Thu, 04 Sep 2025 06:14:52 +0000</pubDate>
      <link>https://dev.to/zuplo/troubleshooting-broken-object-level-authorization-44f1</link>
      <guid>https://dev.to/zuplo/troubleshooting-broken-object-level-authorization-44f1</guid>
      <description>&lt;p&gt;&lt;strong&gt;Broken Object Level Authorization (BOLA)&lt;/strong&gt; is the top API security risk according to &lt;a href="https://owasp.org/" rel="noopener noreferrer"&gt;OWASP&lt;/a&gt;. It happens when APIs fail to verify if users are authorized to access specific data objects, even if they are authenticated. This vulnerability can lead to data breaches, account takeovers, and compliance violations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Takeaways:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What is BOLA?&lt;/strong&gt; Attackers manipulate object IDs (e.g., changing
&lt;code&gt;/api/orders/123&lt;/code&gt; to &lt;code&gt;/api/orders/124&lt;/code&gt;) to access unauthorized data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it’s critical:&lt;/strong&gt; BOLA is easy to exploit and affects APIs across
industries like finance and healthcare.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How to detect it:&lt;/strong&gt; Look for APIs that accept object IDs without verifying
user permissions or return &lt;code&gt;200 OK&lt;/code&gt; instead of &lt;code&gt;403 Forbidden&lt;/code&gt; for unauthorized access.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How to fix it:&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Enforce strict server-side authorization checks.&lt;/li&gt;
&lt;li&gt;Use unpredictable identifiers like UUIDs instead of sequential IDs.&lt;/li&gt;
&lt;li&gt;Validate API inputs and outputs using schemas.&lt;/li&gt;
&lt;li&gt;Implement API gateways like Zuplo for centralized control and integrate
security testing into CI/CD pipelines.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Quick Comparison: BOLA vs. Other Authorization Issues
&lt;/h3&gt;

&lt;p&gt;| &lt;strong&gt;Issue&lt;/strong&gt;                                                                       | &lt;strong&gt;Description&lt;/strong&gt;                                                   | &lt;strong&gt;Example&lt;/strong&gt;                             | | ------------------------------------------------------------------------------- | ----------------------------------------------------------------- | --------------------------------------- | | &lt;strong&gt;BOLA&lt;/strong&gt;                                                                        | Unauthorized access to specific data objects by manipulating IDs. | Changing &lt;code&gt;/api/orders/123&lt;/code&gt; to &lt;code&gt;/124&lt;/code&gt;.   | | &lt;a href="https://zuplo.com/learning-center/2025/07/30/troubleshooting-broken-function-level-authorization" rel="noopener noreferrer"&gt;&lt;strong&gt;BFLA&lt;/strong&gt;&lt;/a&gt; | Accessing endpoints users should not have access to at all.       | Accessing admin-only APIs.              | | &lt;strong&gt;BOPLA&lt;/strong&gt;                                                                       | Accessing unauthorized properties within an object.               | Viewing hidden fields in API responses. |&lt;/p&gt;

&lt;h3&gt;
  
  
  Next Steps
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Audit your APIs for BOLA vulnerabilities.&lt;/li&gt;
&lt;li&gt;Use tools like &lt;a href="https://www.zaproxy.org/" rel="noopener noreferrer"&gt;OWASP ZAP&lt;/a&gt; or
&lt;a href="https://portswigger.net/burp" rel="noopener noreferrer"&gt;Burp Suite&lt;/a&gt; for automated testing.&lt;/li&gt;
&lt;li&gt;Regularly monitor logs for suspicious activity, like sequential ID
enumeration.&lt;/li&gt;
&lt;li&gt;Educate your team on
&lt;a href="https://zuplo.com/blog/2022/12/01/api-key-authentication" rel="noopener noreferrer"&gt;secure API development practices&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By addressing BOLA vulnerabilities, you protect sensitive data, ensure compliance, and maintain user trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Identify BOLA Vulnerabilities
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Warning Signs of BOLA Problems
&lt;/h3&gt;

&lt;p&gt;Spotting BOLA vulnerabilities early can save your system from major security breaches. One red flag is when APIs accept object identifiers without verifying them against the permissions of the logged-in user. For example, if an API endpoint behaves differently depending on the object ID passed - without returning an "unauthorized" error - it could be a sign of a BOLA issue.&lt;/p&gt;

&lt;p&gt;Keep an eye out for APIs that return a 200 (success) response instead of a 403 (forbidden) code when unauthorized access is attempted. Also, watch for direct internal references in URLs, as these can indicate a potential vulnerability. If there are no internal checks to confirm ownership or permissions before delivering a response, the API is likely exposed to BOLA attacks.&lt;/p&gt;

&lt;p&gt;These warning signs are just the starting point. Manual testing can dig deeper to reveal how your API handles manipulated object identifiers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Manual Testing Methods for BOLA
&lt;/h3&gt;

&lt;p&gt;Manual testing remains one of the most effective ways to uncover BOLA vulnerabilities. By simulating various scenarios, you can see how your API reacts to manipulated inputs. Begin by examining API documentation or using tools like an interception proxy to find endpoints accepting object identifiers. Look for patterns in endpoints, such as &lt;code&gt;/users/{userID}&lt;/code&gt; or &lt;code&gt;/orders/{orderID}&lt;/code&gt;, that might indicate areas of risk.&lt;/p&gt;

&lt;p&gt;A key method is to modify object identifiers in API requests and observe if unauthorized access is granted. For instance, consider this endpoint for a social media platform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PATCH /api/users/profile
{ "userID": 12345, "displayName": "My New Name" }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the API blindly trusts the provided &lt;code&gt;userID&lt;/code&gt; without checking it against the logged-in user's session, an attacker could potentially change another user's profile.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Broken Object Level Authorization occurs when an API fails to implement &amp;gt; strict controls around who can access what. It's like leaving your house &amp;gt; unlocked and hoping nobody with bad intentions walks in." &amp;gt; &amp;gt; - &lt;a href="https://www.stackhawk.com/product/" rel="noopener noreferrer"&gt;StackHawk&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;GraphQL APIs require similar scrutiny. Test by altering object IDs in query parameters and check for vulnerabilities. Additionally, look for bulk access issues where the API might return data for multiple users instead of just the authenticated one.&lt;/p&gt;

&lt;p&gt;For example, a healthcare system might have an endpoint 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;GET /api/patients/{patientID}/records
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If any authenticated user can access another patient’s records by simply changing the &lt;code&gt;patientID&lt;/code&gt;, it reveals a severe flaw that compromises sensitive data. These examples show how small manipulations can lead to major security breaches, underlining the importance of thorough testing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automated BOLA Detection Tools
&lt;/h3&gt;

&lt;p&gt;While manual testing provides detailed insights, automated tools are essential for scaling your efforts across all endpoints. Traditional methods like fuzzing and static analysis often miss the nuances of BOLA vulnerabilities, but AI-powered tools can better interpret application logic and craft precise test cases.&lt;/p&gt;

&lt;p&gt;Tools like &lt;strong&gt;OWASP ZAP&lt;/strong&gt; and &lt;strong&gt;Burp Suite&lt;/strong&gt; are popular choices for &lt;a href="https://zuplo.com/docs/articles/testing-api-key-authentication" rel="noopener noreferrer"&gt;API security testing&lt;/a&gt;. OWASP ZAP is free and open-source, offering robust automation capabilities. On the other hand, Burp Suite provides broader functionality and greater flexibility, though its commercial pricing reflects these added features. Both tools can be adapted with add-ons for more advanced BOLA detection.&lt;/p&gt;

&lt;p&gt;The effectiveness of automated tools is evident in real-world use cases. In 2023, researchers from &lt;a href="https://www.paloaltonetworks.com/" rel="noopener noreferrer"&gt;Palo Alto Networks&lt;/a&gt;' Unit 42 used an AI-powered tool to test the &lt;a href="https://easyappointments.org/" rel="noopener noreferrer"&gt;Easy!Appointments&lt;/a&gt; platform. They discovered 15 BOLA vulnerabilities, tracked as CVE-2023-3285 through CVE-2023-3290 and CVE-2023-38047 through CVE-2023-38055. These flaws allowed low-privileged users to manipulate appointments created by higher-privileged users. The issues were patched in version 1.5.0.&lt;/p&gt;

&lt;p&gt;Modern tools also excel at API discovery, identifying &lt;a href="https://zuplo.com/learning-center/2025/07/31/api-discoverability-why-its-important-the-risk-of-shadow-and-zombie-apis" rel="noopener noreferrer"&gt;shadow or undocumented APIs&lt;/a&gt; that might otherwise go unnoticed. This capability is critical, especially as API-related cyberattacks continue to rise. For instance, India reported a staggering 3,000% increase in API attacks during Q3 of 2024, with over 271 million incidents in that period alone.&lt;/p&gt;

&lt;p&gt;Automated tools can also integrate seamlessly into CI/CD pipelines, catching vulnerabilities early in the development process. Choose tools that provide actionable remediation advice instead of generic descriptions, as this helps developers address issues more efficiently.&lt;/p&gt;

&lt;p&gt;When combined with manual testing, automated tools create a well-rounded strategy to tackle modern API security challenges effectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Broken Object Level Authorization (BOLA) Explained
&lt;/h2&gt;

&lt;p&gt;Here's a video that covers BOLA pretty well:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/YciLnEY1AN0"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Fix BOLA Issues in APIs
&lt;/h2&gt;

&lt;p&gt;Fixing BOLA vulnerabilities requires a layered approach that combines strict access checks, input validation, and secure handling of identifiers. These steps help ensure that your API remains protected from unauthorized access and manipulation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up Proper Object-Level Authorization
&lt;/h3&gt;

&lt;p&gt;The first step to addressing BOLA vulnerabilities is enforcing strict authorization on every API endpoint. This means verifying that the authenticated user has permission to access or modify the requested resource - not just confirming their identity.&lt;/p&gt;

&lt;p&gt;Start by validating user permissions for every function that accesses a database record based on client input. Implement a centralized and reusable authorization mechanism to streamline this process.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"BOLA is already #1 on the OWASP API Security Top 10 list - and for good &amp;gt; reasons. API providers do a great job at making sure that users are &amp;gt; authenticated to the API, so they want to make sure that legitimate users have &amp;gt; access. But the number one thing that's often overlooked is authorization, &amp;gt; ensuring that user A can't access, interact with, or alter user B's &amp;gt; resources - at all." - Corey Ball, Cybersecurity Consulting Manager and Author &amp;gt; of "Hacking APIs"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To map users to their authorized resources, link user accounts with the specific objects they are allowed to access. For sensitive data, ensure every request verifies the user's association with the requested record.&lt;/p&gt;

&lt;p&gt;Use a &lt;strong&gt;JWT token&lt;/strong&gt; to extract the user ID instead of accepting it as a parameter. This prevents attackers from tampering with user identifiers in request parameters, as the user information comes directly from the authenticated session token.&lt;/p&gt;

&lt;p&gt;Introduce robust session management systems and role-based access controls to enforce fine-grained permissions. This ensures users can only access data necessary for their roles or specific tasks. Additionally, implement &lt;a href="https://zuplo.com/learning-center/2025/04/15/how-api-schema-validation-boosts-effective-contract-testing" rel="noopener noreferrer"&gt;schema validation&lt;/a&gt; to ensure your API processes only properly structured data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Better API Schema Validation
&lt;/h3&gt;

&lt;p&gt;Strong authorization measures should be paired with &lt;a href="https://zuplo.com/blog/2022/03/18/incoming-body-validation-with-json-schema" rel="noopener noreferrer"&gt;API schema validation&lt;/a&gt; to guard against malicious inputs and unexpected behavior. By validating incoming data against predefined schemas, APIs can block harmful or malformed requests before they reach the application logic.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/POkuwh0iAbc"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Define a &lt;strong&gt;JSON Schema&lt;/strong&gt; for your API responses, detailing required fields, data types, and acceptable value ranges. For example, if your API expects a user ID, specify whether it should accept integers, UUIDs, or specific string patterns.&lt;/p&gt;

&lt;p&gt;Integrate validation logic directly into your API using middleware or framework-provided libraries. This ensures incoming requests are checked against defined schemas before processing and outgoing responses comply with expected formats. Input validation helps block attackers from sending unexpected data, while output validation prevents accidental exposure of sensitive information.&lt;/p&gt;

&lt;p&gt;Secure object identifiers by enforcing strict formatting rules and sanitizing inputs to reject special characters. When errors occur, return concise messages that inform the user without revealing system details. Regularly update your schemas to reflect changes in your API as it evolves.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making Object Identifiers More Secure
&lt;/h3&gt;

&lt;p&gt;Even with strong authorization and validation, securing object identifiers is critical to reducing attack risks. Replace sequential IDs with &lt;strong&gt;UUIDs&lt;/strong&gt; and map them to internal IDs. This approach makes external references unguessable while maintaining internal database efficiency.&lt;/p&gt;

&lt;p&gt;For example, generate UUIDs when creating objects, store both the UUID and internal ID in your database, and use the UUID for all external API communications. This ensures that even if an attacker guesses an identifier, they cannot exploit it without proper authorization.&lt;/p&gt;

&lt;p&gt;However, secure identifiers alone are not enough. Authorization checks remain essential - a valid UUID does not automatically grant access to a resource if the user is not authorized. A real-world example of this occurred when a major social media platform allowed users to access private images by altering the &lt;code&gt;user_id&lt;/code&gt; parameter in a URL. This flaw exposed millions of users' personal photos until it was fixed.&lt;/p&gt;

&lt;p&gt;To further minimize risks, apply the principle of least privilege. Limit user and system component permissions to only what is necessary for their roles or tasks. This reduces the damage an attacker can cause, even if they gain unauthorized access.&lt;/p&gt;

&lt;p&gt;The urgency of addressing BOLA vulnerabilities cannot be overstated. In 2023, over 75% of reported API vulnerabilities stemmed from improper &lt;a href="https://zuplo.com/docs/policies/acl-policy-inbound" rel="noopener noreferrer"&gt;access control&lt;/a&gt;, with BOLA being the most exploited issue worldwide. Protecting your APIs from BOLA attacks is not optional - it’s a critical step for maintaining secure and trustworthy systems.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://zuplo.com?utm_source=dev.to&amp;amp;utm_medium=inline-cta"&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%2Fiikcuf7axhu4gkw0jmqa.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prevention and Best Practices
&lt;/h2&gt;

&lt;p&gt;To keep APIs secure, it’s essential to take proactive steps: enforce strict authorization controls, integrate continuous security testing, and monitor for potential threats. This layered approach not only complements earlier troubleshooting steps but also strengthens your API’s overall security.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up API Gateways for Authorization
&lt;/h3&gt;

&lt;p&gt;API gateways act as the frontline defense against BOLA (Broken Object Level Authorization) vulnerabilities. By centralizing security controls, they ensure consistent enforcement across every endpoint. Instead of embedding authorization logic into each individual microservice, gateways allow you to manage policies from one central location.&lt;/p&gt;

&lt;p&gt;Zuplo's programmable API gateways make it easier to implement robust authorization measures. You can configure these gateways to validate &lt;a href="https://zuplo.com/docs/policies/open-id-jwt-auth-inbound" rel="noopener noreferrer"&gt;JWT tokens&lt;/a&gt;, check OAuth scopes, and &lt;a href="https://zuplo.com/learning-center/2025/01/28/how-rbac-improves-api-permission-management" rel="noopener noreferrer"&gt;enforce role-based access controls&lt;/a&gt; before requests even reach your backend. For instance, if a user tries to access &lt;code&gt;/api/invoices/12345&lt;/code&gt;, the gateway ensures they have the necessary permissions to view that invoice. This prevents attackers from exploiting endpoints by simply changing object IDs in their requests.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/Xo02a8rzQKc"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;To enhance security further, use VPC Links to connect your gateway securely to private network applications. Pair this with fine-grained access control at the API level to double-check object ownership and user permissions, even after passing through the gateway.&lt;/p&gt;

&lt;p&gt;But gateways aren’t the only tool in your arsenal. Automating security testing in your CI/CD pipeline is another key step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Security Testing to CI/CD Pipelines
&lt;/h3&gt;

&lt;p&gt;Integrating automated security tests into your CI/CD pipeline helps identify BOLA vulnerabilities early in development - before they ever reach production. This “shift-left” approach makes it easier and less expensive to tackle issues upfront. These automated tests build on earlier manual and automated testing methods, reinforcing a proactive stance on API security.&lt;/p&gt;

&lt;p&gt;Tools like &lt;strong&gt;StackHawk&lt;/strong&gt; allow teams to embed security testing directly into their CI/CD workflows. With every build, vulnerabilities are automatically scanned and flagged, with detailed reports that pinpoint the issue’s location in the code and suggest fixes.&lt;/p&gt;

&lt;p&gt;Here’s how to set it up: configure your pipeline to run both SAST (Static Application Security Testing) and DAST (Dynamic Application Security Testing) tools on every commit. SAST tools analyze your codebase for security flaws, while DAST tools test the live application for vulnerabilities like BOLA. You can even write tests that simulate unauthorized access attempts to ensure your authorization mechanisms are rock-solid. If these tests fail, the build should stop immediately.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"A call to arms for CISOs: Stop chasing audits - embed end-to-end, automated &amp;gt; API security testing throughout your SDLC to deliver fast, secure, and &amp;gt; compliant product releases." – Aptori&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Additionally, use IaC (Infrastructure as Code) vulnerability scanning to catch deployment misconfigurations that could lead to BOLA risks. Providing developers with tools that integrate directly into their IDEs can also encourage better authorization practices during API development.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring for Authorization Problems
&lt;/h3&gt;

&lt;p&gt;Real-time monitoring is essential for spotting BOLA attacks as they happen and uncovering security gaps. Effective monitoring goes beyond basic access logs by identifying patterns that hint at unauthorized access attempts.&lt;/p&gt;

&lt;p&gt;Set up anomaly detection systems to flag unusual activity. For example, if a user suddenly accesses hundreds of customer records in a short period, it could signal a BOLA attack. Alerts should notify your security team immediately when such patterns arise.&lt;/p&gt;

&lt;p&gt;Be on the lookout for sequential ID enumeration attacks, where attackers try different object IDs systematically (e.g., &lt;code&gt;/api/users/1&lt;/code&gt;, &lt;code&gt;/api/users/2&lt;/code&gt;, &lt;code&gt;/api/users/3&lt;/code&gt;). Track these patterns and configure alerts for suspicious behavior.&lt;/p&gt;

&lt;p&gt;Detailed logging is another critical tool. Record successful requests and failed authorizations, including user IDs, resources accessed, timestamps, and reasons for denial. This level of detail helps quickly identify and respond to attacks.&lt;/p&gt;

&lt;p&gt;Dashboards can make monitoring more actionable. Use them to visualize key metrics like authorization failure rates, unusual access patterns, and frequently targeted endpoints. This allows your team to spot emerging threats at a glance.&lt;/p&gt;

&lt;p&gt;To respond to detected threats, consider automating defensive actions. For instance, temporarily block IP addresses involved in enumeration attacks or require additional authentication for users exhibiting suspicious activity. Rate limiting at the gateway level can also slow attackers down, making their efforts more difficult and time-consuming. Finally, establish baseline behavior patterns for your API usage. If there’s a sudden spike in access or other unusual activity, it should trigger an immediate investigation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion and Key Takeaways
&lt;/h2&gt;

&lt;p&gt;BOLA (Broken Object Level Authorization) has earned its spot as the &lt;strong&gt;#1 risk&lt;/strong&gt; in the OWASP Top 10 API Security Risks for 2023. High-profile breaches in recent years highlight just how dangerous these vulnerabilities can be.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Lessons from Addressing BOLA
&lt;/h3&gt;

&lt;p&gt;Corey Ball, Cybersecurity Consulting Manager and author of &lt;em&gt;Hacking APIs&lt;/em&gt;, puts it succinctly: &lt;em&gt;"API providers do a great job at making sure that users are authenticated to the API, so they want to make sure that legitimate users have access. But the number one thing that's often overlooked is authorization, ensuring that user A can't access, interact with, or alter user B's resources - at all."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Three essential practices stand out when tackling BOLA:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server-side validation&lt;/strong&gt;: Every object access request should be validated on
the server side.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unpredictable identifiers&lt;/strong&gt;: Use UUIDs or other non-sequential identifiers
instead of easily guessable IDs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Least privilege principle&lt;/strong&gt;: Restrict user permissions to only what is
absolutely necessary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Beyond improving overall security, addressing BOLA also helps meet regulatory requirements such as GDPR, CCPA, and HIPAA. This reduces the risk of privacy violations, account takeovers, financial fraud, or even sabotage of systems accessed via APIs.&lt;/p&gt;

&lt;p&gt;These lessons provide the groundwork for the proactive security strategies discussed in the next section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next Steps for Strengthening API Security
&lt;/h3&gt;

&lt;p&gt;To build on these principles, consider the following actions to enhance your API security framework:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Integrate continuous security testing&lt;/strong&gt;: Use API audit and scanning tools
directly within developers' IDEs to identify vulnerabilities early. This "shift-left" approach pairs well with both manual and automated testing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session management and access control&lt;/strong&gt;: Define user roles and permissions,
bind object identifiers to authenticated sessions, and sanitize all inputs to prevent unauthorized access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leverage API gateways&lt;/strong&gt;: Tools like Zuplo act as centralized checkpoints for
enforcing security policies across all endpoints. These gateways ensure consistent authorization controls and streamline policy management.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, conduct regular audits of access logs to spot enumeration attacks or unusual activity patterns. Pair this with routine penetration tests to uncover emerging vulnerabilities and reinforce your defenses. The ultimate goal? Building a security-first mindset where authorization is a priority from the start.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How can I protect my API from Broken Object Level Authorization (BOLA) vulnerabilities?
&lt;/h3&gt;

&lt;p&gt;To protect your API from &lt;strong&gt;Broken Object Level Authorization (BOLA)&lt;/strong&gt; vulnerabilities, it’s crucial to enforce strict access controls for every object a user tries to access. Always verify that the user has the necessary permissions for the specific object mentioned in their request. Incorporating object-level security checks directly into your code is a must.&lt;/p&gt;

&lt;p&gt;Another layer of protection comes from using unique, hard-to-guess identifiers for objects, which makes unauthorized access more difficult. Additionally, make it a habit to test your API for security weaknesses. Tools like &lt;strong&gt;OWASP ZAP&lt;/strong&gt; and &lt;strong&gt;Burp Suite&lt;/strong&gt; are excellent for conducting regular security assessments, helping you identify and address potential vulnerabilities before they become a problem. These steps are key to keeping sensitive data secure.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are the best practices for securing server-side authorization to prevent unauthorized access?
&lt;/h3&gt;

&lt;p&gt;To maintain secure server-side authorization and guard against unauthorized access, it's crucial to &lt;strong&gt;incorporate authorization checks early in the development process&lt;/strong&gt;. This approach ensures that roles and permissions are clearly outlined as your application grows. Using middleware to &lt;a href="https://zuplo.com/docs/policies/axiomatics-authz-inbound" rel="noopener noreferrer"&gt;enforce authorization policies&lt;/a&gt; on every request is another essential step, as it helps to close off any unprotected endpoints.&lt;/p&gt;

&lt;p&gt;Adopting the &lt;strong&gt;principle of least privilege&lt;/strong&gt; is key. This means restricting user permissions to only what’s absolutely necessary for their specific role. One effective way to implement this is through role-based access control (RBAC). Beyond that, make it a habit to regularly review access logs to spot any unusual or suspicious activity. Adding &lt;strong&gt;multi-factor authentication (MFA)&lt;/strong&gt; can also bolster security by requiring additional verification steps.&lt;/p&gt;

&lt;p&gt;When combined, these measures create a reliable authorization framework that protects sensitive information and reduces the likelihood of unauthorized access.&lt;/p&gt;

&lt;h3&gt;
  
  
  How can I use tools like OWASP ZAP or Burp Suite to find and fix BOLA vulnerabilities in my API?
&lt;/h3&gt;

&lt;p&gt;Tools like &lt;strong&gt;OWASP ZAP&lt;/strong&gt; and &lt;strong&gt;Burp Suite&lt;/strong&gt; play a crucial role in identifying and resolving Broken Object Level Authorization (BOLA) vulnerabilities in APIs. These tools streamline the process of testing access controls by simulating unauthorized access attempts and analyzing how the API responds. This makes it easier to pinpoint areas where users might gain access to data or resources they shouldn’t.&lt;/p&gt;

&lt;p&gt;With their active scanning features, you can detect misconfigurations and weaknesses in authorization checks. They also help validate object ownership, ensuring that proper access controls are enforced. This added layer of scrutiny helps protect sensitive data and reduces the risk of unauthorized access.&lt;/p&gt;

</description>
      <category>security</category>
      <category>api</category>
    </item>
    <item>
      <title>RFC 9727 api-catalog Explained</title>
      <dc:creator>Adrian Machado</dc:creator>
      <pubDate>Thu, 04 Sep 2025 06:14:36 +0000</pubDate>
      <link>https://dev.to/zuplo/rfc-9727-api-catalog-explained-4k40</link>
      <guid>https://dev.to/zuplo/rfc-9727-api-catalog-explained-4k40</guid>
      <description>&lt;p&gt;&lt;a href="https://datatracker.ietf.org/doc/rfc9727/" rel="noopener noreferrer"&gt;RFC 9727&lt;/a&gt; introduces a standardized way for organizations to share API information through a well-known URI, &lt;code&gt;/.well-known/api-catalog&lt;/code&gt;. Released in June 2025, this standard simplifies &lt;a href="https://zuplo.com/learning-center/2025/07/31/api-discoverability-why-its-important-the-risk-of-shadow-and-zombie-apis" rel="noopener noreferrer"&gt;API discovery&lt;/a&gt;, governance, and lifecycle management by requiring a machine-readable catalog in the Linkset format (&lt;code&gt;application/linkset+json&lt;/code&gt;). The catalog includes API endpoints, version details, policies, and links to &lt;a href="https://www.openapis.org/" rel="noopener noreferrer"&gt;OpenAPI&lt;/a&gt; specifications, ensuring consistent and secure API documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Highlights:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Location&lt;/strong&gt;: API catalogs are hosted at &lt;code&gt;/.well-known/api-catalog&lt;/code&gt;,
accessible via HTTPS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Format&lt;/strong&gt;: Uses the Linkset format with a profile parameter
(&lt;code&gt;https://www.rfc-editor.org/info/rfc9727&lt;/code&gt;) to ensure compliance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Purpose&lt;/strong&gt;: Improves API discoverability, reduces outdated APIs, and
strengthens governance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Requires HTTPS, TLS encryption, and read-only access for
external users.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RFC 9727 addresses challenges like API sprawl and poor documentation, making it easier for developers to locate, understand, and use APIs while helping organizations maintain consistency and security in their API portfolios.&lt;/p&gt;

&lt;h2&gt;
  
  
  RFC 9727 Technical Requirements and Structure
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Well-Known URI and Linkset Format Requirements
&lt;/h3&gt;

&lt;p&gt;RFC 9727 lays out the technical groundwork for implementing API catalogs. Specifically, it mandates that HTTPS HEAD requests to &lt;code&gt;/.well-known/api-catalog&lt;/code&gt; must return a Link header containing the RFC-defined relations. This ensures compatibility with various discovery tools and methods. To protect the integrity of API discovery, the catalog must be accessible exclusively over HTTPS, utilizing TLS for secure communication.&lt;/p&gt;

&lt;p&gt;The API catalog itself must be published in the Linkset format, using the &lt;code&gt;application/linkset+json&lt;/code&gt; content type. Additionally, it must include a profile parameter with the URI &lt;code&gt;https://www.rfc-editor.org/info/rfc9727&lt;/code&gt; to clearly denote compliance with RFC 9727.&lt;/p&gt;

&lt;p&gt;Kevin Smith of &lt;a href="https://www.vodafone.com/" rel="noopener noreferrer"&gt;Vodafone&lt;/a&gt; finalized RFC 9727 in June 2025 after 13 revisions spanning two years. He described its purpose succinctly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A request to the api-catalog resource will return a document detailing the &amp;gt; Publisher's APIs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These technical requirements are designed to enhance API discoverability and ensure consistent catalog management.&lt;/p&gt;

&lt;h3&gt;
  
  
  API Catalog Content and Metadata
&lt;/h3&gt;

&lt;p&gt;Beyond the technical setup, the catalog's content is key to improving API discoverability. An RFC 9727-compliant API catalog must provide hyperlinks to API endpoints, allowing automated tools to reliably locate and interact with the APIs. To elevate the catalog from a simple endpoint list to a developer-friendly resource, it should include detailed metadata. This can cover usage policies, API version details, and links to &lt;a href="https://zuplo.com/learning-center/2024/09/25/mastering-api-definitions" rel="noopener noreferrer"&gt;OpenAPI Specification (OAS) definitions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If embedding this metadata directly in the catalog isn’t feasible, it should instead be accessible at the corresponding API endpoint URIs. This approach gives publishers the flexibility to either centralize the information in the catalog or distribute it across individual endpoints.&lt;/p&gt;

&lt;p&gt;The catalog can also use the "item" link relation to identify resources that represent individual APIs. Additionally, RFC 9727 supports catalog federation via the "api-catalog" relation type. This feature enables linking to other API catalogs, paving the way for distributed networks of catalogs while maintaining discoverability.&lt;/p&gt;

&lt;h3&gt;
  
  
  api-catalog Examples
&lt;/h3&gt;

&lt;p&gt;Here are some examples, pulled straight from the RFC&lt;/p&gt;

&lt;h4&gt;
  
  
  Using Linkset with Link Relations
&lt;/h4&gt;

&lt;p&gt;This example uses the Linkset format (RFC9264) and the following link relations defined in (RFC8631):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;service-desc&lt;/code&gt;: Used to link to a description of the API that is primarily
intended for machine consumption (for example, the OpenAPI specification, YAML, or JSON file)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;service-doc&lt;/code&gt;: Used to link to API documentation that is primarily intended
for human consumption.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;service-meta&lt;/code&gt;: Used to link to additional metadata about the API and is
primarily intended for machine consumption.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;status&lt;/code&gt;: Used to link to the API status (e.g., API "health" indication) for
machine and/or human consumption.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Client request:&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;GET .well-known/api-catalog HTTP/1.1 Host: example.com Accept:
application/linkset+json
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Server response:&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="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="ne"&gt;OK Date: Mon, 01 Jun 2023 00:00:01 GMT Server: Apache-Coyote/1.1&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/linkset+json;&lt;/span&gt;
&lt;span class="na"&gt;profile="https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;//www.rfc-editor.org/info/rfc9727"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;"linkset"&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;"anchor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://developer.example.com/apis/foo_api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"service-desc"&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;"href"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://developer.example.com/apis/foo_api/spec"&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;"application/yaml"&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;"status"&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;"href"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://developer.example.com/apis/foo_api/status"&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;"application/json"&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;"service-doc"&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;"href"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://developer.example.com/apis/foo_api/doc"&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;"text/html"&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;"service-meta"&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;"href"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://developer.example.com/apis/foo_api/policies"&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;"text/xml"&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;span class="nl"&gt;"anchor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://apis.example.net/apis/cantona_api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"service-desc"&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;"href"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://apis.example.net/apis/cantona_api/spec"&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;"text/n3"&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;"service-doc"&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;"href"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://apis.example.net/apis/cantona_api/doc"&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;"text/html"&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;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;h4&gt;
  
  
  Using Linkset with Bookmarks
&lt;/h4&gt;

&lt;p&gt;You could also just embed a URL within the &lt;code&gt;item&lt;/code&gt; property instead:&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;"linkset"&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;"anchor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.example.com/.well-known/api-catalog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"item"&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;"href"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://developer.example.com/apis/foo_api"&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;"href"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://developer.example.com/apis/bar_api"&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;"href"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://developer.example.com/apis/cantona_api"&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;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;h4&gt;
  
  
  Nesting API Catalog links
&lt;/h4&gt;

&lt;p&gt;If your catalog is large, and cleanly segmented, you can consider having a primary catalog which branches out into sub-catalogs (ex. different products).&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;"linkset"&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;"anchor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.example.com/.well-known/api-catalog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"api-catalog"&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;"href"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://apis.example.com/iot/api-catalog"&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;"href"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://ecommerce.example.com/api-catalog"&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;"href"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://developer.example.com/gaming/api-catalog"&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;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;h3&gt;
  
  
  Security Requirements and Best Practices
&lt;/h3&gt;

&lt;p&gt;With the catalog’s structure and content defined, ensuring secure and reliable access becomes a top priority. RFC 9727 emphasizes operational responsibility and data protection as critical components of API catalog management. Publishers are encouraged to adhere to best practices, such as monitoring the catalog’s availability, performance, and metadata accuracy.&lt;/p&gt;

&lt;p&gt;To maintain quality, both manual reviews and automated checks should be conducted regularly. These efforts help identify and fix syntax errors, preventing disruptions in automated discovery processes.&lt;/p&gt;

&lt;p&gt;Lifecycle management is also a central focus. Removing outdated or deprecated API entries as part of the release cycle reduces risks tied to insecure or obsolete API versions. By prioritizing these security measures, publishers can ensure their API catalogs remain reliable and effective for discovery.&lt;/p&gt;

&lt;h2&gt;
  
  
  How RFC 9727 Changes API Catalog Management
&lt;/h2&gt;

&lt;p&gt;RFC 9727 introduces a transformative approach to managing API catalogs. It modernizes API discovery while integrating governance and lifecycle management into a unified framework. By providing standardized discovery tools and governance structures, this specification turns API catalogs from static, hard-to-navigate repositories into dynamic, machine-readable resources that actively support API operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Improved API Discovery
&lt;/h3&gt;

&lt;p&gt;RFC 9727 makes API discovery faster and more reliable. By standardizing the Linkset format, it ensures that discovery tools can interpret catalog information consistently, no matter who publishes it. This eliminates the previous chaos where organizations used different formats and scattered their catalogs across various locations. For developers, this means easier access to API details without the need for extra manual work.&lt;/p&gt;

&lt;p&gt;Publishers also gain new flexibility. They can announce APIs through multiple channels, making APIs more visible at key points in a developer's workflow - whether browsing documentation or sending programmatic requests.&lt;/p&gt;

&lt;p&gt;The inclusion of metadata is another game-changer. Catalogs can now provide critical details like version histories, usage policies, and links to OpenAPI specifications. This gives developers immediate access to the information they need to evaluate and integrate APIs effectively.&lt;/p&gt;

&lt;p&gt;All of this creates a seamless discovery process, laying the groundwork for improved governance and lifecycle management.&lt;/p&gt;

&lt;h3&gt;
  
  
  Better API Governance
&lt;/h3&gt;

&lt;p&gt;Beyond discovery, RFC 9727 strengthens &lt;a href="https://zuplo.com/learning-center/2025/07/14/what-is-api-governance-and-why-is-it-important" rel="noopener noreferrer"&gt;API governance&lt;/a&gt;. The requirement for a well-known URI ensures that every API domain publishes its catalog in a consistent, predictable location. This fixed setup, combined with enforced metadata standards, allows governance teams to monitor API usage more effectively and ensure compliance.&lt;/p&gt;

&lt;p&gt;This centralized system also minimizes risks, such as developers accidentally violating usage policies or working with outdated API versions. By clearly communicating policies and guidelines, organizations reduce confusion and errors.&lt;/p&gt;

&lt;p&gt;RFC 9727 also encourages best practices, like regularly monitoring catalog availability and conducting security reviews before deployment. These steps help maintain high-quality catalogs that accurately reflect an organization’s API offerings.&lt;/p&gt;

&lt;p&gt;To safeguard catalog integrity, publishers are advised to enforce read-only access for external requests to the well-known URI. This ensures that while APIs remain discoverable, their catalogs are protected from unauthorized modifications.&lt;/p&gt;

&lt;h3&gt;
  
  
  API Lifecycle Management Benefits
&lt;/h3&gt;

&lt;p&gt;RFC 9727 simplifies API lifecycle management by embedding catalog updates into release workflows. It suggests that API management tools include catalog maintenance as a standard part of their processes, ensuring catalogs always align with the latest API deployments.&lt;/p&gt;

&lt;p&gt;The specification also aids in handling legacy APIs and deprecated endpoints. By allowing publishers to include metadata about older versions, it provides developers with clear migration paths to newer services. Catalogs can communicate deprecation timelines, redirect users to updated versions, and outline usage policies to guide transitions. This transparency reduces the usual headaches associated with API version changes.&lt;/p&gt;

&lt;p&gt;Additionally, RFC 9727 tackles the issue of &lt;a href="https://zuplo.com/learning-center/2025/07/31/api-discoverability-why-its-important-the-risk-of-shadow-and-zombie-apis" rel="noopener noreferrer"&gt;"zombie APIs"&lt;/a&gt; - outdated APIs that linger and pose security risks. By requiring publishers to remove obsolete entries during the release cycle, the specification helps maintain clean and secure API inventories. Routine catalog audits become an essential part of this process.&lt;/p&gt;

&lt;p&gt;Framework providers can take this a step further by automating lifecycle management. For example, any changes to API links or metadata can trigger automatic catalog updates, keeping discovery information accurate in real time. By aligning catalog updates with API release schedules, organizations can maintain a precise and up-to-date inventory, reflecting the dynamic nature of modern API ecosystems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing RFC 9727 with Zuplo
&lt;/h2&gt;

&lt;p&gt;Zuplo’s built-in &lt;a href="https://zuplo.com/blog/2023/03/06/announcing-open-api-native-support" rel="noopener noreferrer"&gt;OpenAPI integration&lt;/a&gt; ensures your API catalog stays in line with RFC 9727 without extra effort. This real-time synchronization prevents the common issue of outdated API catalogs when changes occur, as the developer portal automatically reflects updates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up RFC 9727 Compliance in Zuplo
&lt;/h3&gt;

&lt;p&gt;With Zuplo’s programmable features, you can create an RFC 9727-compliant API catalog at &lt;code&gt;/.well-known/api-catalog&lt;/code&gt; in Linkset format. Start by developing a custom handler that pulls information from your OpenAPI specifications and formats it to meet RFC 9727 requirements, including details like API versions, usage policies, and links to documentation.&lt;/p&gt;

&lt;p&gt;To make your APIs more accessible, configure your developer portal to expose the &lt;code&gt;api-catalog&lt;/code&gt; endpoint. This ensures discoverability for both developers and automated tools. Zuplo’s flexibility allows you to fully customize the catalog generation process to align with your specific RFC 9727 needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Zuplo Features for RFC 9727 Support
&lt;/h3&gt;

&lt;p&gt;Zuplo comes packed with features that assist in meeting RFC 9727 requirements. &lt;strong&gt;GitOps integration&lt;/strong&gt; ensures your API catalog stays consistent and up-to-date. Any changes made to API specifications through Git workflows automatically sync with the catalog.&lt;/p&gt;

&lt;p&gt;Zuplo’s &lt;strong&gt;API governance tools&lt;/strong&gt; - like API linting, pull requests, and CI workflows - help maintain catalog quality. These tools align with RFC 9727’s recommendation for both human and automated syntax validations. Together, these features simplify compliance and make ongoing catalog management easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Maintaining Accurate and Secure Catalogs
&lt;/h3&gt;

&lt;p&gt;Zuplo’s automation and GitOps workflows ensure your catalog remains accurate and secure throughout its lifecycle. For example, when APIs are deprecated or removed during your release process, the catalog can be updated automatically, addressing RFC 9727’s requirement to remove outdated entries.&lt;/p&gt;

&lt;p&gt;Syntax validation is another key area. By integrating automated checks into your CI/CD pipeline, you can catch errors in OpenAPI specifications before they impact production. Since Zuplo natively supports OpenAPI, syntax issues are flagged during the build process, ensuring your catalog remains valid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://zuplo.com?utm_source=dev.to&amp;amp;utm_medium=inline-cta"&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%2Fiikcuf7axhu4gkw0jmqa.png" width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools and Strategies for RFC 9727 Implementation
&lt;/h2&gt;

&lt;p&gt;To successfully implement an RFC 9727-compliant API catalog, you’ll need a combination of effective tools and thoughtful strategies. This standard emphasizes both technical precision and operational reliability, so it’s crucial to establish processes that ensure compliance from the outset.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools for RFC 9727 Implementation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;JSON schema validators&lt;/strong&gt; play a key role in ensuring your API catalog meets the required structure and format. These tools, like &lt;a href="https://ajv.js.org/" rel="noopener noreferrer"&gt;AJV&lt;/a&gt;, catch syntax errors early, preventing issues that could disrupt API discovery. By integrating JSON schema validation into your build process, you can verify catalog compliance before deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Linkset format checkers&lt;/strong&gt; are specifically designed to validate the &lt;code&gt;application/linkset+json&lt;/code&gt; format. They ensure that the catalog correctly implements linkset structures, including relation types, target URIs, and metadata. The &lt;a href="https://www.ietf.org/" rel="noopener noreferrer"&gt;Internet Engineering Task Force&lt;/a&gt; (IETF) provides reference implementations that can serve as benchmarks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenAPI linting tools&lt;/strong&gt; help maintain consistency between API specifications and their catalog entries. Tools such as &lt;a href="https://ratemyopenapi.com" rel="noopener noreferrer"&gt;RateMyOpenAPI&lt;/a&gt; allow you to enforce custom rules, ensuring every API in the catalog is properly documented and versioned.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CI/CD integration plugins&lt;/strong&gt; streamline compliance checks throughout your development workflow. Plugins for platforms like &lt;a href="https://docs.github.com/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt;, &lt;a href="https://about.gitlab.com/solutions/continuous-integration/" rel="noopener noreferrer"&gt;GitLab CI&lt;/a&gt;, and &lt;a href="https://www.jenkins.io/" rel="noopener noreferrer"&gt;Jenkins&lt;/a&gt; can automate validation tests, generate reports, and even block deployments if compliance issues are detected.&lt;/p&gt;

&lt;p&gt;Once these tools are in place, the next step is to develop strategies to maintain and monitor your API catalog over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  API Catalog Maintenance Strategies
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Automated catalog generation&lt;/strong&gt; simplifies updates by linking catalog creation directly to your OpenAPI specifications. This ensures that your catalog stays current as your APIs evolve. Pair this with &lt;strong&gt;version control integration&lt;/strong&gt; to treat your catalog like code - track changes, review updates, and roll back problematic modifications through Git workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Release lifecycle integration&lt;/strong&gt; keeps your catalog accurate by embedding updates into your API deployment process. For example, removing outdated API entries during the release lifecycle helps maintain a clean and reliable catalog.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security maintenance&lt;/strong&gt; is another critical aspect. Regularly update access controls, review authentication policies, and monitor for unauthorized access attempts. RFC 9727 requires enforcing read-only privileges for external requests and internal monitoring systems, while limiting write access to designated roles. Conducting regular security audits ensures these controls remain effective.&lt;/p&gt;

&lt;p&gt;By combining these strategies with ongoing monitoring, you can ensure your catalog remains compliant and efficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tracking API Catalog Usage and Performance
&lt;/h3&gt;

&lt;p&gt;Tracking usage and performance metrics is essential for understanding how developers interact with your catalog. Analyze requests to the &lt;code&gt;/.well-known/api-catalog&lt;/code&gt; URI and correlate them with subsequent API requests to measure engagement. This data can reveal how effectively your catalog supports API discovery.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance monitoring&lt;/strong&gt; is vital for maintaining a responsive and reliable catalog. Key metrics to track include response times, error rates, and overall availability. These factors directly affect the experience of developers and automated tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Analytics integration&lt;/strong&gt; with platforms like Zuplo provides deeper insights into usage patterns. You can identify which APIs are most accessed, when peak discovery times occur, and how different developer groups interact with your catalog. These insights can guide API improvements and better catalog organization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rate limiting analysis&lt;/strong&gt; helps balance accessibility with system protection. RFC 9727 recommends implementing rate-limiting measures to prevent abuse and mitigate denial-of-service attacks. Regular analysis ensures these limits are effective without hindering legitimate users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compliance monitoring&lt;/strong&gt; involves scanning for issues such as missing metadata, broken links, or formatting errors. Keeping an eye on these details ensures your catalog maintains high quality as your API offerings grow.&lt;/p&gt;

&lt;p&gt;RFC 9727 also emphasizes the importance of monitoring availability, performance, usage, and metadata accuracy to maintain operational excellence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion and Key Takeaways
&lt;/h2&gt;

&lt;p&gt;RFC 9727 marks an important step in API standardization, offering a framework for API discovery through a well-known URI approach.&lt;/p&gt;

&lt;p&gt;By introducing a structured method for API catalogs, RFC 9727 makes programmatic discovery possible - a critical feature for businesses that rely heavily on APIs. This capability helps organizations address the ongoing challenge of "zombie" APIs - outdated or neglected endpoints that can create significant security vulnerabilities.&lt;/p&gt;

&lt;p&gt;The standard fosters better collaboration, consistent API management, and scalable governance while reducing risks associated with obsolete endpoints. It also supports the growth of API portfolios by providing a systematic approach to their management, ensuring consistency and improving security through integrated lifecycle governance.&lt;/p&gt;

&lt;p&gt;To make adopting RFC 9727 easier, &lt;strong&gt;Zuplo streamlines implementation&lt;/strong&gt; with features like native OpenAPI integration. This ensures that gateway configurations and specifications remain synchronized, removing the manual effort of maintaining accurate API catalogs. As Tom Carden from &lt;a href="https://www.rewiringamerica.org/about-us" rel="noopener noreferrer"&gt;Rewiring America&lt;/a&gt; shared:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Zuplo is the ultimate one-stop shop for all your API needs. With rate &amp;gt; limiting, &lt;a href="https://zuplo.com/features/api-key-management" rel="noopener noreferrer"&gt;API key management&lt;/a&gt;, &amp;gt; and documentation hosting, it saved us weeks of engineering time and let us &amp;gt; focus on solving problems unique to our mission."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Zuplo equips organizations with tools to customize compliance, enhance security, and simplify operations with features like OpenAPI synchronization, advanced authentication options, and detailed analytics. These capabilities address the complexities that often hinder successful adoption of standards like RFC 9727.&lt;/p&gt;

&lt;p&gt;As API ecosystems grow, RFC 9727 lays the groundwork for effective API management practices. Companies that implement this standard now can benefit from stronger governance, a better developer experience, and more streamlined API lifecycle management. Combining this standard with platforms like Zuplo positions organizations to handle the expanding demands of modern API ecosystems with confidence and security.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How does RFC 9727 make APIs easier to discover and manage?
&lt;/h3&gt;

&lt;p&gt;RFC 9727 introduces a standardized &lt;strong&gt;'api-catalog' well-known URI&lt;/strong&gt; and a specific link relation to improve how APIs are discovered and managed. These features allow for automated API discovery on a larger scale, simplifying the process for developers and teams to find, utilize, and oversee APIs.&lt;/p&gt;

&lt;p&gt;By organizing and making API information more accessible, RFC 9727 contributes to smoother API lifecycle management, stronger governance, and easier integration with API platforms. This standard marks an important advancement in creating a more streamlined and uniform method for handling API catalogs.&lt;/p&gt;

&lt;h3&gt;
  
  
  What security measures does RFC 9727 recommend to protect the integrity and reliability of API catalogs?
&lt;/h3&gt;

&lt;p&gt;RFC 9727 highlights crucial security practices for protecting API catalogs. It advises employing &lt;strong&gt;cryptographic techniques&lt;/strong&gt; like digital signatures or checksums to guarantee data integrity and authenticity. For safeguarding data during transmission, the use of secure transport protocols such as &lt;strong&gt;Transport Layer Security (TLS)&lt;/strong&gt; is strongly recommended.&lt;/p&gt;

&lt;p&gt;The document also stresses the importance of &lt;strong&gt;access controls and authentication mechanisms&lt;/strong&gt; to block unauthorized access or tampering. These steps are essential for ensuring the security, reliability, and trust in your organization's API catalogs.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s the best way for organizations to create and manage an RFC 9727-compliant API catalog with Zuplo?
&lt;/h3&gt;

&lt;p&gt;Organizations can rely on Zuplo's &lt;strong&gt;programmable API gateway&lt;/strong&gt; to simplify the process of building and managing an API catalog that adheres to RFC 9727 standards. By automating API discovery, organizing APIs into clear categories, and enabling the creation of remote Model Context Protocol (MCP) servers, Zuplo helps ensure your catalog meets these compliance requirements.&lt;/p&gt;

&lt;p&gt;Using Zuplo, you can boost &lt;strong&gt;API discoverability&lt;/strong&gt;, strengthen &lt;strong&gt;governance&lt;/strong&gt;, and streamline &lt;strong&gt;lifecycle management&lt;/strong&gt;. This not only ensures compliance with RFC 9727 but also delivers a well-structured, user-friendly API catalog tailored to your organization's unique needs.&lt;/p&gt;

</description>
      <category>security</category>
      <category>api</category>
    </item>
    <item>
      <title>Strangler Fig pattern for API versioning</title>
      <dc:creator>Adrian Machado</dc:creator>
      <pubDate>Thu, 04 Sep 2025 06:14:20 +0000</pubDate>
      <link>https://dev.to/zuplo/strangler-fig-pattern-for-api-versioning-3knm</link>
      <guid>https://dev.to/zuplo/strangler-fig-pattern-for-api-versioning-3knm</guid>
      <description>&lt;p&gt;The &lt;strong&gt;Strangler Fig pattern&lt;/strong&gt; is a method for modernizing legacy APIs without disrupting users or causing downtime. Inspired by how a strangler fig plant replaces its host tree, this approach allows old and new systems to coexist, with functionality gradually transitioning to the new system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Points
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What It Is&lt;/strong&gt;: A step-by-step approach to replace old APIs by introducing a
new system alongside the existing one, using a facade to manage traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Use It&lt;/strong&gt;: Reduces risk compared to a full rewrite, avoids downtime, and
allows incremental updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How It Works&lt;/strong&gt;:&lt;/li&gt;
&lt;li&gt;Introduce a facade to route requests between old and new APIs.&lt;/li&gt;
&lt;li&gt;Migrate functionality in small, manageable pieces.&lt;/li&gt;
&lt;li&gt;Test and validate each change before retiring legacy components.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tools&lt;/strong&gt;: Platforms like Zuplo assist with routing, monitoring, and managing
API migrations efficiently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This method is particularly useful for transitioning to microservices or updating API versions while maintaining stability and user satisfaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Video: Strangler Fig Pattern | Migrate Monolithic Application to Microservices Architecture
&lt;/h2&gt;

&lt;p&gt;Strangler fig is not a pattern that is limited to API versioning. Check out this video which explains the pattern in the context of microservice migrations to get the full picture:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/MV11JStATVE"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  How the Strangler Fig Pattern Works for API Versioning
&lt;/h2&gt;

&lt;p&gt;The Strangler Fig pattern allows legacy and new API versions to operate side by side, enabling a smooth and controlled transition without disrupting users.&lt;/p&gt;

&lt;p&gt;At its core, this pattern introduces an intermediary layer that sits between API consumers and backend systems. This layer handles routing, ensuring requests are directed to the appropriate API version based on specific rules. Let’s break down how this routing system works during migration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using a Facade for Request Routing
&lt;/h3&gt;

&lt;p&gt;The facade acts as the central routing layer, intercepting all incoming requests and directing them to the correct API version based on predefined rules.&lt;/p&gt;

&lt;p&gt;Initially, most requests are routed to the legacy API. As new features are rolled out, the facade gradually shifts targeted requests to the updated components. This ensures API consumers experience no interruptions or compatibility issues during the transition.&lt;/p&gt;

&lt;p&gt;The facade’s routing decisions can be based on various factors such as headers, URL paths, client IDs, geographic data, or specific payload characteristics. This flexibility allows teams to roll out new features to select user groups, test performance, and address any issues before a full-scale deployment. If problems arise, the facade can instantly redirect traffic back to the stable legacy version.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step-by-Step Migration Process
&lt;/h3&gt;

&lt;p&gt;Migrating with the Strangler Fig pattern involves a structured process that minimizes risks while allowing teams to learn and adapt. Each step builds on the last, creating a clear path from old systems to modernized architecture.&lt;/p&gt;

&lt;p&gt;The process starts with clearly identifying system boundaries and dividing the API into smaller, manageable components, often referred to as "thin slices". Once these slices are defined, an intermediary layer is introduced to allow seamless integration of new components without disrupting the existing system.&lt;/p&gt;

&lt;p&gt;A great example of this is &lt;a href="https://www.altexsoft.com/software-product-development/" rel="noopener noreferrer"&gt;AltexSoft&lt;/a&gt;’s migration of a 20-year-old property management system. The team updated the database structure, added new tables, and deployed new features while ensuring the legacy system remained functional. As new components were tested and validated, they were gradually integrated into the modern architecture.&lt;/p&gt;

&lt;p&gt;The migration typically follows a cycle: develop a new component, route traffic to it, monitor its performance, and retire the corresponding legacy component once it’s proven reliable. Over time, the facade evolves, starting with basic passthrough routing and gradually handling more complex traffic patterns as the migration progresses.&lt;/p&gt;

&lt;p&gt;The end goal is to fully decommission the legacy system once all functionalities have been successfully transitioned. At this point, teams can either remove the facade entirely or keep it as an adapter layer for legacy clients who haven’t updated their integration methods.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-Step Guide to Implementing the Strangler Fig Pattern
&lt;/h2&gt;

&lt;p&gt;Implementing the Strangler Fig pattern requires thoughtful planning and a structured approach. This method ensures a seamless shift from legacy APIs to modern alternatives.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assessing the Legacy API
&lt;/h3&gt;

&lt;p&gt;Start by thoroughly analyzing your API's architecture, dependencies, and key functionalities. Map out all endpoints, data flows, and integration points to create a detailed migration plan. Collaborate with experts to uncover hidden business logic and edge cases that might complicate the process. To better understand the current system, develop automated black-box and performance tests that capture its behavior. Adding strategic logging in critical areas - using techniques like aspect-oriented programming - can also provide valuable insights into how the API performs in production.&lt;/p&gt;

&lt;p&gt;Break the system into smaller, manageable "thin slices" and prioritize components based on business needs. Begin with a low-risk section of the system to gain confidence before tackling more critical features. Once you’ve mapped out the legacy API, move on to setting up a gateway to manage traffic effectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up a Facade for Traffic Interception
&lt;/h3&gt;

&lt;p&gt;Deploy an API gateway to serve as the central hub for routing requests. Configure it to direct traffic based on HTTP headers or URL paths. For instance, if you're updating an e-commerce checkout API from version 1 to version 2, you could route requests with a specific header (e.g., &lt;code&gt;x-version=v2&lt;/code&gt;) to the new version, while all other requests default to the legacy API. This approach avoids the need for additional proxy layers or overly complex URL structures. Use access logs to verify that routing behaves as expected and to troubleshoot any issues that arise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gradual Migration and Testing
&lt;/h3&gt;

&lt;p&gt;Migrate incrementally, testing at every stage to ensure backward compatibility. For example, provide default values for new endpoint parameters so existing clients remain unaffected. Use unit, integration, and regression tests to catch potential issues early in the process. This steady and deliberate approach allows for a smoother transition. Keep an eye on API adoption rates to determine which versions are actively used, and communicate updates clearly through release notes, migration guides, and updated documentation.&lt;/p&gt;

&lt;p&gt;Once the new API versions are stable and meet performance expectations, you can begin deprecating the legacy endpoints.&lt;/p&gt;

&lt;h3&gt;
  
  
  Removing Legacy APIs
&lt;/h3&gt;

&lt;p&gt;When the migration is complete and stable, start phasing out legacy APIs. Provide a clear timeline for deprecation and continue supporting older versions during the transition period. Before retiring any legacy endpoints, confirm that they are no longer handling significant traffic. Offer detailed documentation and support to ensure users feel confident throughout the process. For example, a major grocery retailer modernized its coupon management system by first targeting the frequently used but less complex &lt;code&gt;/get_coupons&lt;/code&gt; endpoint. This allowed them to validate their approach before moving on to more challenging components.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://zuplo.com?utm_source=dev.to&amp;amp;utm_medium=inline-cta"&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%2Fiikcuf7axhu4gkw0jmqa.png" width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools and Platforms to Support Migration
&lt;/h2&gt;

&lt;p&gt;Migrating systems using the Strangler Fig pattern requires tools that can handle intricate routing and monitoring tasks. These tools ensure a smooth transition while minimizing disruptions to users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Zuplo for Programmable API Management
&lt;/h3&gt;

&lt;p&gt;Zuplo stands out from traditional API gateways by offering programmable capabilities that allow custom code for advanced routing. Its integration with &lt;a href="https://www.redhat.com/en/topics/devops/what-is-gitops" rel="noopener noreferrer"&gt;GitOps&lt;/a&gt; ensures that every change - whether it's routing, policy updates, or configuration tweaks - is version-controlled and auditable. This approach minimizes the risk of losing progress or unintentionally rolling back critical rules.&lt;/p&gt;

&lt;p&gt;Zuplo is OpenAPI-native, which keeps your gateway aligned with your API specifications throughout the migration. This means as you develop new API versions, the gateway configuration stays consistent with the documented specs, eliminating mismatches between what's deployed and what's described.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;developer portal&lt;/strong&gt; is another key feature, providing separate, interactive documentation for both legacy and new API versions. This makes it easier for API consumers to identify the correct endpoints during the transition.&lt;/p&gt;

&lt;p&gt;Zuplo also offers &lt;strong&gt;extensive customization&lt;/strong&gt;, allowing you to create routing logic tailored to user segments, geographic locations, or specific usage patterns. This flexibility is crucial for managing the complexities of legacy systems.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Zuplo lets us focus on our API's value, not the infrastructure. Native GitOps &amp;gt; and local development works seamlessly. Customizable modules and theming give &amp;gt; us complete flexibility. Easy recommendation." - Matt Hodgson, CTO, Vendr&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Additionally, Zuplo’s &lt;strong&gt;edge deployment&lt;/strong&gt; processes routing decisions closer to users, reducing latency - a critical factor when balancing traffic between systems with different response times or geographic distributions.&lt;/p&gt;

&lt;p&gt;Now, let’s explore how Zuplo supports monitoring and analytics during migration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring and Analytics with Zuplo
&lt;/h3&gt;

&lt;p&gt;Zuplo pairs its routing capabilities with robust monitoring tools to help you maintain stability throughout the migration. Its analytics provide real-time insights into both legacy and new API versions, allowing you to compare performance, track error rates, and observe usage trends.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/mjmV8iNCWec"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom policies&lt;/strong&gt; enable advanced migration strategies, such as deploying canary releases to test new versions with specific user groups or automatically rolling back traffic if error rates spike. This ensures a controlled and reliable transition.&lt;/p&gt;

&lt;p&gt;The analytics dashboard offers a clear view of traffic distribution across API versions, making it easier to phase out legacy endpoints. By tracking adoption rates of new versions, you can identify and address potential issues before they escalate.&lt;/p&gt;

&lt;p&gt;Security and performance are maintained through &lt;strong&gt;rate limiting and authentication policies&lt;/strong&gt;. For example, you can apply stricter rate limits to legacy endpoints while ensuring new versions can handle the expected traffic load.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits and Challenges of the Strangler Fig Pattern
&lt;/h2&gt;

&lt;p&gt;Building on the migration steps mentioned earlier, let's dive into the key advantages and operational hurdles of using the Strangler Fig pattern.&lt;/p&gt;

&lt;p&gt;This pattern offers a practical way to manage &lt;a href="https://zuplo.com/blog/2022/05/17/how-to-version-an-api" rel="noopener noreferrer"&gt;API versioning&lt;/a&gt; while minimizing risks. One of its standout benefits is &lt;strong&gt;risk reduction&lt;/strong&gt;, as it allows for controlled testing and validation of each change. Additionally, it provides &lt;strong&gt;immediate value&lt;/strong&gt; to users by enabling incremental updates rather than forcing a complete overhaul in one go.&lt;/p&gt;

&lt;p&gt;Another major plus is &lt;strong&gt;zero downtime&lt;/strong&gt;. Users can continue accessing familiar endpoints without interruptions, avoiding the confusion and frustration that often come with abrupt system changes.&lt;/p&gt;

&lt;p&gt;However, the pattern isn't without its challenges. Running old and new systems in parallel &lt;strong&gt;increases resource demands&lt;/strong&gt; and adds complexity to operations. Essentially, you're maintaining two infrastructures during the transition, which can strain both budgets and team capacity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comparison Table: Benefits and Challenges
&lt;/h3&gt;

&lt;p&gt;| Benefits                                                                      | Challenges                                                                                            | | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | | Enables a smooth migration from a service to one or more replacement services | Unsuitable for small systems with low complexity                                                      | | Keeps legacy services operational while updating to newer versions            | Cannot be applied in systems where backend requests can't be intercepted and routed                   | | Allows adding new features and services while refactoring older ones          | The proxy or facade layer risks becoming a single point of failure or a bottleneck if poorly designed | | Useful for API versioning                                                     | Requires a robust rollback plan to revert changes safely if issues arise                              | | Supports legacy interactions for systems that won't or can't be upgraded      |                                                                                                       |&lt;/p&gt;

&lt;p&gt;While these trade-offs are clear, addressing the operational challenges is crucial to ensure a smooth migration process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tackling Common Challenges
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;facade layer&lt;/strong&gt; is often the most vulnerable point. If it fails, both the old and new systems could become inaccessible. To mitigate this risk, design the facade with redundancy and load balancing to maintain availability and reliability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data consistency&lt;/strong&gt; is another critical issue. When both old and new APIs interact with shared data, synchronization problems can occur. Using event-driven architectures or shared state management strategies can help prevent conflicts and keep systems aligned.&lt;/p&gt;

&lt;p&gt;Managing &lt;strong&gt;resource usage&lt;/strong&gt; and operational overhead requires careful planning. As traffic shifts from legacy systems to new ones, you can gradually scale down the resources allocated to older components, keeping costs in check.&lt;/p&gt;

&lt;p&gt;Ivan Mosiev highlights another challenge:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Ongoing analysis is required to assess the impact on legacy systems, which &amp;gt; adds complexity as you're dealing with both systems in parallel until the &amp;gt; migration is finished".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This parallel operation demands constant monitoring and clear communication across teams to avoid missteps.&lt;/p&gt;

&lt;p&gt;Having a &lt;strong&gt;rollback strategy&lt;/strong&gt; for each component is non-negotiable. Feature toggles or dynamic routing make it easier to redirect traffic back to the old system if something goes wrong. This safety net is especially important during high-traffic periods when errors can have a greater impact.&lt;/p&gt;

&lt;p&gt;The Strangler Fig pattern is best suited for large, complex systems where its advantages outweigh the added operational demands. For smaller, simpler APIs, alternative versioning methods might be more cost-effective and easier to manage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: API Versioning with the Strangler Fig Pattern
&lt;/h2&gt;

&lt;p&gt;The Strangler Fig pattern offers a practical way to handle API versioning by gradually phasing out outdated functionalities without disrupting existing operations. As Martin Fowler puts it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Rather than replacing a system all at once, it's easier to build a new system &amp;gt; around the old, gradually replacing functionalities until the legacy system is &amp;gt; phased out."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This method relies on a facade to direct requests between the old and new services, ensuring everything continues to function smoothly during the transition.&lt;/p&gt;

&lt;p&gt;To make this process more efficient, having the right tools is critical. For instance, Zuplo supports this approach by enabling &lt;a href="https://zuplo.com/docs/articles/versioning-on-zuplo" rel="noopener noreferrer"&gt;separate OpenAPI files&lt;/a&gt; for each version, programmable routing through custom policies, and GitOps workflows with unlimited preview environments. These features ensure precise version control and allow for easy rollbacks when needed.&lt;/p&gt;

&lt;p&gt;For APIs with complex structures, this pattern aligns with key migration principles. The process hinges on careful planning, ensuring seamless request interception, reliable data synchronization, and ongoing monitoring.&lt;/p&gt;

&lt;p&gt;Whether you're moving from monolithic architectures to microservices or updating API contracts, the Strangler Fig pattern allows for steady progress while keeping users happy and systems stable. It provides a structured approach to API modernization that minimizes risk and maximizes efficiency.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How does the Strangler Fig pattern help ensure uninterrupted API migration?
&lt;/h3&gt;

&lt;p&gt;The Strangler Fig pattern offers a practical way to handle API migration by phasing out old components and introducing new ones gradually. This ensures that users can keep accessing the API without any disruptions during the transition.&lt;/p&gt;

&lt;p&gt;The process involves slowly redirecting traffic to the updated components while keeping the legacy system running. This approach reduces risks, preserves system stability, and guarantees a seamless experience for users throughout the migration.&lt;/p&gt;

&lt;h3&gt;
  
  
  What challenges can arise when using the Strangler Fig pattern for API versioning, and how can they be resolved?
&lt;/h3&gt;

&lt;p&gt;The Strangler Fig pattern comes with its fair share of challenges. These include maintaining &lt;strong&gt;data consistency&lt;/strong&gt; between the old and new systems, dealing with &lt;strong&gt;dependencies&lt;/strong&gt; across interconnected components, and managing the operational complexity of running both systems simultaneously during the transition.&lt;/p&gt;

&lt;p&gt;To tackle these hurdles, you can implement a &lt;strong&gt;facade or proxy&lt;/strong&gt; to efficiently route traffic between the legacy and updated APIs. Careful planning for data migration is crucial to prevent inconsistencies, and taking an incremental approach to updates can help reduce disruptions. This method keeps the system stable while ensuring a smoother experience for users during the transition.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does Zuplo support the Strangler Fig pattern for API versioning?
&lt;/h3&gt;

&lt;p&gt;Zuplo makes it easier to use the &lt;strong&gt;Strangler Fig pattern&lt;/strong&gt; by providing a robust API gateway that helps you smoothly transition from legacy APIs to new versions. With Zuplo, you can handle migrations without downtime, centrally manage multiple API versions, and direct traffic intelligently - all while keeping your current users unaffected.&lt;/p&gt;

&lt;p&gt;This method lets developers and API managers update systems step by step, ensuring stability and keeping users happy along the way.&lt;/p&gt;

</description>
      <category>api</category>
    </item>
    <item>
      <title>How to Access a REST API Through GraphQL</title>
      <dc:creator>Adrian Machado</dc:creator>
      <pubDate>Thu, 04 Sep 2025 06:14:05 +0000</pubDate>
      <link>https://dev.to/zuplo/how-to-access-a-rest-api-through-graphql-4f7f</link>
      <guid>https://dev.to/zuplo/how-to-access-a-rest-api-through-graphql-4f7f</guid>
      <description>&lt;p&gt;Here’s why combining &lt;a href="https://graphql.org/" rel="noopener noreferrer"&gt;GraphQL&lt;/a&gt; with REST is useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simplified Data Fetching&lt;/strong&gt;: Avoid over-fetching or under-fetching data
common in REST.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single Endpoint&lt;/strong&gt;: GraphQL consolidates multiple REST endpoints into one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema Control&lt;/strong&gt;: GraphQL schemas provide clear data structures and
relationships.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved Performance&lt;/strong&gt;: Tools like caching and batching reduce API call
overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Security&lt;/strong&gt;: Use features like authentication, rate limiting, and
input validation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Quick Setup Steps:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Define a GraphQL Schema&lt;/strong&gt;: Map REST API data into structured GraphQL types.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create Resolvers&lt;/strong&gt;: Connect GraphQL queries to REST endpoints.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimize Performance&lt;/strong&gt;: Use caching, batching, and rate limiting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure the API&lt;/strong&gt;: Implement authentication, authorization, and input
validation.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Comparison Table: REST vs. &lt;a href="https://graphql.org/" rel="noopener noreferrer"&gt;GraphQL&lt;/a&gt;
&lt;/h3&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%2Fkdp4tmc4heeglgrquef2.jpg" 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%2Fkdp4tmc4heeglgrquef2.jpg" alt="GraphQL" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;| &lt;strong&gt;Feature&lt;/strong&gt;                      | &lt;strong&gt;REST&lt;/strong&gt;                 | &lt;strong&gt;GraphQL&lt;/strong&gt;              | | -------------------------------- | ------------------------ | ------------------------ | | &lt;strong&gt;Endpoints&lt;/strong&gt;                    | Multiple endpoints       | Single endpoint          | | &lt;strong&gt;Data Fetching&lt;/strong&gt;                | Fixed data structure     | Flexible, client-defined | | &lt;strong&gt;Over-fetching/Under-fetching&lt;/strong&gt; | Common issues            | Avoided with queries     | | &lt;strong&gt;Performance&lt;/strong&gt;                  | Higher API call overhead | Optimized with batching  |&lt;/p&gt;

&lt;h2&gt;
  
  
  Video: Convert REST APIs to GraphQL in 3 simple steps
&lt;/h2&gt;

&lt;p&gt;We find video tutorial helpful, so in case you would prefer to watch/listen, here's a video from IBM that covers a lot of the same topics:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/T7QZEmug_Wc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Requirements
&lt;/h2&gt;

&lt;p&gt;When setting up a &lt;a href="https://zuplo.com/docs/articles/testing-graphql" rel="noopener noreferrer"&gt;GraphQL server&lt;/a&gt; to work with REST APIs, you'll need specific tools, a proper configuration, and robust security measures.&lt;/p&gt;

&lt;h3&gt;
  
  
  Required Tools
&lt;/h3&gt;

&lt;p&gt;To integrate REST APIs with GraphQL, you'll rely on a few key tools:&lt;/p&gt;

&lt;p&gt;| &lt;strong&gt;Tool&lt;/strong&gt;                                                              | &lt;strong&gt;Primary Function&lt;/strong&gt; | &lt;strong&gt;Key Feature&lt;/strong&gt;                        | | --------------------------------------------------------------------- | -------------------- | -------------------------------------- | | &lt;a href="https://www.apollographql.com/docs/apollo-server" rel="noopener noreferrer"&gt;&lt;strong&gt;Apollo Server&lt;/strong&gt;&lt;/a&gt; | GraphQL Server       | Built-in support for REST data sources | | &lt;a href="https://www.npmjs.com/package/express-graphql" rel="noopener noreferrer"&gt;&lt;strong&gt;Express-GraphQL&lt;/strong&gt;&lt;/a&gt;  | HTTP Middleware      | Easy integration with REST endpoints   | | &lt;strong&gt;GraphQL Schema Tools&lt;/strong&gt;                                              | Schema Definition    | Generates types from REST responses    | | &lt;strong&gt;Zuplo API Gateway&lt;/strong&gt;                                                 | Request Management   | Advanced caching and rate limiting     |&lt;/p&gt;

&lt;h3&gt;
  
  
  GraphQL Server Setup Steps
&lt;/h3&gt;

&lt;p&gt;To connect your GraphQL server with REST endpoints, tools like Apollo Server simplify the process. Here's an example using TypeScript to create a REST data source for a movie-related API:&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MoviesAPI&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;RESTDataSource&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;override&lt;/span&gt; &lt;span class="nx"&gt;baseURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://movies-api.example.com/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getMovie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Movie&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Movie&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`movies/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApolloServer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;typeDefs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;resolvers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;dataSources&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="na"&gt;moviesAPI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MoviesAPI&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example demonstrates how to define a dedicated REST data source, ensuring clear separation of logic and efficient data handling. Once your server is configured, securing it becomes the next priority.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security Setup
&lt;/h3&gt;

&lt;p&gt;Protecting your GraphQL server requires a multi-layered security approach:&lt;/p&gt;

&lt;p&gt;| &lt;strong&gt;Security Layer&lt;/strong&gt;   | &lt;strong&gt;Implementation&lt;/strong&gt;      | &lt;strong&gt;Purpose&lt;/strong&gt;                         | | -------------------- | ----------------------- | ----------------------------------- | | &lt;strong&gt;Authentication&lt;/strong&gt;   | JWT or OAuth2.0         | Verifies user identity              | | &lt;strong&gt;Authorization&lt;/strong&gt;    | Field-level permissions | Controls access to specific data    | | &lt;strong&gt;Rate Limiting&lt;/strong&gt;    | Request throttling      | Prevents denial-of-service attacks  | | &lt;strong&gt;Input Validation&lt;/strong&gt; | Schema checks           | Guards against malicious injections |&lt;/p&gt;

&lt;p&gt;For production environments, consider additional safeguards: mask error messages, disable introspection, and set timeouts for REST calls. These measures enhance both stability and security.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating GraphQL Schemas from REST
&lt;/h2&gt;

&lt;p&gt;Learn how to map REST responses to GraphQL types and effectively manage errors during the process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Schema Definition
&lt;/h3&gt;

&lt;p&gt;The following GraphQL schema transforms REST API responses into structured GraphQL types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Movie&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="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;releaseDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;director&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Director&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;ratings&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="n"&gt;Rating&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Director&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="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;biography&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Rating&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="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RatingSource&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Float&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="k"&gt;enum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RatingSource&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="n"&gt;IMDB&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;ROTTEN_TOMATOES&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;METACRITIC&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="k"&gt;union&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MovieResult&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="n"&gt;Movie&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="n"&gt;MovieError&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MovieError&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="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&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;This schema simplifies complex REST responses by organizing them into GraphQL types, making it easier to query and manage relationships between entities.&lt;/p&gt;

&lt;h3&gt;
  
  
  REST Resolver Creation
&lt;/h3&gt;

&lt;p&gt;Resolvers serve as the connection between GraphQL queries and REST endpoints. Below is an example implementation using Apollo Server's &lt;code&gt;RESTDataSource&lt;/code&gt;:&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MoviesAPI&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;RESTDataSource&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.movies.example/v1/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getMovie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;movie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`movies/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;director&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDirector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;directorId&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;catch &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="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GraphQLError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to fetch movie data&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;extensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;REST_ERROR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="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;Here's a quick breakdown of resolver types and their best practices:&lt;/p&gt;

&lt;p&gt;| Resolver Type | Purpose              | Best Practice                                                                        | | ------------- | -------------------- | ------------------------------------------------------------------------------------ | | Query         | Fetches data         | Keep resolvers lightweight; delegate to data sources                                 | | Field         | Resolves nested data | Use &lt;a href="https://github.com/graphql/dataloader" rel="noopener noreferrer"&gt;DataLoader&lt;/a&gt; for batching and performance | | Mutation      | Modifies data        | Validate inputs before making REST API calls                                         |&lt;/p&gt;

&lt;h3&gt;
  
  
  Error Handling
&lt;/h3&gt;

&lt;p&gt;Managing errors in GraphQL differs significantly from traditional REST APIs. Proper error handling ensures smoother debugging and better user experience. Here's how you can handle errors in a query resolver:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resolvers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dataSources&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;movie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dataSources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;moviesAPI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMovie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&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="na"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Movie&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;movie&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;catch &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="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="na"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MovieError&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;code&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;extensions&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UNKNOWN_ERROR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;message&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="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key steps for effective error handling:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Explicit error types:&lt;/strong&gt; Define specific error types in your schema to make
issues easy to identify.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared interfaces:&lt;/strong&gt; Use GraphQL interfaces for common properties across
error types.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logging and monitoring:&lt;/strong&gt; Implement robust error logging to track issues
effectively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error transformation:&lt;/strong&gt; Convert REST API errors into formats compatible with
GraphQL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also configure error policies to control how errors are handled during queries:&lt;/p&gt;

&lt;p&gt;| Policy   | Use Case                | Behavior                                  | | -------- | ----------------------- | ----------------------------------------- | | &lt;code&gt;none&lt;/code&gt;   | Default behavior        | Returns &lt;code&gt;undefined&lt;/code&gt; for data on errors    | | &lt;code&gt;ignore&lt;/code&gt; | Partial data acceptance | Ignores errors and returns available data | | &lt;code&gt;all&lt;/code&gt;    | Debugging               | Returns both errors and partial data      |&lt;/p&gt;

&lt;p&gt;&lt;a href="https://zuplo.com?utm_source=dev.to&amp;amp;utm_medium=inline-cta"&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%2Fiikcuf7axhu4gkw0jmqa.png" width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance Improvements
&lt;/h2&gt;

&lt;p&gt;Integrating GraphQL and REST efficiently requires careful attention to performance. By batching requests, managing rate limits, and leveraging monitoring tools, you can significantly improve the responsiveness and scalability of your system. These strategies build on established security and schema practices to deliver a smoother experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Request Optimization
&lt;/h3&gt;

&lt;p&gt;One way to streamline performance is by batching REST requests using tools like &lt;strong&gt;DataLoader&lt;/strong&gt;. Here's an example:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;batchLoadMovies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ids&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;movies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;restClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/movies/batch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ids&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;movies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&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;movieLoader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;batchLoadMovies&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As Raja Chattopadhyay explains:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"With GraphQL, the client can request exactly how much data it needs, making &amp;gt; sure the application API calls are optimized. This helps significantly to &amp;gt; improve overall performance and reduce underlying network and storage costs".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By reducing the overhead of individual API calls, you can also manage the overall request volume more effectively using rate limiting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rate Limit Management
&lt;/h3&gt;

&lt;p&gt;To balance throughput and latency, implement tiered rate limits. For example:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryComplexityRule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;maxCost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;listLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;nestedLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="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;rateLimiter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TokenBucketRateLimiter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;tokensPerInterval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hour&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Authenticated users can make up to 5,000 requests per hour, while unauthenticated users are capped at 60 requests per hour. These limits help maintain system stability and ensure fair usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://zuplo.com/" rel="noopener noreferrer"&gt;Zuplo&lt;/a&gt; Performance Optimizations
&lt;/h3&gt;

&lt;p&gt;The Zuplo API Gateway provides several features to enhance performance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://zuplo.com/docs/policies/complex-rate-limit-inbound" rel="noopener noreferrer"&gt;&lt;strong&gt;Advanced Rate Limiting&lt;/strong&gt;&lt;/a&gt;:
Set limits based on request volume, payload size, query complexity, and authentication status.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Monitoring&lt;/strong&gt;: Use real-time analytics to track latency, error
rates, and query patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edge Optimization&lt;/strong&gt;: Deploy GraphQL gateways closer to users to reduce
latency.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Additional Optimizations
&lt;/h3&gt;

&lt;p&gt;To further improve performance, consider caching frequently accessed data and limiting query depth:&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="c1"&gt;// Cache common data&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InMemoryLRUCache&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;maxSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&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;300000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 5 minutes&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Limit query depth&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;depthLimit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createComplexityLimit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;maxDepth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;onLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;complexity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Query complexity (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;complexity&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) exceeds limit`&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;Platforms handling thousands of requests per second benefit greatly from these techniques. By implementing these optimizations, you're setting the stage for a highly efficient production environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment and Monitoring
&lt;/h2&gt;

&lt;p&gt;Deploying and monitoring your GraphQL-REST integration effectively involves a structured production setup and reliable tracking tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Production Setup
&lt;/h3&gt;

&lt;p&gt;To ensure consistent deployment, containerize your application with &lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV=production&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm", "start"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Automate the deployment process using &lt;a href="https://zuplo.com/docs/articles/custom-ci-cd" rel="noopener noreferrer"&gt;CI/CD pipelines&lt;/a&gt; for efficiency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and test&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;npm ci&lt;/span&gt;
        &lt;span class="s"&gt;npm test&lt;/span&gt;
        &lt;span class="s"&gt;rover subgraph check&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;docker build -t graphql-gateway .&lt;/span&gt;
        &lt;span class="s"&gt;docker push graphql-gateway&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once deployed, focus on monitoring key performance indicators to ensure smooth operation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Usage Tracking
&lt;/h3&gt;

&lt;p&gt;Zuplo's dashboard offers tools to track your API's health and performance. Key metrics to monitor include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Request volume and latency&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Error rates by endpoint&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Query complexity scores&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authentication status&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rate limit usage&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep an eye on error rates and request patterns to proactively address potential issues. For distributed tracing, &lt;a href="https://opentelemetry.io/docs/" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt; can be a powerful tool:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tracer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;opentelemetry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTracer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;graphql-gateway&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;span&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resolveMovie&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&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;fetchMovieFromRest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttributes&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;movie.id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;span&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;In tandem with monitoring, handle schema updates carefully to ensure backward compatibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  Schema Updates
&lt;/h3&gt;

&lt;p&gt;Updating your schema requires a systematic approach to avoid disruptions. Here’s how you can manage it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Add Deprecation Notices&lt;/strong&gt;
Mark outdated fields as deprecated to guide developers toward newer alternatives:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Movie&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="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;rating&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;deprecated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reason&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="n"&gt;Use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;reviewScore&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instead&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;reviewScore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Float&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;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Track Field Usage&lt;/strong&gt;&lt;br&gt;
Use tools like Zuplo's analytics to monitor the usage of deprecated fields and identify impacted clients:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Coordinate Updates&lt;/strong&gt;&lt;br&gt;
Plan schema changes during off-peak hours and communicate updates clearly via your developer portal. Allow at least 30 days for clients to adapt to breaking changes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;"GraphQL enables frontend developers or consumers of APIs to request the exact &amp;gt; data that they need, with no over-fetching or under-fetching." – Selvaganesh&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Zuplo also offers additional features to enhance your deployment and monitoring processes, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blue-green deployment support&lt;/li&gt;
&lt;li&gt;Automated schema validation&lt;/li&gt;
&lt;li&gt;Real-time performance monitoring&lt;/li&gt;
&lt;li&gt;Custom domain configuration&lt;/li&gt;
&lt;li&gt;Edge deployment options&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;GraphQL brings a new level of precision and efficiency to working with REST APIs, making data queries more straightforward and effective. Here's a quick recap of the main takeaways and actionable steps to get started.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Points
&lt;/h3&gt;

&lt;p&gt;GraphQL serves as a powerful query layer for REST APIs, offering several clear benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Efficient Data Fetching&lt;/strong&gt;: Clients can request only the data they need,
avoiding the common REST pitfalls of over-fetching or under-fetching.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved Developer Workflow&lt;/strong&gt;: A strong type system supports features like
auto-completion and real-time query validation, making development smoother.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streamlined Architecture&lt;/strong&gt;: Instead of juggling multiple REST endpoints,
GraphQL consolidates access through a single endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better Performance&lt;/strong&gt;: By cutting down on unnecessary data transfer, GraphQL
boosts application performance - especially important for mobile apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using these advantages as a foundation, you can start integrating GraphQL to enhance your API workflows and see immediate improvements in efficiency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;Here’s a step-by-step guide to implementing GraphQL alongside your REST APIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Set Up the Environment&lt;/strong&gt;: Install &lt;a href="https://nodejs.org/en" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; and
GraphQL-related packages to prepare your development environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design the Schema&lt;/strong&gt;: Map out a schema that mirrors the structure of your
REST API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement Resolvers&lt;/strong&gt;: Create resolvers to connect GraphQL queries to your
REST endpoints.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor and Optimize&lt;/strong&gt;: Use tools like those from Zuplo to track
&lt;a href="https://zuplo.com/docs/articles/metrics-plugins" rel="noopener noreferrer"&gt;essential metrics&lt;/a&gt; and refine performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Key metrics to monitor include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request volume and latency&lt;/li&gt;
&lt;li&gt;Error rates&lt;/li&gt;
&lt;li&gt;Query complexity&lt;/li&gt;
&lt;li&gt;Authentication status&lt;/li&gt;
&lt;li&gt;Rate limit usage&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How does integrating GraphQL with REST APIs improve performance?
&lt;/h3&gt;

&lt;p&gt;Integrating GraphQL with REST APIs can significantly boost performance by cutting down on the number of requests needed to fetch data. With REST, gathering related information often means hitting multiple endpoints. In contrast, GraphQL lets you pull all the data you need in a single query. This approach avoids &lt;strong&gt;over-fetching&lt;/strong&gt; (getting more data than you need) and &lt;strong&gt;under-fetching&lt;/strong&gt; (not getting enough data), as you can specify exactly what to request.&lt;/p&gt;

&lt;p&gt;On top of that, GraphQL supports &lt;a href="https://zuplo.com/docs/policies/caching-inbound" rel="noopener noreferrer"&gt;smart caching strategies&lt;/a&gt;. By using both client-side and server-side caching, it reduces redundant data transfers, making API interactions faster and more efficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are the best security practices for setting up a GraphQL server with REST API integration?
&lt;/h3&gt;

&lt;p&gt;To keep your GraphQL server secure when working with REST APIs, start by focusing on &lt;strong&gt;authentication&lt;/strong&gt; and &lt;strong&gt;authorization&lt;/strong&gt;. Using tools like &lt;a href="https://zuplo.com/blog/tags/JWT-API-Authentication" rel="noopener noreferrer"&gt;JSON Web Tokens&lt;/a&gt; (JWTs) can help manage user access, ensuring that only the right users can execute specific queries or mutations.&lt;/p&gt;

&lt;p&gt;To guard against &lt;strong&gt;Denial of Service (DoS)&lt;/strong&gt; attacks, set limits on query depth and complexity, establish timeouts, and validate all incoming data to block injection attempts. Regular security checks, like audits and penetration testing, are also essential for spotting and fixing vulnerabilities.&lt;/p&gt;

&lt;p&gt;These measures help protect your GraphQL server while ensuring smooth integration with REST APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  How can developers effectively handle errors when querying REST APIs through GraphQL?
&lt;/h3&gt;

&lt;p&gt;When working with GraphQL to query REST APIs, handling errors effectively requires a focus on structured error responses. Since GraphQL always returns a &lt;strong&gt;200 status code&lt;/strong&gt; - even when something goes wrong - errors are included in the &lt;code&gt;errors&lt;/code&gt; array within the response. This array gives crucial details, such as the type of error and where it occurred in the query. This makes it easier to differentiate between &lt;strong&gt;syntax errors&lt;/strong&gt; (like invalid queries) and &lt;strong&gt;execution errors&lt;/strong&gt; (such as REST API call failures).&lt;/p&gt;

&lt;p&gt;To enhance error handling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write &lt;strong&gt;clear and user-friendly error messages&lt;/strong&gt; that don't reveal sensitive
details.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;custom error codes&lt;/strong&gt; to provide precise feedback and allow for tailored
error responses.&lt;/li&gt;
&lt;li&gt;Rely on &lt;strong&gt;logging and monitoring tools&lt;/strong&gt; to track, analyze, and debug errors
effectively.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By adopting these strategies, developers can ensure a smoother user experience while maintaining strong error management practices.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>tutorial</category>
      <category>api</category>
    </item>
  </channel>
</rss>
