API Linter logo

Backstage API Linter Plugin

Created by Zalando

API Linter is a quality tool for OpenAPI. It checks your API specs against Zalando REST guidelines through Zally. The Backstage plugin brings that linting into your portal so engineers can validate specs where they already work. It fits teams that want consistent APIs and clear feedback without extra tooling hops.

The plugin lets you load an OpenAPI document from a URL or paste it into the page. It runs checks and returns violations with rule names, severity, and the exact path in the spec. You can spot issues early, share results in reviews, and track improvements over time. It supports OpenAPI 2 and 3 through the Zally backend. That keeps the focus on design quality rather than manual guideline checks.

Zalando uses this plugin inside its developer portal and shared it with the community. If your goal is to standardize APIs across many teams, this plugin helps you scale that practice inside Backstage with clear results and a familiar workflow.

Installation Instructions

These instructions apply to self-hosted Backstage only.

Install the frontend package

Copy
cd packages/app
yarn add backstage-plugin-api-linter

Configure the proxy to your Zally server

Add this to your app config. Use your Zally base URL for target.

Copy
# app-config.yaml
proxy:
  "/api-linter":
    target: https://your.zally.instance
    allowedHeaders: ["Authorization"]

Wire the proxy in the Backstage backend

New backend system

Add the proxy backend package.

Copy
cd packages/backend
yarn add @backstage/plugin-proxy-backend

Register the proxy backend in the backend entry file.

Copy
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';

const backend = createBackend();

// other plugins
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(import('@backstage/plugin-auth-backend'));

// proxy backend
backend.add(import('@backstage/plugin-proxy-backend'));

backend.start();

Old backend system

Add the proxy backend package.

Copy
cd packages/backend
yarn add @backstage/plugin-proxy-backend

Create the proxy plugin file.

Copy
// packages/backend/src/plugins/proxy.ts
import { createRouter } from '@backstage/plugin-proxy-backend';
import { PluginEnvironment } from '../types';
import type { Router } from 'express';

export default async function createPlugin(env: PluginEnvironment): Promise<Router> {
  return await createRouter({
    logger: env.logger,
    config: env.config,
  });
}

Wire it in the backend index file.

Copy
// packages/backend/src/index.ts
import proxy from './plugins/proxy';

// inside main bootstrap
const proxyEnv = useHotMemoize(module, () => createEnv('proxy'));
apiRouter.use('/proxy', await proxy(proxyEnv));

Add the UI to your app

Export the plugin in your app module.

Copy
// packages/app/src/App.tsx
export { APILinterPlugin } from 'backstage-plugin-api-linter';

Import the component where you render routes.

Copy
// packages/app/src/App.tsx or a routed page file
import React from 'react';
import { APILinter } from 'backstage-plugin-api-linter';

Use with TabbedLayout

Copy
// inside your page component
import { TabbedLayout, Content } from '@backstage/core-components';

export const MyPage = () => (
  <TabbedLayout>
    <TabbedLayout.Route path="/" title="Home">
      <Content>Home content</Content>
    </TabbedLayout.Route>
    <TabbedLayout.Route path="/linter" title="Linter" data-id="api-linter">
      <Content>
        <APILinter />
      </Content>
    </TabbedLayout.Route>
  </TabbedLayout>
);

Use inside a simple content area

Copy
import { Content } from '@backstage/core-components';

export const ApiLinterPage = () => (
  <Content>
    <APILinter />
  </Content>
);

Add a route to your app router so users can reach the page.

Copy
// packages/app/src/App.tsx
import { Route } from 'react-router';
import { FlatRoutes } from '@backstage/core-app-api';
import { ApiLinterPage } from './components/ApiLinterPage';

const AppRoutes = () => (
  <FlatRoutes>
    {/* other routes */}
    <Route path="/api-linter" element={<ApiLinterPage />} />
  </FlatRoutes>
);

Optional tracking

You can pass tracking functions to the component.

Copy
import { APILinter } from 'backstage-plugin-api-linter';

const ApiLinterWithTracking = () => (
  <APILinter
    sendEvent={PluginTracking.sendEvent}
    sendPageView={PluginTracking.sendPageView}
    eventInfo={{
      plugin: 'api-linter',
      eventCategory: 'API linter page',
    }}
  />
);

Example types shown by the plugin docs

Copy
type IEventTracking = {
  plugin: string;
  eventLabel: string;
  eventAction: string;
  eventCategory: string;
};

type ICommonEventInfo = {
  plugin: string;
  eventCategory: string;
};

How it works

The UI calls the Backstage proxy path you configured as api linter. The proxy forwards the request to your Zally server. Ensure the Zally server is reachable from the backend. Ensure auth headers pass through if your Zally setup needs them.

Changelog

The API Linter plugin has not seen any significant changes since a year ago.

Set up Backstage in minutes with Roadie