Claude Skills Guide

Claude Code for Backstage Plugin Workflow Tutorial

Backstage, Spotify’s open-source developer portal framework, has transformed how organizations build internal developer platforms. One of Backstage’s most powerful features is its plugin system, which allows you to extend the platform with custom functionality tailored to your organization’s needs. This tutorial explores how Claude Code can accelerate your Backstage plugin development workflow, from scaffolding to deployment.

Why Use Claude Code for Backstage Plugin Development

Building Backstage plugins requires understanding multiple technologies: React, TypeScript, the Backstage plugin API, and often backend services with various APIs. Claude Code excels at navigating this complexity by understanding the relationships between different parts of your codebase and generating coherent, working code.

When you work with Claude Code on Backstage plugins, you benefit from its ability to understand the Backstage plugin architecture, generate TypeScript React components following Backstage conventions, create proper plugin configuration, and write integration tests for plugin functionality.

Setting Up Your Backstage Plugin Project

Before diving into development, ensure your environment is properly configured. Claude Code can help you set up a new Backstage plugin or work within an existing monorepo.

Creating a New Plugin

The fastest way to scaffold a new Backstage plugin is using the Backstage CLI. Here’s how Claude Code can guide you through the process:

First, ensure you have the Backstage CLI installed:

npm install -g @backstage/cli

Then create your plugin:

cd your-backstage-repo
yarn new plugin

When prompted, choose whether you want a frontend plugin, backend plugin, or both. For most use cases, you’ll want a frontend plugin that can optionally include backend functionality.

Claude Code can help you understand the generated structure and make initial modifications. Simply share the generated files with Claude Code and ask for explanations or modifications.

Understanding Plugin Structure

A typical Backstage plugin contains several key directories and files. Let’s examine what Claude Code can help you understand and modify:

plugins/my-custom-plugin/
├── src/
│   ├── components/      # React components for the plugin UI
│   ├── plugin.ts        # Main plugin definition
│   └── index.ts         # Public API exports
├── dev/                 # Development utilities
├── package.json         # Plugin dependencies
└── README.md           # Documentation

The plugin.ts file is particularly important—it defines what your plugin exposes to Backstage. Claude Code can help you understand how to extend this file to add new features, routes, and integrations.

Building Your First Plugin Feature

Let’s walk through creating a simple but practical Backstage plugin: a service status dashboard that shows the health of various internal services.

Step 1: Define the Plugin

Open your plugin’s main file and define the plugin with its initial configuration:

import { createPlugin, createRouteRef, createSubRouteRef } from '@backstage/core-plugin-api';

export const rootRouteRef = createRouteRef({
  id: 'service-status',
});

export const serviceDetailRouteRef = createSubRouteRef({
  id: 'service-detail',
  path: '/:serviceId',
  parent: rootRouteRef,
});

export const serviceStatusPlugin = createPlugin({
  id: 'service-status',
  routes: {
    root: rootRouteRef,
    serviceDetail: serviceDetailRouteRef,
  },
});

Claude Code can explain each of these route definitions and help you add more complex routing patterns as your plugin grows.

Step 2: Create the Main Page Component

Next, create a component that displays your service status overview:

import React from 'react';
import { 
  Content, 
  Header, 
  Page, 
  Table, 
  StatusOK, 
  StatusWarning, 
  StatusError 
} from '@backstage/core-components';

interface Service {
  name: string;
  status: 'healthy' | 'degraded' | 'down';
  uptime: string;
  lastChecked: string;
}

export function ServiceStatusPage() {
  const services: Service[] = [
    { name: 'auth-service', status: 'healthy', uptime: '99.9%', lastChecked: '2 min ago' },
    { name: 'api-gateway', status: 'healthy', uptime: '99.8%', lastChecked: '2 min ago' },
    { name: 'payment-service', status: 'degraded', uptime: '98.5%', lastChecked: '2 min ago' },
    { name: 'notification-service', status: 'healthy', uptime: '99.9%', lastChecked: '2 min ago' },
  ];

  const getStatusComponent = (status: Service['status']) => {
    switch (status) {
      case 'healthy':
        return <StatusOK />;
      case 'degraded':
        return <StatusWarning />;
      case 'down':
        return <StatusError />;
    }
  };

  const columns = [
    { title: 'Service', field: 'name' },
    { title: 'Status', render: (row: Service) => getStatusComponent(row.status) },
    { title: 'Uptime', field: 'uptime' },
    { title: 'Last Checked', field: 'lastChecked' },
  ];

  return (
    <Page themeId="tool">
      <Header title="Service Status" subtitle="Monitor your infrastructure health" />
      <Content>
        <Table<Service>
          title="Active Services"
          columns={columns}
          data={services}
          options={{ search: true, paging: true }}
        />
      </Content>
    </Page>
  );
}

