Deprecating REST APIs: A Developer's Guide

All good things eventually come to an end - and sometimes bad things like our abhorrent API design choices do too. Unfortunately for us API devs, you can't always delete your old endpoint and start over - you need to have a deprecation strategy in place so your API consumers aren't caught unaware.

API deprecation is a critical aspect of API lifecycle management that ensures your services remain robust, secure, and maintainable. In this article, we'll delve into what it means to deprecate an API, why it's necessary, and how to do it effectively. Most other articles just cover concepts - but we'll get our hands dirty with code to show you how to deprecate your APIs.

What Does It Mean to Deprecate an API#

Deprecating an API refers to the process of signaling to developers that a particular API, or a part of it (like an endpoint or field), is scheduled to be discontinued or replaced. Deprecation is a formal way to communicate that while the API is still functional, it should no longer be used, and alternative solutions should be adopted. Unless the API is being totally phased out, the API provider will have a new version of the API ready to be adopted. Check out our article on API versioning to learn more.

Why You Should Deprecate an API#

Deprecating APIs is essential for maintaining a clean, efficient, and secure system. Here are some reasons why you might deprecate an API:

  • Security Enhancements: Older APIs may have vulnerabilities that are addressed in newer versions.
  • Performance Improvements: Newer APIs might offer better performance.
  • Feature Upgrades: Introducing new features that aren't compatible with older versions.
  • Maintenance Overhead: Reducing the burden of supporting outdated APIs.
  • Regulatory Compliance: Ensuring APIs meet current legal and compliance standards.

Phases of API Deprecation#

Before we can get into the process of actually deprecating an API, here's a quick overview of phases of API deprecation - just so we are clear on terminology going forward.

Phases of API Deprecation

API deprecation typically follows the following timeline:

  1. Deprecation Planning: The API is still being actively used, but for the reasons outlined above, you've decided to decommission it.
  2. Deprecation Announcement: The API's deprecation date has been announced, both through a deprecation notice provided to users and through the API itself (more on this later). The period between this announcement and the deprecation date is a grace period in which users can start migrating to one of the provided alternative options.
  3. Deprecation Day: The day has finally arrived to deprecate the API. Crucially, the API must remain functional to allow time for users to migrate. This is also a good time to reach out to users again and let them know the sunset date.
  4. Sunset Day: You've hopefully migrated the last of your users away from the deprecated API at this point. You can begin deletion of the old API. It's usually good practice to leave behind the API documentation (users might have it bookmarked), but update it to reflect the sunset status.

Key Terms#

  • Deprecation Notice: A warning provided to users about the upcoming discontinuation.
  • Grace Period: A timeframe during which the deprecated API remains operational before being retired.
  • Alternative Options: Information about newer versions or other APIs that should be used instead.

Deprecation vs. Sunsetting an API#

While often used interchangeably, deprecation and sunsetting (aka decommissioning or retiring) an API are distinct phases in an API's lifecycle.

  • Deprecation: The period where users are warned that an API will be discontinued in the future. The API remains functional but is no longer recommended for use.
  • Sunsetting: The point at which the API is no longer available. Users must have migrated to newer versions or alternatives.

Technical Considerations for Deprecating an API#

When deprecating an API (and planning the replacement), consider the following technical aspects:

  • Monitoring Usage: Track how often the API you want to deprecate is used to gauge the impact of deprecation.
  • Versioning Strategy: Implement a versioning strategy to make the transition smoother..
  • Backwards Compatibility: Make it easy to migrate from the old endpoint to the new one. A radically different API will make users reluctant to migrate, and harder for you to sunset the API.
  • Documentation Updates: Keep all API documentation up-to-date with deprecation notices. The best place to typically do this is within your OpenAPI document (see how below).

Types of API Deprecation#

Deprecation doesn't always refer to an entire API - in fact you can actually deprecate endpoints and even features of endpoints. This avoids having to create an entirely new version of your API - moving clients off old versions of your API can be quite challenging. Note that you will likely follow the same steps as above, but potentially less rigorously (ex. deprecating a parameter might be announced in a general patch note).

Given the prevalence of OpenAPI, and its use in API documentation platforms - here are examples of how to perform different types of deprecations, which most OpenAPI tooling should handle. One unfortunate missing feature in OpenAPI 3.1 is that there isn't a formal way to guide users to the latest version of your API - despite a spec being proposed years ago, so we'll have to hack that into the description.

