How to Create and Manage Annotation Layers for Contextual Data Visualization in Apache Superset
Annotation layers in Apache Superset allow you to overlay time-bound contextual events—such as product releases, marketing campaigns, or system outages—directly onto charts and dashboards to provide meaningful background for data interpretation.
Creating and managing annotation layers for contextual data visualization involves working with Superset's backend data model, REST API endpoints, and the React-based Explore view controls. The implementation spans SQLAlchemy models for data persistence, Flask-AppBuilder APIs for CRUD operations, and a dedicated frontend component that integrates annotation queries into the chart rendering pipeline.
Understanding the Annotation Layer Data Model
Superset stores annotation data using two primary entities defined in superset/models/annotations.py.
The AnnotationLayer and Annotation Entities
The AnnotationLayer class acts as a logical namespace that groups related annotations, while the Annotation class represents individual time-range entries that belong to a layer.
class AnnotationLayer(Model, AuditMixinNullable):
__tablename__ = "annotation_layer"
id = Column(Integer, primary_key=True)
name = Column(String(250))
descr = Column(Text)
class Annotation(Model, AuditMixinNullable):
__tablename__ = "annotation"
id = Column(Integer, primary_key=True)
start_dttm = Column(DateTime)
end_dttm = Column(DateTime)
layer_id = Column(Integer, ForeignKey("annotation_layer.id"), nullable=False)
short_descr = Column(String(500))
long_descr = Column(Text)
layer = relationship(AnnotationLayer, backref="annotation")
Both models inherit from AuditMixinNullable, which automatically captures created_on, changed_on, and user references for audit purposes.
Backend Architecture and Data Access
Business logic validation and data access are abstracted through dedicated DAOs to ensure data integrity when you create and manage annotation layers.
Data Access Objects (DAOs) for Validation
The superset/daos/annotation_layer.py file contains AnnotationLayerDAO and AnnotationDAO classes that enforce uniqueness constraints before persistence.
AnnotationLayerDAO.validate_update_uniqueness()ensures layer names remain unique across the system during create and update operations.AnnotationDAO.validate_update_uniqueness()enforces unique short descriptions within a specific layer.
These validation methods are invoked by command objects (such as CreateAnnotationLayerCommand) before committing transactions to the database.
REST API Endpoints for Managing Annotation Layers
Superset exposes comprehensive CRUD operations through Flask-AppBuilder REST endpoints defined in superset/annotation_layers/api.py.
| Endpoint | HTTP Method | Purpose |
|---|---|---|
/api/v1/annotation_layer/ |
POST | Creates a new layer via CreateAnnotationLayerCommand. |
/api/v1/annotation_layer/<int:pk> |
PUT | Updates an existing layer via UpdateAnnotationLayerCommand. |
/api/v1/annotation_layer/<int:pk> |
DELETE | Deletes a single layer via DeleteAnnotationLayerCommand. |
/api/v1/annotation_layer/ |
GET | Lists all layers with support for filtering, ordering, and pagination. |
/api/v1/annotation_layer/bulk_delete |
DELETE | Removes multiple layers simultaneously. |
API Schema Validation
Request and response payloads are validated using Marshmallow schemas in superset/annotation_layers/schemas.py:
class AnnotationLayerPostSchema(Schema):
name = fields.String(required=True, validate=[Length(1, 250)])
descr = fields.String(allow_none=True)
class AnnotationLayerPutSchema(Schema):
name = fields.String(required=False, validate=[Length(1, 250)])
descr = fields.String(required=False)
Permissions are enforced through class_permission_name = "Annotation" and method_permission_name = MODEL_API_RW_METHOD_PERMISSION_MAP, ensuring only authorized users can modify annotation layers.
Creating and Managing Layers via API
You can create and manage annotation layers for contextual data visualization programmatically using standard HTTP requests.
Creating a new layer:
curl -X POST "http://localhost:8088/api/v1/annotation_layer/" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_JWT>" \
-d '{
"name": "Release Calendar",
"descr": "Key release dates for the product"
}'
Successful response:
{
"id": 7,
"result": {
"name": "Release Calendar",
"descr": "Key release dates for the product"
}
}
Updating an existing layer:
curl -X PUT "http://localhost:8088/api/v1/annotation_layer/7" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_JWT>" \
-d '{"descr":"Updated description for release tracking"}'
Deleting layers:
# Single layer deletion
curl -X DELETE "http://localhost:8088/api/v1/annotation_layer/7" \
-H "Authorization: Bearer <YOUR_JWT>"
# Bulk deletion (ids 8, 9, and 10)
curl -X DELETE "http://localhost:8088/api/v1/annotation_layer/bulk_delete?q=%5B8%2C9%2C10%5D" \
-H "Authorization: Bearer <YOUR_JWT>"
Frontend Implementation in the Explore View
The user interface for managing annotation layers is implemented as a React control integrated into Superset's chart exploration workflow.
The AnnotationLayerControl Component
Located at superset-frontend/src/explore/components/controls/AnnotationLayerControl/index.tsx, the AnnotationLayerControl manages the list of active annotation layers for a chart. It renders existing layers and provides interfaces to add, edit, or remove entries.
Key interface definition:
export interface Annotation {
name: string;
show?: boolean;
annotation: string;
timeout: Date;
key: string;
formData: QueryFormData | null;
isDashboardRequest?: boolean;
force?: boolean;
}
Adding and Editing Layers via the UI
When users add a new layer, the control opens a pop-over interface defined in superset-frontend/src/explore/components/controls/AnnotationLayerControl/AnnotationLayer.tsx. This form captures:
- Layer name and description
- Time range (start and end datetimes)
- Visualization configuration
Upon saving, the refreshAnnotationData callback dispatches runAnnotationQuery to fetch annotation values from the backend.
Querying Annotation Data
The runAnnotationQuery function, defined in superset-frontend/src/components/Chart/chartAction.ts, constructs and executes queries against the Annotation table. It retrieves time-range data that matches the chart's current time filter, enabling the rendering of visual overlays such as vertical bands or markers on time-series charts.
Integration example:
<AnnotationLayerControl
name="annotation_layers"
value={currentLayers}
onChange={updated => setFormData({ ...formData, annotation_layers: updated })}
refreshAnnotationData={payload => dispatch(runAnnotationQuery(payload))}
colorScheme={theme.colors.primary}
vizType={formData.viz_type}
/>
The control connects to Redux via mapStateToProps and mapDispatchToProps, accessing theme settings, current chart state, and error messages.
End-to-End Workflow Example
To create and manage annotation layers for contextual data visualization in a production scenario:
-
Create the layer namespace – Use the Explore UI or POST to
/api/v1/annotation_layer/to establish a logical group (e.g., "Marketing Campaigns"). -
Populate with annotations – Through the UI pop-over or API, add time-range entries specifying
start_dttm,end_dttm, and descriptions (e.g., "Holiday Sale: Nov 24-27"). -
Attach to charts – In the Explore view, add the layer via
AnnotationLayerControl. The component callsrunAnnotationQueryto fetch relevant annotations based on the chart's time range. -
Manage lifecycle – Update descriptions via PUT requests, or delete layers (checking for existing annotations first to avoid 422 errors) via DELETE or
bulk_deleteendpoints.
Summary
- Annotation layers provide logical namespaces (
AnnotationLayer) for grouping time-bound contextual events (Annotation), defined insuperset/models/annotations.py. - Backend validation occurs through
AnnotationLayerDAOandAnnotationDAOinsuperset/daos/annotation_layer.py, enforcing unique names and descriptions. - REST API endpoints in
superset/annotation_layers/api.pyprovide full CRUD operations, including bulk deletion, with Marshmallow schema validation insuperset/annotation_layers/schemas.py. - Frontend integration centers on
AnnotationLayerControlinsuperset-frontend/src/explore/components/controls/AnnotationLayerControl/index.tsx, which manages UI state and dispatchesrunAnnotationQueryfromsuperset-frontend/src/components/Chart/chartAction.tsto retrieve overlay data.
Frequently Asked Questions
What is the difference between an annotation layer and an annotation?
An annotation layer is a logical container or namespace—such as "Product Releases" or "Marketing Campaigns"—that groups related contextual events together. An annotation is a specific time-range entry within that layer, containing start_dttm, end_dttm, and description fields that render as visual overlays on charts.
Can I delete an annotation layer that contains existing annotations?
No, the backend prevents deletion of layers that still host annotations to maintain referential integrity. The DeleteAnnotationLayerCommand checks for existing annotations via DAO validation methods, returning a 422 error if dependencies are detected. You must first delete or reassign all annotations within the layer before the system permits layer deletion.
How do I apply annotation layers to specific charts?
In the Superset Explore view, locate the Annotation Layers control in the chart configuration panel. The AnnotationLayerControl component allows you to select existing layers, which triggers runAnnotationQuery to fetch time-range data. The annotations then render as overlays—typically vertical bands, markers, or highlighted regions—on time-series charts based on the layer's defined time ranges.
What permissions are required to manage annotation layers?
Management requires permissions associated with the Annotation class, as defined in superset/annotation_layers/api.py via class_permission_name = "Annotation". Standard REST permissions (can_read, can_write, can_delete) apply to the endpoints. Users must have appropriate role-based access control (RBAC) permissions granted through Superset's security model to create, update, or delete layers via either the UI or REST API.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →