Skip to main content
Version: Next

Quick Start

This guide walks you through creating your first Superset extension - a simple "Hello World" panel that displays a message fetched from a backend API endpoint. You'll learn the essential structure and patterns for building full-stack Superset extensions.

Prerequisites

Before starting, ensure you have:

  • Node.js and npm compatible with your Superset version
  • Python compatible with your Superset version
  • A running Superset development environment
  • Basic knowledge of React, TypeScript, and Flask

Step 1: Install the Extensions CLI

First, install the Apache Superset Extensions CLI:

pip install apache-superset-extensions-cli

Step 2: Create a New Extension

Use the CLI to scaffold a new extension project. Extensions can include frontend functionality, backend functionality, or both, depending on your needs. This quickstart demonstrates a full-stack extension with both frontend UI components and backend API endpoints to show the complete integration pattern.

superset-extensions init

The CLI will prompt you for information:

Extension ID (unique identifier, alphanumeric only): hello_world
Extension name (human-readable display name): Hello World
Initial version [0.1.0]: 0.1.0
License [Apache-2.0]: Apache-2.0
Include frontend? [Y/n]: Y
Include backend? [Y/n]: Y

This creates a complete project structure:

hello_world/
├── extension.json # Extension metadata and configuration
├── backend/ # Backend Python code
│ ├── src/
│ │ └── hello_world/
│ │ ├── __init__.py
│ │ └── entrypoint.py # Backend registration
│ └── pyproject.toml
└── frontend/ # Frontend TypeScript/React code
├── src/
│ └── index.tsx # Frontend entry point
├── package.json
├── tsconfig.json
└── webpack.config.js

Step 3: Configure Extension Metadata

The generated extension.json contains basic metadata. Update it to register your panel in SQL Lab:

{
"id": "hello_world",
"name": "Hello World",
"version": "0.1.0",
"license": "Apache-2.0",
"frontend": {
"contributions": {
"views": {
"sqllab.panels": [
{
"id": "hello_world.main",
"name": "Hello World"
}
]
}
},
"moduleFederation": {
"exposes": ["./index"]
}
},
"backend": {
"entryPoints": ["hello_world.entrypoint"],
"files": ["backend/src/hello_world/**/*.py"]
},
"permissions": ["can_read"]
}

Key fields:

  • frontend.contributions.views.sqllab.panels: Registers your panel in SQL Lab
  • backend.entryPoints: Python modules to load eagerly when extension starts

Step 4: Create Backend API

The CLI generated a basic backend/src/hello_world/entrypoint.py. We'll create an API endpoint.

Create backend/src/hello_world/api.py

from flask import Response
from flask_appbuilder.api import expose, protect, safe
from superset_core.api.rest_api import RestApi


class HelloWorldAPI(RestApi):
resource_name = "hello_world"
openapi_spec_tag = "Hello World"
class_permission_name = "hello_world"

@expose("/message", methods=("GET",))
@protect()
@safe
def get_message(self) -> Response:
"""Gets a hello world message
---
get:
description: >-
Get a hello world message from the backend
responses:
200:
description: Hello world message
content:
application/json:
schema:
type: object
properties:
result:
type: object
properties:
message:
type: string
401:
$ref: '#/components/responses/401'
"""
return self.response(
200,
result={"message": "Hello from the backend!"}
)

Key points:

  • Extends RestApi from superset_core.api.types.rest_api
  • Uses Flask-AppBuilder decorators (@expose, @protect, @safe)
  • Returns responses using self.response(status_code, result=data)
  • The endpoint will be accessible at /extensions/hello_world/message
  • OpenAPI docstrings are crucial - Flask-AppBuilder uses them to automatically generate interactive API documentation at /swagger/v1, allowing developers to explore endpoints, understand schemas, and test the API directly from the browser

Update backend/src/hello_world/entrypoint.py

Replace the generated print statement with API registration:

from superset_core.api import rest_api

from .api import HelloWorldAPI

rest_api.add_extension_api(HelloWorldAPI)

This registers your API with Superset when the extension loads.

Step 5: Create Frontend Component

The CLI generated boilerplate files. The webpack config and package.json are already properly configured with Module Federation.

Create frontend/src/HelloWorldPanel.tsx

Create a new file for the component implementation:

import React, { useEffect, useState } from 'react';
import { authentication } from '@apache-superset/core';