How to Deprecate an API Endpoint#

Deprecating an API endpoint involves updating your API documentation and specifications to indicate the deprecation.

paths:
  /v1/old-endpoint:
    get:
      deprecated: true
      summary: "Deprecated endpoint for retrieving user data"
      description:
        "This endpoint is deprecated and will be removed on YYYY-MM-DD. Use
        /v2/new-endpoint instead."
      responses:
        "200":
          description: Successful response
  1. Set deprecated: true: This signals that the endpoint is deprecated.
  2. Update summary and description: Include a clear message about the deprecation and alternatives.
  3. Provide a Timeline: You can use and document the deprecation header

How to Deprecate an Entire API Version#

I don't believe there is a canonical way of marking an entire API version as being deprecated in OpenAPI - so I'll just make one up.

openapi: 3.0.0
info:
  version: "1.0.0"
  title: "Deprecated API Version"
  description:
    "Version 1.0.0 is deprecated and will be retired on YYYY-MM-DD. Please
    upgrade to version 2.0.0."
  x-deprecation: "2023-11-11T23:59:59Z"
paths:
  /v1/endpoint:
    get:
      summary: "Endpoint in deprecated API version"
      responses:
        "200":
          description: Successful response
  1. Add x-deprecated: <Date> in the info section: Custom extension to indicate the date the API will be deprecated. This can be useful for API client generators to take advantage of to warn users.
  2. Update description: Clearly state the deprecation status and guide users to the new version.

How to Deprecate an API Field#

Sometimes, only a specific field within an API response or request needs to be deprecated.

components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: string
        username:
          type: string
        fullName:
          type: string
          deprecated: true
          description:
            "This field is deprecated and will be removed on YYYY-MM-DD. Use
            'firstName' and 'lastName' instead."
        firstName:
          type: string
        lastName:
          type: string
  1. Set deprecated: true on the field: Indicates that the field should no longer be used.
  2. Update Field Description: Provide details about the deprecation and alternatives.
  3. Maintain Backward Compatibility: Keep the field operational during the deprecation period.

The HTTP Deprecation Header#

The HTTP Deprecation header is used to inform clients that a resource is deprecated. We have a full guide to this header, but here's a quick overview:

How It Works#

According to the latest draft proposal, the field should be an RFC 9651 Date, which is expressed in Unix time. The date can either be in the past (the API has already been deprecated) or can be in the future (the API will be deprecated at this date). Here's an example for an API that was deprecated Friday, June 30, 2023 at 23:59:59 UTC:

Deprecation: @1688169599

In addition to updating your docs via OpenAPI - the deprecation header provides a runtime warning to consumers of your API. This can be more useful than updating your OpenAPI given that API consumers usually don't check your docs after they are done integrating.

Implementation is also pretty straight-forward:

const express = require("express");
const app = express();
 
app.get("/v1/old-endpoint", (req, res) => {
  const deprecationDate = new Date().now();
  res.set("Deprecation", deprecationDate);
  res.json({ message: "This endpoint is deprecated." });
});
 
app.listen(3000, () => console.log("Server running on port 3000"));

Framework Support for API Deprecation#

Many API frameworks have some support for API deprecation - typically only covering deprecating individual endpoints. Given that many frameworks generate OpenAPI specs for you, they often provide annotations that hook into the generated spec.

Fastify (Javascript/Typescript)#

There are many API frameworks for JS/TS, but Fastify is one of our favorites. Fastify allows you to set custom metadata for routes, and when combined with the fastify-swagger plugin, you can mark routes as deprecated in your OpenAPI documentation.

const fastify = require("fastify")();
fastify.register(require("fastify-swagger"), {
  exposeRoute: true,
  routePrefix: "/documentation",
  swagger: {
    info: {
      title: "API Documentation",
      version: "1.0.0",
    },
  },
});
 
fastify.get(
  "/v1/old-endpoint",
  {
    schema: {
      deprecated: true,
      summary: "Deprecated endpoint",
      description:
        "This endpoint is deprecated and will be removed in the future.",
      response: {
        200: {
          type: "object",
          properties: {
            message: { type: "string" },
          },
        },
      },
    },
  },
  async (request, reply) => {
    reply.header("Deprecation", "true");
    return { message: "This endpoint is deprecated." };
  },
);
 
