Deprecating an API is never is easy (in fact we wrote a
full guide on API deprecation) - but
many popular Node JS frameworks can ease the pain. Deprecating an API endpoint
typically involves notifying consumers that the endpoint is outdated and will be
removed in the future. As a developer, you can communicate this two ways
- Through API documentation (an OpenAPI spec generated from your framework of
choice).
- By sending the
HTTP
Deprecation header.
Here’s how different Node.js frameworks handle API deprecation:
1. Fastify
Support Level: Built-in Support via Plugins
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.
Fastify API Deprecation Example
Generate an OpenAPI file using fastify-swagger, and use the deprecated
property to mark the API endpoint as deprecated . Additionally, send the
deprecation header back in the route’s implementation.
const fastify = require("fastify")();
fastify.register(require("fastify-swagger"), {
exposeRoute: true,
routePrefix: "/documentation",
openapi: {
openapi: "3.0.0",
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) => {
// Friday, June 30, 2023 at 23:59:59 UTC in Unix time
reply.header("Deprecation", "@1688169599");
return { message: "This endpoint is deprecated." };
},
);
fastify.listen(3000, (err) => {
if (err) throw err;
console.log("Server running on port 3000");
});
2. Express JS
Support Level: Manual Implementation
Express.js doesn’t have built-in support for deprecating APIs, but you can
implement deprecation logic manually.
-
Adding the header at the route level:
You can add the header directly in your route’s response.
const express = require("express");
const app = express();
app.get("/v1/old-endpoint", (req, res) => {
res.set("Deprecation", "@1688169599");
res.send({ message: "This endpoint is deprecated." });
});
app.listen(3000, () => console.log("Server running on port 3000"));
-
Using Middleware:
If you are deprecating multiple endpoints, you can use middleware to maximize
code reuse.
function deprecationMiddleware(req, res, next) {
res.set("Deprecation", "@1688169599");
next();
}
app.use("/v1/old-endpoint", deprecationMiddleware, (req, res) => {
res.send({ message: "This endpoint is deprecated." });
});
app.use("/v1/another-old-endpoint", deprecationMiddleware, (req, res) => {
res.send({ message: "This endpoint is deprecated." });
});
Express JS OpenAPI Deprecation Example
You can use the swagger-jsdoc package
to generate an OpenAPI file from your routes, and mark them deprecated. You can
additionally use
swagger-ui-express to serve
your docs.
const express = require("express");
const swaggerJsdoc = require("swagger-jsdoc");
const swaggerUi = require("swagger-ui-express");
const app = express();
const swaggerDefinition = {
openapi: "3.0.0",
info: {
title: "API Documentation",
version: "1.0.0",
},
};
const options = {
swaggerDefinition,
apis: ["./index.js"], // Path to the API docs
};
const swaggerSpec = swaggerJsdoc(options);
app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec));
// In index.js
/**
* @openapi
* /v1/old-endpoint:
* get:
* deprecated: true
* summary: Deprecated endpoint
* description: This endpoint is deprecated and will be removed in the future.
* responses:
* 200:
* description: Successful response
*/
app.get("/v1/old-endpoint", (req, res) => {
res.set("Deprecation", "@1688169599");
res.send({ message: "This endpoint is deprecated." });
});
app.listen(3000, () => console.log("Server running on port 3000"));
3. NestJS
Support Level: Built-in Support via Decorators
NestJS is a progressive Node.js framework that uses decorators and metadata,
making it easy to mark endpoints as deprecated. With the
@nestjs/swagger package, you
can integrate Swagger/OpenAPI and use the @ApiOperation() decorator.
NestJS API Deprecation Example
Set the @ApiOperation({ deprecated: true }) directive to handle updating your
OpenAPI docs and the @Header("Deprecation", "@1688169599") directive will
return the deprecation header for you.
import { Controller, Get, Header } from "@nestjs/common";
import { ApiOperation, ApiTags } from "@nestjs/swagger";
@ApiTags("API")
@Controller("api")
export class ApiController {
@Get("v1/old-endpoint")
@ApiOperation({ deprecated: true })
@Header("Deprecation", "@1688169599")
getOldEndpoint() {
return { message: "This endpoint is deprecated." };
}
}
4. Hapi.js
Support Level: Built-in Support via Route Options
Hapi.js allows you to set route-specific metadata, including marking routes as
deprecated, especially when using the
hapi-swagger plugin for
documentation.
Hapi.js API Deprecation Example
Simply use the top-level deprecated property to update your OpenAPI, and set
the deprecation header in your route’s response.
const Hapi = require("@hapi/hapi");
const Inert = require("@hapi/inert");
const Vision = require("@hapi/vision");
const HapiSwagger = require("hapi-swagger");
const server = Hapi.server({
port: 3000,
host: "localhost",
});
const init = async () => {
await server.register([
Inert,
Vision,
{
plugin: HapiSwagger,
options: {
info: {
title: "API Documentation",
version: "1.0.0",
},
},
},
]);
server.route({
method: "GET",
path: "/v1/old-endpoint",
options: {
description: "Deprecated endpoint",
notes: "This endpoint is deprecated and will be removed in the future.",
tags: ["api", "deprecated"],
plugins: {
"hapi-swagger": {
deprecated: true,
},
},
handler: (request, h) => {
const response = h.response({
message: "This endpoint is deprecated.",
});
response.header("Deprecation", "@1688169599");
return response;
},
},
});
await server.start();
console.log("Server running on %s", server.info.uri);
};
init();
5. Restify
Support Level: Manual Implementation
Restify is focused on building RESTful APIs and allows for middleware and custom
headers, but doesn’t have built-in deprecation support. As far as I can tell,
there isn’t a widely accepted OpenAPI/Swagger generator either.
Restify API Deprecation Example
This is pretty obvious, but simply return the deprecation header via
res.header.
const restify = require("restify");
const server = restify.createServer();
server.get("/v1/old-endpoint", (req, res, next) => {
res.header("Deprecation", "@1688169599");
res.send({ message: "This endpoint is deprecated." });
next();
});
server.listen(3000, () => {
console.log("%s listening at %s", server.name, server.url);
});
6. Koa.js
Support Level: Manual Implementation
Koa.js allows you to set response headers and create middleware but doesn’t have
built-in support for deprecation. I scoured NPM but couldn’t find any modules
for OpenAPI generation from Koa - but I did find 2 solutions that might work for
you.
koa-swagger-decorator:
A library that only supports OpenAPI 2.0 (which does have a deprecated
property)
- Use Tsoa over your Koa API: You can read
this awesome guide
Koa.js API Deprecation Example
In case you don’t want/care about OpenAPI support, you can just send the
deprecation header back.
const Koa = require("koa");
const app = new Koa();
app.use(async (ctx, next) => {
if (ctx.path === "/v1/old-endpoint") {
ctx.set("Deprecation", "@1688169599");
ctx.body = { message: "This endpoint is deprecated." };
} else {
await next();
}
});
app.listen(3000, () => {
console.log("Server running on port 3000");
});
Takeaways
Deprecating API endpoints requires a surprising amount of manual effort in most
Node JS frameworks - and deprecating an entire API is tedious in all frameworks.
Here’s my recommendations:
- Utilizing Middleware: Utilize middleware to set deprecation headers
consistently. This seems to be the only way to “deprecate” an entire API in
most frameworks.
- Consider Frameworks with OpenAPI Support: It might be time to ditch legacy
frameworks that lack OpenAPI support - your users will thank you. The irony is
that you won’t have an easy way to deprecate these APIs as you migrate!