const HelloWorldPanel: React.FC = () => {
const [message, setMessage] = useState<string>('');
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string>('');

useEffect(() => {
const fetchMessage = async () => {
try {
const csrfToken = await authentication.getCSRFToken();
const response = await fetch('/extensions/hello_world/message', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken!,
},
});

if (!response.ok) {
throw new Error(`Server returned ${response.status}`);
}

const data = await response.json();
setMessage(data.result.message);
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
} finally {
setLoading(false);
}
};

fetchMessage();
}, []);

if (loading) {
return (
<div style={{ padding: '20px', textAlign: 'center' }}>
<p>Loading...</p>
</div>
);
}

if (error) {
return (
<div style={{ padding: '20px', color: 'red' }}>
<strong>Error:</strong> {error}
</div>
);
}

return (
<div style={{ padding: '20px' }}>
<h3>Hello World Extension</h3>
<div
style={{
padding: '16px',
backgroundColor: '#f6ffed',
border: '1px solid #b7eb8f',
borderRadius: '4px',
marginBottom: '16px',
}}
>
<strong>{message}</strong>
</div>
<p>This message was fetched from the backend API! 🎉</p>
</div>
);
};

export default HelloWorldPanel;

Update frontend/src/index.tsx

Replace the generated code with the extension entry point:

import React from 'react';
import { core } from '@apache-superset/core';
import HelloWorldPanel from './HelloWorldPanel';

export const activate = (context: core.ExtensionContext) => {
context.disposables.push(
core.registerViewProvider('hello_world.main', () => <HelloWorldPanel />),
);
};

export const deactivate = () => {};

Key patterns:

  • activate function is called when the extension loads
  • core.registerViewProvider registers the component with ID hello_world.main (matching extension.json)
  • authentication.getCSRFToken() retrieves the CSRF token for API calls
  • Fetch calls to /extensions/{extension_id}/{endpoint} reach your backend API
  • context.disposables.push() ensures proper cleanup

Step 6: Install Dependencies

Install the frontend dependencies:

cd frontend
npm install
cd ..

Step 7: Package the Extension

Create a .supx bundle for deployment:

superset-extensions bundle

This command automatically:

  • Builds frontend assets using Webpack with Module Federation
  • Collects backend Python source files
  • Creates a dist/ directory with:
    • manifest.json - Build metadata and asset references
    • frontend/dist/ - Built frontend assets (remoteEntry.js, chunks)
    • backend/ - Python source files
  • Packages everything into hello_world-0.1.0.supx - a zip archive with the specific structure required by Superset

Step 8: Deploy to Superset

To deploy your extension, you need to enable extensions support and configure where Superset should load them from.

Configure Superset

Add the following to your superset_config.py:

# Enable extensions feature
FEATURE_FLAGS = {
"EXTENSIONS": True,
}

# Set the directory where extensions are stored
EXTENSIONS_PATH = "/path/to/extensions/folder"

Copy Extension Bundle

Copy your .supx file to the configured extensions path:

cp hello_world-0.1.0.supx /path/to/extensions/folder/

Restart Superset

Restart your Superset instance to load the extension:

# Restart your Superset server
superset run

Superset will extract and validate the extension metadata, load the assets, register the extension with its capabilities, and make it available for use.

Step 9: Test Your Extension

  1. Open SQL Lab in Superset
  2. Look for the "Hello World" panel in the panels dropdown or sidebar
  3. Open the panel - it should display "Hello from the backend!"
  4. Check that the message was fetched from your API endpoint

Understanding the Flow

Here's what happens when your extension loads:

  1. Superset starts: Reads extension.json and loads backend entrypoint
  2. Backend registration: entrypoint.py registers your API via rest_api.add_extension_api()
  3. Frontend loads: When SQL Lab opens, Superset fetches the remote entry file
  4. Module Federation: Webpack loads your extension code and resolves @apache-superset/core to window.superset
  5. Activation: activate() is called, registering your view provider
  6. Rendering: When the user opens your panel, React renders <HelloWorldPanel />
  7. API call: Component fetches data from /extensions/hello_world/message
  8. Backend response: Your Flask API returns the hello world message
  9. Display: Component shows the message to the user

Next Steps

Now that you have a working extension, explore:

For a complete real-world example, examine the query insights extension in the Superset codebase.