dbt Docs logo

Backstage dbt Docs Plugin

Created by IIBenII

dbt Docs gives you an auto generated site for your dbt project. It shows models, tests, columns, sources, and a lineage graph. It is built from the manifest and catalog artifacts that dbt produces. Teams use it to understand how data moves and what each model does.

The dbt Docs Backstage plugin brings that experience into your internal portal. It adds a dbt page to your entity views so engineers can browse models and tests in place. You can read the model documentation and inspect stats and columns. You can follow dependencies across nodes. You can open raw code and compiled code. This centralizes your data model context inside Backstage next to ownership and other system metadata. The plugin is listed in the Backstage plugin directory and focuses on viewing dbt models and tests documentation inside Backstage.

Common use cases are straightforward. Put data context next to service owners and runbooks. Speed debugging when a downstream model breaks. Give reviewers a quick way to see lineage and intent during changes. Reduce tab switching for data engineers and app teams. If you run a self hosted Backstage, this plugin fits that workflow and keeps data docs where people already work.

Installation Instructions

These instructions apply to self-hosted Backstage only.

Install the packages

From your Backstage root directory.

Copy
yarn --cwd packages/app add @iiben_orgii/backstage-plugin-dbt
yarn --cwd packages/backend add @iiben_orgii/backstage-plugin-dbt-backend

Add the dbt tab in the frontend

Edit packages/app/src/components/catalog/EntityPage.tsx.

Import the page and the availability helper.

Copy
// packages/app/src/components/catalog/EntityPage.tsx
import { DbtPage, isDBTAvailable } from "@iiben_orgii/backstage-plugin-dbt";

Add the tab in the service entity layout.

Copy
// Inside your serviceEntityPage definition
const serviceEntityPage = (
  <EntityLayout>
    <EntityLayout.Route if={isDBTAvailable} path="/dbt" title="dbt">
      <DbtPage />
    </EntityLayout.Route>
  </EntityLayout>
);

Wire the backend for the new backend system

Edit packages/backend/src/index.ts.

Import the backend plugin.

Copy
// packages/backend/src/index.ts
import { dbtPlugin } from "@iiben_orgii/backstage-plugin-dbt-backend";

Add the plugin to your backend instance.

Copy
// Still in packages/backend/src/index.ts

const backend = createBackend();

backend.add(dbtPlugin);

backend.start();

Wire the backend for the legacy backend system

Create the router file.

Create packages/backend/src/plugins/dbt.ts.

Copy
// packages/backend/src/plugins/dbt.ts
import { createRouter } from "@iiben_orgii/backstage-plugin-dbt-backend";
import { Router } from "express";
import { PluginEnvironment } from "../types";

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

Register the route in the backend index.

Edit packages/backend/src/index.ts.

Copy
// packages/backend/src/index.ts
import dbt from "./plugins/dbt";

async function main() {
  // ...
  const dbtEnv = useHotMemoize(module, () => createEnv("dbt"));
  // ...
  apiRouter.use("/dbt", await dbt(dbtEnv));
  // ...
}

Add the app config schema to the app package

Create packages/app/config.d.ts.

Copy
export interface Config {
  dbtdoc: {
    /**
     * Frontend root URL
     * @visibility frontend
     */
    bucket?: string;
    backend?: "GoogleStorage" | "S3";
  };
}

Update packages/app/package.json.

Copy
{
  "files": [
    "dist",
    "config.d.ts"
  ],
  "configSchema": "config.d.ts"
}

Set up a single shared bucket

Add this to app-config.yaml.

Copy
dbtdoc:
  bucket: your-bucket-123
  backend: GoogleStorage # or S3

Set up one bucket per service or override the path

Limitation. All dbt docs must use the same backend type. GoogleStorage or S3.

Add annotations in each entity catalog file. Example catalog-info.yaml.

Copy
apiVersion: backstage.io/v1alpha1
kind: Component
spec:
  type: service
  owner: user:guest
  lifecycle: experimental
metadata:
  name: test
  annotations:
    dbtdoc-bucket: my-bucket
    dbtdoc-path: optional/override/path # Optional

Then set the backend type in app-config.yaml.

Copy
dbtdoc:
  backend: GoogleStorage # or S3

Place your dbt docs in the bucket

Default layout for multi or single setup.

  • {dbtdoc-bucket}/{kind}/{name}/manifest.json
  • {dbtdoc-bucket}/{kind}/{name}/catalog.json

You can override the kind and name part with the dbtdoc-path annotation.

As of version two point two point zero you can place manifest.json and catalog.json at the bucket root level too.

GCS authentication

The backend uses Application Default Credentials for Google Cloud Storage. See ADC docs.

Summary of what you added

  • Frontend tab in EntityPage that imports DbtPage and isDBTAvailable
  • Backend plugin wired by dbtPlugin for the new backend system
  • Or a router at packages/backend/src/plugins/dbt.ts plus a route in backend index for the legacy system
  • Config schema in packages/app to expose dbtdoc settings
  • app-config.yaml with bucket and backend
  • Optional annotations dbtdoc-bucket and dbtdoc-path in your entities

Changelog

The dbt Docs plugin has not seen any significant changes since 6 months ago.

Set up Backstage in minutes with Roadie