fastify.listen(3000, (err) => {
  if (err) throw err;
  console.log("Server running on port 3000");
});

If you'd like to see more Node framework examples - check out 6 more framework examples we created.

Spring (Java)#

Spring Framework recognizes the @Deprecated annotation at the method or class level. When combined with Swagger integration tools like Springfox or SpringDoc, deprecated APIs are automatically documented as such.

@RestController
public class UserController {
 
    @Deprecated
    @GetMapping("/v1/old-endpoint")
    public ResponseEntity<String> oldEndpoint() {
        return ResponseEntity.ok("This endpoint is deprecated.");
    }
 
    @GetMapping("/v2/new-endpoint")
    public ResponseEntity<String> newEndpoint() {
        return ResponseEntity.ok("This is the new endpoint.");
    }
}

.Net#

Use the [Obsolete] attribute to mark controllers or action methods as deprecated. Tools like Swashbuckle (for OpenAPI integration) recognize this attribute and update the API documentation accordingly.

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    [Obsolete("This endpoint is deprecated. Use GET /v2/some-endpoint instead.", false)]
    [HttpGet("v1/some-endpoint")]
    public IActionResult OldEndpoint()
    {
        return Ok("This endpoint is deprecated.");
    }
 
    [HttpGet("v2/some-endpoint")]
    public IActionResult NewEndpoint()
    {
        return Ok("This is the new endpoint.");
    }
}

Django Rest Framework (Python)#

DRF supports deprecation through third-party packages like drf-yasg for Swagger documentation, which recognizes the deprecated parameter.

from rest_framework.decorators import api_view
from rest_framework.response import Response
from drf_yasg.utils import swagger_auto_schema
 
@swagger_auto_schema(method='get', deprecated=True)
@api_view(['GET'])
def old_endpoint(request):
    return Response({'message': 'This endpoint is deprecated.'})

What if I Use an API Management Tool?#

If you're using an API management tool or a managed API gateway - then the platform likely includes some sort of deprecation functionality. Here's some common tools and how to deprecate an API with them

Deprecate a Zuplo API#

Zuplo is natively powered by OpenAPI and is also fully-programmable using Typescript. That means the OpenAPI deprecation examples and deprecation header implementation from above are fully compatible with Zuplo! Your auto-generated developer portal will be automatically updated to reflect the deprecation.

Deprecate a Kong API#

Kong supports adding custom headers through plugins.

plugins:
  - name: response-transformer
    config:
      add:
        headers:
          - "Deprecation: @1688169599"

Deprecate an Apigee API#

Apigee allows you to set headers via policies. I apologize in advance for the XML.

<AssignMessage async="false" continueOnError="false" enabled="true" name="AddDeprecationHeader">
    <Add>
        <Headers>
            <Header name="Deprecation">@1688169599</Header>
        </Headers>
    </Add>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</AssignMessage>

Examples of Successful API Deprecations#

Twitter API v1 to v2 Transition#

Twitter successfully deprecated its v1 API in favor of v2, providing extensive documentation, developer support, and a long deprecation timeline to allow developers to transition smoothly.

Key Strategies:

  • Comprehensive Communication: Regular updates via blogs, emails, and developer forums.
  • Extended Support Period: Provided ample time for developers to migrate.
  • Robust Documentation: Detailed guides and tutorials for the new API.

Lessons from API Deprecation Failures#

Google Reader API Shutdown#

When Google retired the Google Reader service, many applications relying on its API were left without alternatives, leading to significant disruption.

What Went Wrong:

  • Short Notice: The deprecation and shutdown happened rapidly.
  • Lack of Alternatives: No direct replacement or migration path provided.
  • Insufficient Communication: Users and developers felt blindsided by the decision.

Takeaways:

  • Provide Adequate Time: Always give users sufficient time to adapt.
  • Offer Alternatives: Guide users to other services or APIs.
  • Maintain Open Communication: Keep the developer community informed at every step.

Conclusion#

API deprecation is an inevitable part of evolving software services. By following best practices—clear communication, providing alternatives, and updating documentation — you can ensure a smooth transition for your users. Remember to leverage tools like the HTTP Deprecation header and update your OpenAPI specifications to reflect the changes accurately.

Questions? Let's chatOPEN DISCORD
0members online

Designed for Developers, Made for the Edge