This component uses Backstage’s built-in UI components to maintain visual consistency with the rest of your portal. Claude Code can help you extend this with filtering, sorting, and real-time updates.

Step 3: Register the Plugin Route

Now you need to expose your component through the plugin’s router:

import { ServiceStatusPage } from './components/ServiceStatusPage';

export const ServiceStatusPage = serviceStatusPlugin.provide(
  createRouter({
    component: () => <ServiceStatusPage />,
  }),
);

Adding Backend Integration

Most real-world plugins need to communicate with backend services. Backstage plugins typically use a proxy or custom backend to handle this communication securely.

Creating a Backend API Client

Here’s how you might create a client to fetch service status data:

import { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';

export interface ServiceStatusClient {
  getServices(): Promise<Service[]>;
  getServiceDetails(id: string): Promise<ServiceDetail>;
}

export class ServiceStatusClientImpl implements ServiceStatusClient {
  constructor(
    private readonly discoveryApi: DiscoveryApi,
    private readonly fetchApi: FetchApi,
  ) {}

  async getServices(): Promise<Service[]> {
    const baseUrl = await this.discoveryApi.getPluginUrl('service-status');
    const response = await this.fetchApi.fetch(`${baseUrl}/api/services`);
    
    if (!response.ok) {
      throw new Error(`Failed to fetch services: ${response.statusText}`);
    }
    
    return response.json();
  }

  async getServiceDetails(id: string): Promise<ServiceDetail> {
    const baseUrl = await this.discoveryApi.getPluginUrl('service-status');
    const response = await this.fetchApi.fetch(`${baseUrl}/api/services/${id}`);
    
    if (!response.ok) {
      throw new Error(`Failed to fetch service details: ${response.statusText}`);
    }
    
    return response.json();
  }
}

Claude Code can help you set up the corresponding backend routes in your plugin’s backend module, complete with proper error handling and authentication.

Testing Your Plugin

Testing is crucial for maintaining plugin quality. Backstage provides utilities for both unit and integration testing.

Writing Component Tests

import { renderInTestApp } from '@backstage/test-utils';
import { ServiceStatusPage } from './ServiceStatusPage';
import { screen } from '@testing-library/react';

describe('ServiceStatusPage', () => {
  it('renders service status table', async () => {
    await renderInTestApp(<ServiceStatusPage />);
    
    expect(screen.getByText('Service Status')).toBeInTheDocument();
    expect(screen.getByText('Active Services')).toBeInTheDocument();
    expect(screen.getByText('auth-service')).toBeInTheDocument();
  });
});

Run your tests with:

cd plugins/service-status
yarn test

Best Practices for Backstage Plugin Development

When developing Backstage plugins with Claude Code assistance, keep these best practices in mind:

Follow Backstage Conventions: Stick to established patterns in the Backstage ecosystem. Use the standard component library, follow naming conventions, and leverage existing utilities. Claude Code understands these conventions and can help you stay on track.

Keep Plugins Focused: Each plugin should do one thing well. If you find your plugin growing beyond its original purpose, consider splitting it into multiple plugins that can work together.

Document Thoroughly: Include clear documentation for both users and other developers. Claude Code can help you generate README files and API documentation.

Test Extensively: The plugin will be used by many teams. Invest in comprehensive tests to ensure reliability.

Conclusion

Claude Code significantly accelerates Backstage plugin development by handling the boilerplate, explaining complex APIs, and generating working code that follows best practices. Whether you’re building a simple status dashboard or a complex integration with external systems, Claude Code can guide you through the entire development workflow.

Start with small, focused plugins, and gradually expand as you become more comfortable with the Backstage plugin architecture. The combination of Claude Code’s assistance and Backstage’s extensive plugin system empowers you to create powerful, customized developer experiences for your organization.

Built by theluckystrike — More at zovo.one