docs: format with prettier (#1218)

This commit is contained in:
Patrik Oldsberg
2020-06-09 19:50:41 +02:00
committed by GitHub
parent 4f3375073d
commit a633dce818
20 changed files with 756 additions and 364 deletions
+187 -96
View File
@@ -4,35 +4,67 @@
### Can we call Backstage something different? So that it fits our company better?
Yes, Backstage is just a platform for building your own developer portal. We happen to call our internal version Backstage, as well, as a reference to our music roots. You can call your version whatever suits your team, company, or brand.
Yes, Backstage is just a platform for building your own developer portal. We
happen to call our internal version Backstage, as well, as a reference to our
music roots. You can call your version whatever suits your team, company, or
brand.
### Is Backstage a monitoring platform?
No, but it can be! Backstage is designed to be a developer portal for all your infrastructure tooling, services, and documentation. So, it's not a monitoring platform — but that doesn't mean you can't integrate a monitoring tool into Backstage by writing [a plugin](https://github.com/spotify/faq#what-is-a-plugin-in-backstage).
No, but it can be! Backstage is designed to be a developer portal for all your
infrastructure tooling, services, and documentation. So, it's not a monitoring
platform — but that doesn't mean you can't integrate a monitoring tool into
Backstage by writing
[a plugin](https://github.com/spotify/faq#what-is-a-plugin-in-backstage).
### How is Backstage licensed?
Backstage was released as free and open software by Spotify and is licensed under [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
Backstage was released as free and open software by Spotify and is licensed
under [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
### Why did we open source Backstage?
We hope to see Backstage become the infrastructure standard everywhere. When we saw how much Backstage improved developer experience and productivity internally, we wanted to share those gains. After all, if Backstage can create order in an engineering environment as open and diverse as ours, then we're pretty sure it can create order (and boost productivity) anywhere. To learn more, read our blog post, "[What the heck is Backstage anyway?](https://backstage.io/blog/2020/03/18/what-is-backstage)"
We hope to see Backstage become the infrastructure standard everywhere. When we
saw how much Backstage improved developer experience and productivity
internally, we wanted to share those gains. After all, if Backstage can create
order in an engineering environment as open and diverse as ours, then we're
pretty sure it can create order (and boost productivity) anywhere. To learn
more, read our blog post,
"[What the heck is Backstage anyway?](https://backstage.io/blog/2020/03/18/what-is-backstage)"
### Will Spotify's internal plugins be open sourced, too?
Yes, we've already started releasing open source versions of some of the plugins we use here, and we'll continue to do so. [Plugins](https://github.com/spotify/faq#what-is-a-plugin-in-backstage) are the building blocks of functionality in Backstage. We have over 120 plugins inside Spotify — many of those are specialized for our use, so will remain internal and proprietary to us. But we estimate that about a third of our existing plugins make good open source candidates. (And we'll probably end up writing some brand new ones, too.)
Yes, we've already started releasing open source versions of some of the plugins
we use here, and we'll continue to do so.
[Plugins](https://github.com/spotify/faq#what-is-a-plugin-in-backstage) are the
building blocks of functionality in Backstage. We have over 120 plugins inside
Spotify — many of those are specialized for our use, so will remain internal and
proprietary to us. But we estimate that about a third of our existing plugins
make good open source candidates. (And we'll probably end up writing some brand
new ones, too.)
### What's the roadmap for Backstage?
We envision three phases, which you can learn about in [our project roadmap](https://github.com/spotify/backstage#project-roadmap). Even though the open source version of Backstage is relatively new compared to our internal version, we have already begun work on various aspects of all three phases. Looking at the [milestones for active issues](https://github.com/spotify/backstage/milestones) will also give you a sense of our progress.
We envision three phases, which you can learn about in
[our project roadmap](https://github.com/spotify/backstage#project-roadmap).
Even though the open source version of Backstage is relatively new compared to
our internal version, we have already begun work on various aspects of all three
phases. Looking at the
[milestones for active issues](https://github.com/spotify/backstage/milestones)
will also give you a sense of our progress.
### My company doesn't have thousands of developers or services. Is Backstage overkill?
Not at all! A core reason to adopt Backstage is to standardize how software is built at your company. It's easier to decide on those standards as a small company, and grows in importance as the company grows. Backstage sets a foundation, and an early investment in your infrastructure becomes even more valuable as you grow.
Not at all! A core reason to adopt Backstage is to standardize how software is
built at your company. It's easier to decide on those standards as a small
company, and grows in importance as the company grows. Backstage sets a
foundation, and an early investment in your infrastructure becomes even more
valuable as you grow.
### Our company has a strong design language system/brand that we want to incorporate. Does Backstage support this?
Yes! The Backstage UI is built using Material-UI. With the theming capabilities of Material-UI, you are able to adapt the interface to your brand guidelines.
Yes! The Backstage UI is built using Material-UI. With the theming capabilities
of Material-UI, you are able to adapt the interface to your brand guidelines.
## Technical FAQ:
@@ -40,96 +72,155 @@ Yes! The Backstage UI is built using Material-UI. With the theming capabilities
The short answer is that's what we've been using in Backstage internally.
The original decision was based on Google's Material Design being a thorough, well thought out and complete design system, with many mature and powerful libraries implemented in both the system itself and auxiliary components that we knew that we would like to use.
The original decision was based on Google's Material Design being a thorough,
well thought out and complete design system, with many mature and powerful
libraries implemented in both the system itself and auxiliary components that we
knew that we would like to use.
It strikes a good balance between power, customizability, and ease of use. A
core focus of Backstage is to make plugin developers productive with as few
hurdles as possible. Material-UI lets plugin makers get going easily with both
well-known tech and a large flora of components.
It strikes a good balance between power, customizability, and ease of use. A core focus of Backstage is to make plugin developers productive with as few hurdles as possible. Material-UI lets plugin makers get going easily with both well-known tech and a large flora of components.
### What technology does Backstage use?
The code base is a large-scale React application that uses TypeScript. For [Phase 2](https://github.com/spotify/backstage#project-roadmap), we plan to use Node.js and GraphQL.
### What is the end-to-end user flow? The happy path story.
There are three main user profiles for Backstage: the integrator, the contributor, and the software engineer.
The **integrator** hosts the Backstage app and configures which plugins are available to use in the app.
The **contributor** adds functionality to the app by writing plugins.
The **software engineer** uses the app's functionality and interacts with its plugins.
### What is a "plugin" in Backstage?
Plugins are what provide the feature functionality in Backstage. They are used to integrate different systems into Backstage's frontend, so that the developer gets a consistent UX, no matter what tool or service is being accessed on the other side.
Each plugin is treated as a self-contained web app and can include almost any type of content. Plugins all use a common set of platform APIs and reusable UI components. Plugins can fetch data either from the backend or an API exposed through the proxy.
Learn more about [the different components](https://github.com/spotify/backstage#overview) that make up Backstage.
### Do I have to write plugins in TypeScript?
No, you can use JavaScript if you prefer.
We want to keep the Backstage core APIs in TypeScript, but aren't forcing it on individual plugins.
### How do I find out if a plugin already exists?
Before you write a plugin, [search the plugin issues](https://github.com/spotify/backstage/issues?q=is%3Aissue+label%3Aplugin+) to see if it already exists or is in the works. If no one's thought of it yet, great! Open a new issue as [a plugin suggestion](https://github.com/spotify/backstage/issues/new/choose) and describe what your plugin will do. This will help coordinate our contributors' efforts and avoid duplicating existing functionality.
In the future, we will create [a plugin gallery](https://github.com/spotify/backstage/issues/260) where people can browse and search for all available plugins.
### Which plugin is used the most at Spotify?
By far, our most-used plugin is our TechDocs plugin, which we use for creating technical documentation. Our philosophy at Spotify is to treat "docs like code", where you write documentation using the same workflow as you write your code. This makes it easier to create, find, and update documentation. We hope to release [the open source version](https://github.com/spotify/backstage/issues/687) in the future. (See also: "[Will Spotify's internal plugins be open sourced, too?](https://github.com/spotify/faq#will-spotifys-internal-plugins-be-open-sourced-too)" above)
### Are you planning to have plugins baked into the repo? Or should they be developed in separate repos?
Contributors can add open source plugins to the plugins directory in [this monorepo](https://github.com/spotify/backstage). Integrators can then configure which open source plugins are available to use in their instance of the app. Open source plugins are downloaded as npm packages published in the open source repository.
While we encourage using the open source model, we know there are cases where contributors might want to experiment internally or keep their plugins closed source. Contributors writing closed source plugins should develop them in the plugins directory in their own Backstage repository. Integrators also configure closed source plugins locally from the monorepo.
### Any plans for integrating with other repository managers, such as GitLab or Bitbucket?
We chose GitHub because it is the tool that we are most familiar with, so that will naturally lead to integrations for GitHub being developed at an early stage.
Hosting this project on GitHub does not exclude integrations with alternatives, such as GitLab or Bitbucket. We believe that in time there will be plugins that will provide functionality for these tools as well. Hopefully, contributed by the community!
Also note, implementations of Backstage can be hosted wherever you feel suits your needs best.
### Who maintains Backstage?
Spotify will maintain the open source core, but we envision different parts of the project being maintained by various companies and contributors. We also envision a large, diverse ecosystem of open source plugins, which would be maintained by their original authors/contributors or by the community.
When it comes to [deployment](https://github.com/spotify/backstage/blob/master/DEPLOYMENT.md), the system integrator (typically, the infrastructure team in your organization) maintains Backstage in your own environment.
### Does Spotify provide a managed version of Backstage?
No, this is not a service offering. We build the piece of software, and someone in your infrastructure team is responsible for [deploying](https://github.com/spotify/backstage/blob/master/DEPLOYMENT.md) and maintaining it.
### How secure is Backstage?
We take security seriously. When it comes to packages and code we scan our repositories periodically and update our packages to the latest versions. When it comes to deployment of Backstage within an organisation it depends on the deployment and security setup in your organisation. Reach out to us on [Discord](https://discord.gg/MUpMjP2) if you have specific queries.
Please report sensitive security issues via Spotify's [bug-bounty program](https://hackerone.com/spotify) rather than GitHub.
The code base is a large-scale React application that uses TypeScript. For
[Phase 2](https://github.com/spotify/backstage#project-roadmap), we plan to use
Node.js and GraphQL.
### What is the end-to-end user flow? The happy path story.
There are three main user profiles for Backstage: the integrator, the
contributor, and the software engineer. The **integrator** hosts the Backstage
app and configures which plugins are available to use in the app. The
**contributor** adds functionality to the app by writing plugins. The
**software engineer** uses the app's functionality and interacts with its
plugins.
### What is a "plugin" in Backstage?
Plugins are what provide the feature functionality in Backstage. They are used
to integrate different systems into Backstage's frontend, so that the developer
gets a consistent UX, no matter what tool or service is being accessed on the
other side. Each plugin is treated as a self-contained web app and can include
almost any type of content. Plugins all use a common set of platform APIs and
reusable UI components. Plugins can fetch data either from the backend or an API
exposed through the proxy. Learn more about
[the different components](https://github.com/spotify/backstage#overview) that
make up Backstage.
### Do I have to write plugins in TypeScript?
No, you can use JavaScript if you prefer. We want to keep the Backstage core
APIs in TypeScript, but aren't forcing it on individual plugins.
### How do I find out if a plugin already exists?
Before you write a plugin,
[search the plugin issues](https://github.com/spotify/backstage/issues?q=is%3Aissue+label%3Aplugin+)
to see if it already exists or is in the works. If no one's thought of it yet,
great! Open a new issue as
[a plugin suggestion](https://github.com/spotify/backstage/issues/new/choose)
and describe what your plugin will do. This will help coordinate our
contributors' efforts and avoid duplicating existing functionality. In the
future, we will create
[a plugin gallery](https://github.com/spotify/backstage/issues/260) where people
can browse and search for all available plugins.
### Which plugin is used the most at Spotify?
By far, our most-used plugin is our TechDocs plugin, which we use for creating
technical documentation. Our philosophy at Spotify is to treat "docs like code",
where you write documentation using the same workflow as you write your code.
This makes it easier to create, find, and update documentation. We hope to
release
[the open source version](https://github.com/spotify/backstage/issues/687) in
the future. (See also:
"[Will Spotify's internal plugins be open sourced, too?](https://github.com/spotify/faq#will-spotifys-internal-plugins-be-open-sourced-too)"
above)
### Are you planning to have plugins baked into the repo? Or should they be developed in separate repos?
Contributors can add open source plugins to the plugins directory in
[this monorepo](https://github.com/spotify/backstage). Integrators can then
configure which open source plugins are available to use in their instance of
the app. Open source plugins are downloaded as npm packages published in the
open source repository. While we encourage using the open source model, we
know there are cases where contributors might want to experiment internally or
keep their plugins closed source. Contributors writing closed source plugins
should develop them in the plugins directory in their own Backstage repository.
Integrators also configure closed source plugins locally from the monorepo.
### Any plans for integrating with other repository managers, such as GitLab or Bitbucket?
We chose GitHub because it is the tool that we are most familiar with, so that
will naturally lead to integrations for GitHub being developed at an early
stage. Hosting this project on GitHub does not exclude integrations with
alternatives, such as GitLab or Bitbucket. We believe that in time there will be
plugins that will provide functionality for these tools as well. Hopefully,
contributed by the community! Also note, implementations of Backstage can be
hosted wherever you feel suits your needs best.
### Who maintains Backstage?
Spotify will maintain the open source core, but we envision different parts of
the project being maintained by various companies and contributors. We also
envision a large, diverse ecosystem of open source plugins, which would be
maintained by their original authors/contributors or by the community. When it
comes to
[deployment](https://github.com/spotify/backstage/blob/master/DEPLOYMENT.md),
the system integrator (typically, the infrastructure team in your organization)
maintains Backstage in your own environment.
### Does Spotify provide a managed version of Backstage?
No, this is not a service offering. We build the piece of software, and
someone in your infrastructure team is responsible for
[deploying](https://github.com/spotify/backstage/blob/master/DEPLOYMENT.md) and
maintaining it.
### How secure is Backstage?
We take security seriously. When it comes to packages and code we scan our
repositories periodically and update our packages to the latest versions. When
it comes to deployment of Backstage within an organisation it depends on the
deployment and security setup in your organisation. Reach out to us on
[Discord](https://discord.gg/MUpMjP2) if you have specific queries.
Please report sensitive security issues via Spotify's
[bug-bounty program](https://hackerone.com/spotify) rather than GitHub.
### Does Backstage collect any information that is shared with Spotify?
No. Backstage does not collect any telemetry from any third party using the platform. Spotify, and the open source community, does have access to [GitHub Insights](https://github.com/features/insights), which contains information such as contributors, commits, traffic, and dependencies.
Backstage is an open platform, but you are in control of your own data. You control who has access to any data you provide to your version of Backstage and who that data is shared with.
No. Backstage does not collect any telemetry from any third party using the
platform. Spotify, and the open source community, does have access to
[GitHub Insights](https://github.com/features/insights), which contains
information such as contributors, commits, traffic, and dependencies.
Backstage is an open platform, but you are in control of your own data. You
control who has access to any data you provide to your version of Backstage and
who that data is shared with.
### Can Backstage be used to build something other than a developer portal?
Yes. The core frontend framework could be used for building any large-scale web application where (1) multiple teams are building separate parts of the app, and (2) you want the overall experience to be consistent.
That being said, in [Phase 2](https://github.com/spotify/backstage#project-roadmap) of the project we will add features that are needed for developer portals and systems for managing software ecosystems. Our ambition will be to keep Backstage modular.
Yes. The core frontend framework could be used for building any large-scale
web application where (1) multiple teams are building separate parts of the app,
and (2) you want the overall experience to be consistent. That being said, in
[Phase 2](https://github.com/spotify/backstage#project-roadmap) of the project
we will add features that are needed for developer portals and systems for
managing software ecosystems. Our ambition will be to keep Backstage modular.
### How can I get involved?
Jump right in! Come help us fix some of the [early bugs and first issues](https://github.com/spotify/backstage/labels/good%20first%20issue) or reach [a new milestone](https://github.com/spotify/backstage/milestones). Or write an open source plugin for Backstage, like this [Lighthouse plugin](https://github.com/spotify/backstage/tree/master/plugins/lighthouse).
See all the ways you can [contribute here](https://github.com/spotify/backstage/blob/master/CONTRIBUTING.md). We'd love to have you as part of the community.
Jump right in! Come help us fix some of the
[early bugs and first issues](https://github.com/spotify/backstage/labels/good%20first%20issue)
or reach [a new milestone](https://github.com/spotify/backstage/milestones). Or
write an open source plugin for Backstage, like this
[Lighthouse plugin](https://github.com/spotify/backstage/tree/master/plugins/lighthouse).
See all the ways you can
[contribute here](https://github.com/spotify/backstage/blob/master/CONTRIBUTING.md).
We'd love to have you as part of the community.
### Can I join the Backstage team?
If you're interested in being part of the Backstage team, reach out to [fossopportunities@spotify.com](mailto:fossopportunities@spotify.com)
If you're interested in being part of the Backstage team, reach out to
[fossopportunities@spotify.com](mailto:fossopportunities@spotify.com)
@@ -6,22 +6,22 @@
## Background
Backstage comes with a software catalog functionality, that you can use to
track all your software components and more. It can be powered by data from
various sources, and one of them that is included with the package, is a
custom database backed catalog. It has the ability to keep itself updated
automatically based on the contents of little descriptor files in your
version control system of choice. Developers create these files and maintain
them side by side with their code, and the catalog system reacts accordingly.
Backstage comes with a software catalog functionality, that you can use to track
all your software components and more. It can be powered by data from various
sources, and one of them that is included with the package, is a custom database
backed catalog. It has the ability to keep itself updated automatically based on
the contents of little descriptor files in your version control system of
choice. Developers create these files and maintain them side by side with their
code, and the catalog system reacts accordingly.
This ADR describes the default format of these descriptor files.
### Inspiration
Internally at Spotify, a home grown software catalog system is used heavily
and forms a core part of Backstage and other important pieces of the
infrastructure. The user experience, learnings and certain pieces of metadata
from that catalog are being carried over to the open source effort.
Internally at Spotify, a home grown software catalog system is used heavily and
forms a core part of Backstage and other important pieces of the infrastructure.
The user experience, learnings and certain pieces of metadata from that catalog
are being carried over to the open source effort.
The file format described herein, also draws heavy inspiration from the
[kubernetes object format](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/).
@@ -35,9 +35,9 @@ inside Backstage, or by push events from a CI/CD pipelines, or by webhook
triggers from the version control system, etc.
Each file describes one or more entities in accordance with the
[Backstage System Model](https://github.com/spotify/backstage/issues/390).
All of these entities have a common stucture and nomenclature, and they are
stored in the software catalog from which they then can be queried.
[Backstage System Model](https://github.com/spotify/backstage/issues/390). All
of these entities have a common stucture and nomenclature, and they are stored
in the software catalog from which they then can be queried.
Entities have distinct names, and they may reference each other by those names.
@@ -70,9 +70,9 @@ spec:
```
The root fields `apiVersion`, `kind`, `metadata`, and `spec` are part of the
_envelope_, defining the overall structure of all kinds of entity. Likewise,
the `name`, `namespace`, `labels`, and `annotations` metadata fields are of
special significance and have reserved purposes and distinct shapes.
_envelope_, defining the overall structure of all kinds of entity. Likewise, the
`name`, `namespace`, `labels`, and `annotations` metadata fields are of special
significance and have reserved purposes and distinct shapes.
See below for details about these fields.
@@ -88,30 +88,31 @@ first versions of the catalog will focus on the `Component` kind.
The `apiVersion`is the version of specification format for that particular
entity that this file is written against. The version is used for being able to
evolve the format, and the tuple of `apiVersion` and `kind` should be enough
for a parser to know how to interpret the contents of the rest of the document.
evolve the format, and the tuple of `apiVersion` and `kind` should be enough for
a parser to know how to interpret the contents of the rest of the document.
Backstage specific entities have an `apiVersion` that is prefixed with
`backstage.io/`, to distinguish them from other types of object that share
the same type of structure. This may be relevant when co-hosting these
`backstage.io/`, to distinguish them from other types of object that share the
same type of structure. This may be relevant when co-hosting these
specifications with e.g. kubernetes object manifests.
Early versions of the catalog will be using beta versions, e.g. `backstage.io/v1beta1`,
to signal that the format may still change. After that, we will be using
`backstage.io/v1` and up.
Early versions of the catalog will be using beta versions, e.g.
`backstage.io/v1beta1`, to signal that the format may still change. After that,
we will be using `backstage.io/v1` and up.
### `metadata`
A structure that contains metadata about the entity, i.e. things that aren't directly
part of the entity specification itself. See below for more details about this structure.
A structure that contains metadata about the entity, i.e. things that aren't
directly part of the entity specification itself. See below for more details
about this structure.
### `spec`
The actual specification data that describes the entity.
The precise structure of the `spec` depends on the `apiVersion` and `kind` combination,
and some kinds may not even have a `spec` at all. See further down in this document for
the specification structure of specific kinds.
The precise structure of the `spec` depends on the `apiVersion` and `kind`
combination, and some kinds may not even have a `spec` at all. See further down
in this document for the specification structure of specific kinds.
## Metadata
@@ -119,27 +120,31 @@ The `metadata` root field has the following nested structure.
### `name`
The name of the entity. This name is both meant for human eyes to recognize the entity,
and for machines and other components to reference the entity (e.g. in URLs or from
other entity specification files).
The name of the entity. This name is both meant for human eyes to recognize the
entity, and for machines and other components to reference the entity (e.g. in
URLs or from other entity specification files).
Names must be unique per kind, within a given namespace (if specified), at any point in
time. Names may be reused at a later time, after an entity is deleted from the registry.
Names must be unique per kind, within a given namespace (if specified), at any
point in time. Names may be reused at a later time, after an entity is deleted
from the registry.
Names are required to follow a certain format. Entities that do not follow those rules
will not be accepted for registration in the catalog. The ruleset is configurable to fit
your organization's needs, but the default behavior is as follows.
Names are required to follow a certain format. Entities that do not follow those
rules will not be accepted for registration in the catalog. The ruleset is
configurable to fit your organization's needs, but the default behavior is as
follows.
- Strings of length at least 1, and at most 63
- Must consist of sequences of `[a-z0-9A-Z]` possibly separated by one of `[-_.]`
- Must consist of sequences of `[a-z0-9A-Z]` possibly separated by one of
`[-_.]`
Example: `visits-tracking-service`, `CircleciBuildsDs_avro_gcs`
In addition to this, names are passed through a normalization function and then compared
to the same normalized form of other entity names and made sure to not collide. This rule
of uniqueness exists to avoid situations where e.g. both `my-component` and `MyComponent`
are registered side by side, which leads to confusion and risk. The normalization function
is also configurable, but the default behavior is as follows.
In addition to this, names are passed through a normalization function and then
compared to the same normalized form of other entity names and made sure to not
collide. This rule of uniqueness exists to avoid situations where e.g. both
`my-component` and `MyComponent` are registered side by side, which leads to
confusion and risk. The normalization function is also configurable, but the
default behavior is as follows.
- Strip out all characters outside of the set `[a-zA-Z0-9]`
- Convert to lowercase
@@ -148,55 +153,59 @@ Example: `CircleciBuildsDs_avro_gcs` -> `circlecibuildsdsavrogcs`
### `namespace`
The `name` of a namespace that the entity belongs to. This field is optional, and currently
has no special semantics apart from bounding the name uniqueness constraint if specified.
It is reserved for future use and may get broader semantic implication.
The `name` of a namespace that the entity belongs to. This field is optional,
and currently has no special semantics apart from bounding the name uniqueness
constraint if specified. It is reserved for future use and may get broader
semantic implication.
Namespaces may also be part of the catalog, and are `v1` / `Namespace` entities,
i.e. not Backstage specific but the same as in Kubernetes.
### `description`
A human readable description of the entity, to be shown in Backstage. Should be kept short
and informative, suitable to give an overview of the entity's purpose at a glance. More
detailed explanations and documentation should be placed elsewhere.
A human readable description of the entity, to be shown in Backstage. Should be
kept short and informative, suitable to give an overview of the entity's purpose
at a glance. More detailed explanations and documentation should be placed
elsewhere.
### `labels`
Labels are optional key/value pairs of that are attached to the entity, and their use is
identical to [kubernetes object labels](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/).
Labels are optional key/value pairs of that are attached to the entity, and
their use is identical to
[kubernetes object labels](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/).
Their main purpose is for references to other entities, and for information that is
in one way or another classifying for the current entity. They are often used as values
in queries or filters.
Their main purpose is for references to other entities, and for information that
is in one way or another classifying for the current entity. They are often used
as values in queries or filters.
Both the key and the value are strings, subject to the following restrictions.
Keys have an optional prefix followed by a slash, and then the name part which is required.
The prefix must be a valid lowercase domain name, at most 253 characters in total. The name
part must be sequences of `[a-zA-Z0-9]` separated by any of `[-_.]`, at most 63 characters
in total.
Keys have an optional prefix followed by a slash, and then the name part which
is required. The prefix must be a valid lowercase domain name, at most 253
characters in total. The name part must be sequences of `[a-zA-Z0-9]` separated
by any of `[-_.]`, at most 63 characters in total.
The `backstage.io/` prefix is reserved for use by Backstage core components. Some keys such as
`system` also have predefined semantics.
The `backstage.io/` prefix is reserved for use by Backstage core components.
Some keys such as `system` also have predefined semantics.
Values are strings that follow the same restrictions as `name` above.
### `annotations`
An object with arbitrary non-identifying metadata attached to the entity,
identical in use to [kubernetes object annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/).
identical in use to
[kubernetes object annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/).
Their purpose is mainly, but not limited, to reference into external systems. This could
for example be a reference to the git ref the entity was ingested from, to monitoring
and logging systems, to pagerduty schedules, etc.
Their purpose is mainly, but not limited, to reference into external systems.
This could for example be a reference to the git ref the entity was ingested
from, to monitoring and logging systems, to pagerduty schedules, etc.
Both the key and the value are strings, subject to the following restrictions.
Keys have an optional prefix followed by a slash, and then the name part which is required.
The prefix must be a valid lowercase domain name, at most 253 characters in total. The name
part must be sequences of `[a-zA-Z0-9]` separated by any of `[-_.]`, at most 63 characters
in total.
Keys have an optional prefix followed by a slash, and then the name part which
is required. The prefix must be a valid lowercase domain name, at most 253
characters in total. The name part must be sequences of `[a-zA-Z0-9]` separated
by any of `[-_.]`, at most 63 characters in total.
The `backstage.io/` prefix is reserved for use by Backstage core components.
@@ -6,30 +6,50 @@
## Context
When CommonJS was the primary authoring format, the best practice was to export only one thing from a module using the `module.exports = ...` format. This aligned with the [UNIX philosophy](https://en.wikipedia.org/wiki/Unix_philosophy) of "Do one thing well". The module would be consumed (`const localName = require('the-module');`) without having to know the internal structure.
When CommonJS was the primary authoring format, the best practice was to export
only one thing from a module using the `module.exports = ...` format. This
aligned with the
[UNIX philosophy](https://en.wikipedia.org/wiki/Unix_philosophy) of "Do one
thing well". The module would be consumed
(`const localName = require('the-module');`) without having to know the internal
structure.
Now, ESModules are the primary authoring format. They have numerous benefits, such as compile-time verification of exports, and standards-defined semantics. They have a similar mechanism known as "default exports", which allows for a consumer to `import localName from 'the-module';`. This is implicitly the same as `import { default as localName } from 'the-module';`.
Now, ESModules are the primary authoring format. They have numerous benefits,
such as compile-time verification of exports, and standards-defined semantics.
They have a similar mechanism known as "default exports", which allows for a
consumer to `import localName from 'the-module';`. This is implicitly the same
as `import { default as localName } from 'the-module';`.
However, there are numerous reasons to avoid default exports, as documented by others before:
However, there are numerous reasons to avoid default exports, as documented by
others before:
- https://humanwhocodes.com/blog/2019/01/stop-using-default-exports-javascript-module/
A summary:
- They add indirection by encouraging a developer to create local names for modules, increasing cognitive load and slowing down code comprehension: `import TheListThing from 'not-a-list-thing';`.
- They thwart tools, such as IDEs, that can automatically rename and refactor code.
- They promote typos and mistakes, as the imported member is completely up to the consuming developer to define.
- They are ugly in CommonJS interop, as the default property must be manually specified by the consumer. This is often hidden by Babel's module interop.
- They break re-exports due to name conflicts, forcing the developer to manually name each.
- They add indirection by encouraging a developer to create local names for
modules, increasing cognitive load and slowing down code comprehension:
`import TheListThing from 'not-a-list-thing';`.
- They thwart tools, such as IDEs, that can automatically rename and refactor
code.
- They promote typos and mistakes, as the imported member is completely up to
the consuming developer to define.
- They are ugly in CommonJS interop, as the default property must be manually
specified by the consumer. This is often hidden by Babel's module interop.
- They break re-exports due to name conflicts, forcing the developer to manually
name each.
Using named exports helps prevent needing to rename symbols, which has myriad benefts. A few are:
Using named exports helps prevent needing to rename symbols, which has myriad
benefts. A few are:
- IDE tools like "Find All References" and "Go To Definition" function
- Manual codebase searching ("grep", etc) is easier with a unique symbol
## Decision
We will stop using default exports except when absolutely necessary (such as [`React.lazy`](https://reactjs.org/docs/code-splitting.html#reactlazy) modules). A workaround exists for those that would prefer to never use `default`:
We will stop using default exports except when absolutely necessary (such as
[`React.lazy`](https://reactjs.org/docs/code-splitting.html#reactlazy) modules).
A workaround exists for those that would prefer to never use `default`:
```ts
const Component = React.lazy(() =>
@@ -39,6 +59,9 @@ const Component = React.lazy(() =>
## Consequences
We will actively work to remove them from our codebases, being as explicit as possible. Have a connected component? `export const ConnectedComponent = connect(Component)`.
We will actively work to remove them from our codebases, being as explicit as
possible. Have a connected component?
`export const ConnectedComponent = connect(Component)`.
We will add tools, such as lint rules, to help migrate away from default exports.
We will add tools, such as lint rules, to help migrate away from default
exports.
@@ -6,7 +6,8 @@
## Context
With a growing number of exports of packages like `@backstage/core`, it is becoming more and more difficult to answer questions such as
With a growing number of exports of packages like `@backstage/core`, it is
becoming more and more difficult to answer questions such as
> Is the export in this module also exported by the package?
@@ -14,12 +15,19 @@ or
> What is exported from this directory?
We currently do not use any pattern for how to structure exports. There is a mix of package-level re-exports deep into the directory tree, shallow re-exports for each directory, exports using `*` and explicit lists of each symbol, etc.
The mix and lack of predictability makes it difficult to reason about the boundaries of a module, and for example knowing whether is is safe to export a symbol in a given file.
We currently do not use any pattern for how to structure exports. There is a mix
of package-level re-exports deep into the directory tree, shallow re-exports for
each directory, exports using `*` and explicit lists of each symbol, etc. The
mix and lack of predictability makes it difficult to reason about the boundaries
of a module, and for example knowing whether is is safe to export a symbol in a
given file.
## Decision
We will make each exported symbol traceable through index files all the way down to the root of the package, `src/index.ts`. Each index file will only re-export from its own immediate directory children, and only index files will have re-exports. This gives a file tree similar to this:
We will make each exported symbol traceable through index files all the way down
to the root of the package, `src/index.ts`. Each index file will only re-export
from its own immediate directory children, and only index files will have
re-exports. This gives a file tree similar to this:
```text
index.ts
@@ -33,16 +41,25 @@ lib/index.ts
/helper.ts
```
To check whether for example `SubComponentY` is exported from the package, it should be possible to traverse the index files towards the root, starting at the adjacent one. If there is any index file that doesn't export the previous one, the symbol is not publicly exported. For example, if `components/ComponentX/index.ts` exports `SubComponentY`, but `components/index.ts` does not re-export `./ComponentX`, one should be certain that `SubComponentY` is not exported outside the package. This rule would be broken if for example the root `index.ts` re-exports `./components/ComponentX`
To check whether for example `SubComponentY` is exported from the package, it
should be possible to traverse the index files towards the root, starting at the
adjacent one. If there is any index file that doesn't export the previous one,
the symbol is not publicly exported. For example, if
`components/ComponentX/index.ts` exports `SubComponentY`, but
`components/index.ts` does not re-export `./ComponentX`, one should be certain
that `SubComponentY` is not exported outside the package. This rule would be
broken if for example the root `index.ts` re-exports `./components/ComponentX`
In addition, index files that are re-exporting other index files should always use wildcard form, that is:
In addition, index files that are re-exporting other index files should always
use wildcard form, that is:
```ts
// in components/index.ts
export * from './ComponentX';
```
Index files that are re-exporting symbols from non-index files should always enumerate all exports, that is:
Index files that are re-exporting symbols from non-index files should always
enumerate all exports, that is:
```ts
// in components/ComponentX/index.ts
@@ -50,14 +67,16 @@ export { ComponentX } from './ComponentX';
export type { ComponentXProps } from './ComponentX';
```
Internal cross-directory imports are allowed from non-index modules to index modules, for example:
Internal cross-directory imports are allowed from non-index modules to index
modules, for example:
```ts
// in components/ComponentX/ComponentX.tsx
import { UtilityX } from '../../lib/UtilityX';
```
Imports that bypass an index file are discouraged, but may sometimes be necessary, for example:
Imports that bypass an index file are discouraged, but may sometimes be
necessary, for example:
```ts
// in components/ComponentX/ComponentX.tsx
@@ -66,6 +85,9 @@ import { helperFunc } from '../../lib/UtilityX/helper';
## Consequences
We will actively work to rework the export structure in our codebase, prioritizing the library packages such as `@backstage/core` and `@backstage/backend-common`.
We will actively work to rework the export structure in our codebase,
prioritizing the library packages such as `@backstage/core` and
`@backstage/backend-common`.
If possible, we will add tools, such as lint rules, to help enforce the export structure.
If possible, we will add tools, such as lint rules, to help enforce the export
structure.
@@ -6,7 +6,8 @@
## Context
We want to standardize on a few core entities that we are tracking in the Backstage catalog. This allows us to build specific plugins around them.
We want to standardize on a few core entities that we are tracking in the
Backstage catalog. This allows us to build specific plugins around them.
## Decision
@@ -14,17 +15,26 @@ Backstage should eventually support the following core entities:
- **Components** are individual pieces of software
- **APIs** are the boundaries between different components
- **Resources** are physical or virtual infrastructure needed to operate a component
- **Resources** are physical or virtual infrastructure needed to operate a
component
![Catalog Core Entities](catalog-core-entities.png)
For now, we'll start by only implementing support for the Component entity in the Backstage catalog. This can later be extended to APIs, Resources and other potentially useful entities.
For now, we'll start by only implementing support for the Component entity in
the Backstage catalog. This can later be extended to APIs, Resources and other
potentially useful entities.
### Component
A component is a piece of software, for example a mobile application feature, web site, backend service or data pipeline (list not exhaustive). A component can be tracked in source control, or use some existing open source or commercial software. It can implement APIs for other components to consume. In turn it might depend on APIs implemented by other components, or resources that are attached to it at runtime.
A component is a piece of software, for example a mobile application feature,
web site, backend service or data pipeline (list not exhaustive). A component
can be tracked in source control, or use some existing open source or commercial
software. It can implement APIs for other components to consume. In turn it
might depend on APIs implemented by other components, or resources that are
attached to it at runtime.
Component entities are typically defined in YAML descriptor files next to the code of the component, and could look like this (actual schema will evolve):
Component entities are typically defined in YAML descriptor files next to the
code of the component, and could look like this (actual schema will evolve):
```yaml
apiVersion: backstage.io/v1beta1
@@ -37,11 +47,20 @@ spec:
### API
APIs form an abstraction that allows large software ecosystems to scale. Thus, APIs are a first class citizen in the Backstage model and the primary way to discover existing functionality in the ecosystem.
APIs form an abstraction that allows large software ecosystems to scale. Thus,
APIs are a first class citizen in the Backstage model and the primary way to
discover existing functionality in the ecosystem.
APIs are implemented by components and make their boundaries explicit. They might be defined using an RPC IDL (e.g. in Protobuf, GraphQL or similar), a data schema (e.g. in Avro, TFRecord or similar), or as code interfaces (e.g. framework APIs in Swift, Kotlin, Java, C++, Typescript etc). In any case, APIs exposed by components need to be in a known machine-readable format so we can build further tooling and analysis on top.
APIs are implemented by components and make their boundaries explicit. They
might be defined using an RPC IDL (e.g. in Protobuf, GraphQL or similar), a data
schema (e.g. in Avro, TFRecord or similar), or as code interfaces (e.g.
framework APIs in Swift, Kotlin, Java, C++, Typescript etc). In any case, APIs
exposed by components need to be in a known machine-readable format so we can
build further tooling and analysis on top.
APIs are typically indexed from existing definitions in source control and thus wouldn't need their own descriptor files, but would be stored in the catalog somewhat like this (actual schema will evolve):
APIs are typically indexed from existing definitions in source control and thus
wouldn't need their own descriptor files, but would be stored in the catalog
somewhat like this (actual schema will evolve):
```yaml
apiVersion: backstage.io/v1beta1
@@ -54,9 +73,11 @@ spec:
service HelloService {
rpc SayHello (HelloRequest) returns (HelloResponse);
}
message HelloRequest {
string greeting = 1;
}
message HelloResponse {
string reply = 1;
}
@@ -64,9 +85,16 @@ spec:
### Resource
Resources are the infrastructure your software needs to operate at runtime like Bigtable databases, Pub/Sub topics, S3 buckets or CDNs. Modelling them together with components and APIs will allow us to visualize and create tooling around them in Backstage.
Resources are the infrastructure your software needs to operate at runtime like
Bigtable databases, Pub/Sub topics, S3 buckets or CDNs. Modelling them together
with components and APIs will allow us to visualize and create tooling around
them in Backstage.
Resources are typically indexed from declarative definitions (e.g. Terraform, GCP Config Connector, AWS Cloud Formation) and/or inventories from cloud providers (e.g. GCP Asset Inventory) and thus wouldn't need their own descriptor files, but would be stored in the catalog somewhat like this (actual schema will evolve):
Resources are typically indexed from declarative definitions (e.g. Terraform,
GCP Config Connector, AWS Cloud Formation) and/or inventories from cloud
providers (e.g. GCP Asset Inventory) and thus wouldn't need their own descriptor
files, but would be stored in the catalog somewhat like this (actual schema will
evolve):
```yaml
apiVersion: backstage.io/v1beta1
@@ -80,4 +108,5 @@ spec:
## Consequences
We will continue fleshing out support for the Component entity in the Backstage catalog.
We will continue fleshing out support for the Component entity in the Backstage
catalog.
+12 -3
View File
@@ -1,9 +1,18 @@
# Architecture and Terminology
Backstage is constructed out of three parts. We separate Backstage in this way because we see three groups of contributors that work with Backstage in three different ways.
Backstage is constructed out of three parts. We separate Backstage in this way
because we see three groups of contributors that work with Backstage in three
different ways.
- Core - Base functionality built by core devs in the open source project.
- App - The app is an instance of a Backstage app that is deployed and tweaked. The app ties together core functionality with additional plugins. The app is built and maintained by app developers, usually a productivity team within a company.
- Plugins - Additional functionality to make your Backstage app useful for your company. Plugins can be specific to a company or open sourced and reusable. At Spotify we have over 100 plugins built by over 50 different teams. It has been very powerful to get contributions from various infrastructure teams added into a single unified developer experience.
- App - The app is an instance of a Backstage app that is deployed and tweaked.
The app ties together core functionality with additional plugins. The app is
built and maintained by app developers, usually a productivity team within a
company.
- Plugins - Additional functionality to make your Backstage app useful for your
company. Plugins can be specific to a company or open sourced and reusable. At
Spotify we have over 100 plugins built by over 50 different teams. It has been
very powerful to get contributions from various infrastructure teams added
into a single unified developer experience.
[Back to Docs](README.md)
+51 -20
View File
@@ -2,17 +2,21 @@
## Passport
We chose [Passport](http://www.passportjs.org/) as our authentication platform due to its comprehensive set of supported authentication [strategies](http://www.passportjs.org/packages/).
We chose [Passport](http://www.passportjs.org/) as our authentication platform
due to its comprehensive set of supported authentication
[strategies](http://www.passportjs.org/packages/).
## How to add a new strategy provider
### Quick guide
[1.](#installing-the-dependencies) Install the passport-js based provider package.
[1.](#installing-the-dependencies) Install the passport-js based provider
package.
[2.](#create-implementation) Create a new folder structure for the provider.
[3.](#adding-an-oauth-based-provider) Implement the provider, extending the suitable framework if needed.
[3.](#adding-an-oauth-based-provider) Implement the provider, extending the
suitable framework if needed.
[4.](#hook-it-up-to-the-backend) Add the provider to the backend.
@@ -26,7 +30,8 @@ yarn add @types/passport-provider-a
### Create implementation
Make a new folder with the name of the provider following the below file structure:
Make a new folder with the name of the provider following the below file
structure:
```bash
plugins/auth-backend/src/providers/providerA
@@ -34,13 +39,16 @@ plugins/auth-backend/src/providers/providerA
└── provider.ts
```
**`plugins/auth-backend/src/providers/providerA/provider.ts`** defines the provider class which implements a handler for the chosen framework.
**`plugins/auth-backend/src/providers/providerA/provider.ts`** defines the
provider class which implements a handler for the chosen framework.
#### Adding an OAuth based provider
If we're adding an `OAuth` based provider we would implement the [OAuthProviderHandlers](#OAuthProviderHandlers) interface.
If we're adding an `OAuth` based provider we would implement the
[OAuthProviderHandlers](#OAuthProviderHandlers) interface.
The provider class takes the provider's configuration as a class parameter. It also imports the `Strategy` from the passport package.
The provider class takes the provider's configuration as a class parameter. It
also imports the `Strategy` from the passport package.
```ts
import { Strategy as ProviderAStrategy } from 'passport-provider-a';
@@ -64,9 +72,11 @@ export class ProviderAAuthProvider implements OAuthProviderHandlers {
#### Adding an non-OAuth based provider
_**Note**: We have prioritized OAuth-based providers and non-OAuth providers should be considered experimental._
_**Note**: We have prioritized OAuth-based providers and non-OAuth providers
should be considered experimental._
An non-`OAuth` based provider could implement [AuthProviderRouteHandlers](#AuthProviderRouteHandlers) instead.
An non-`OAuth` based provider could implement
[AuthProviderRouteHandlers](#AuthProviderRouteHandlers) instead.
```ts
export class ProviderAAuthProvider implements AuthProviderRouteHandlers {
@@ -90,9 +100,12 @@ export class ProviderAAuthProvider implements AuthProviderRouteHandlers {
#### Create method
Each provider exports a create method that creates the provider instance, optionally extending a supported authorization framework. This method exists to allow for flexibility if additional frameworks are supported in the future.
Each provider exports a create method that creates the provider instance,
optionally extending a supported authorization framework. This method exists to
allow for flexibility if additional frameworks are supported in the future.
Implementing OAuth by returning an instance of `OAuthProvider` based of the provider's class:
Implementing OAuth by returning an instance of `OAuthProvider` based of the
provider's class:
```ts
export function createProviderAProvider(config: AuthProviderConfig) {
@@ -102,7 +115,9 @@ export function createProviderAProvider(config: AuthProviderConfig) {
}
```
Not extending with OAuth, the main difference here is that the create method is returning a instance of the class without adding the OAuth authorization framework to it.
Not extending with OAuth, the main difference here is that the create method is
returning a instance of the class without adding the OAuth authorization
framework to it.
```ts
export function createProviderAProvider(config: AuthProviderConfig) {
@@ -112,14 +127,22 @@ export function createProviderAProvider(config: AuthProviderConfig) {
#### Verify Callback
> Strategies require what is known as a verify callback. The purpose of a verify callback is to find the user that possesses a set of credentials.
> When Passport authenticates a request, it parses the credentials contained in the request. It then invokes the verify callback with those credentials as arguments [...]. If the credentials are valid, the verify callback invokes done to supply Passport with the user that authenticated.
> Strategies require what is known as a verify callback. The purpose of a verify
> callback is to find the user that possesses a set of credentials. When
> Passport authenticates a request, it parses the credentials contained in the
> request. It then invokes the verify callback with those credentials as
> arguments [...]. If the credentials are valid, the verify callback invokes
> done to supply Passport with the user that authenticated.
>
> If the credentials are not valid (for example, if the password is incorrect), done should be invoked with false instead of a user to indicate an authentication failure.
> If the credentials are not valid (for example, if the password is incorrect),
> done should be invoked with false instead of a user to indicate an
> authentication failure.
>
> http://www.passportjs.org/docs/configure/
**`plugins/auth-backend/src/providers/providerA/index.ts`** is simply re-exporting the create method to be used for hooking the provider up to the backend.
**`plugins/auth-backend/src/providers/providerA/index.ts`** is simply
re-exporting the create method to be used for hooking the provider up to the
backend.
```ts
export { createProviderAProvider } from './provider';
@@ -127,7 +150,9 @@ export { createProviderAProvider } from './provider';
### Hook it up to the backend
**`plugins/auth-backend/src/providers/config.ts`** The provider needs to be configured properly so you need to add it to the list of configured providers, all of which implement [AuthProviderConfig](#AuthProviderConfig):
**`plugins/auth-backend/src/providers/config.ts`** The provider needs to be
configured properly so you need to add it to the list of configured providers,
all of which implement [AuthProviderConfig](#AuthProviderConfig):
```ts
export const providers = [
@@ -138,7 +163,10 @@ export const providers = [
},
```
**`plugins/auth-backend/src/providers/factories.ts`** When the `auth-backend` starts it sets up routing for all the available providers by calling `createAuthProviderRouter` on each provider. You need to import the create method from the provider and add it to the factory:
**`plugins/auth-backend/src/providers/factories.ts`** When the `auth-backend`
starts it sets up routing for all the available providers by calling
`createAuthProviderRouter` on each provider. You need to import the create
method from the provider and add it to the factory:
```ts
import { createProviderAProvider } from './providerA';
@@ -157,11 +185,14 @@ router.post('/auth/providerA/logout');
router.get('/auth/providerA/refresh'); // if supported
```
As you can see each endpoint is prefixed with both `/auth` and its provider name.
As you can see each endpoint is prefixed with both `/auth` and its provider
name.
### Test the new provider
You can `curl -i localhost:7000/auth/providerA/start` and which should provide a `302` redirect with a `Location` header. Paste the url from that header into a web browser and you should be able to trigger the authorization flow.
You can `curl -i localhost:7000/auth/providerA/start` and which should provide a
`302` redirect with a `Location` header. Paste the url from that header into a
web browser and you should be able to trigger the authorization flow.
---
+13 -6
View File
@@ -1,12 +1,16 @@
# Backstage App
To get set up quickly with your own Backstage project you can create a Backstage App.
To get set up quickly with your own Backstage project you can create a Backstage
App.
A Backstage App is a monorepo setup with `lerna` that includes everything you need to run Backstage in your own environment.
A Backstage App is a monorepo setup with `lerna` that includes everything you
need to run Backstage in your own environment.
## Create an app
To create a Backstage app, you will need to have [NodeJS](https://nodejs.org/en/download/) Active LTS Release installed (currently v12).
To create a Backstage app, you will need to have
[NodeJS](https://nodejs.org/en/download/) Active LTS Release installed
(currently v12).
With `npx`:
@@ -14,13 +18,15 @@ With `npx`:
npx @backstage/cli create-app
```
This will create a new Backstage App inside the current folder. The name of the app-folder is the name that was provided when prompted.
This will create a new Backstage App inside the current folder. The name of the
app-folder is the name that was provided when prompted.
<p align='center'>
<img src='https://github.com/spotify/backstage/raw/master/docs/create-app_output.png' width='600' alt='create app'>
</p>
Inside that directory, it will generate all the files and folder structure needed for you to run your app.
Inside that directory, it will generate all the files and folder structure
needed for you to run your app.
### Folder structure
@@ -69,4 +75,5 @@ cd my-backstage-app
yarn start
```
_When `yarn start` is ready it should open up a browser window displaying your app, if not you can navigate to `http://localhost:3000`._
_When `yarn start` is ready it should open up a browser window displaying your
app, if not you can navigate to `http://localhost:3000`._
+89 -25
View File
@@ -1,70 +1,134 @@
![header](designheader.png)
Much like Backstage Open Source, this is a *living* document! We'll keep this updated as we evolve our practices!
Much like Backstage Open Source, this is a _living_ document! We'll keep this
updated as we evolve our practices!
## 📚 Our Philosophy
### Iterative
Backstage Open Source is a newly launched endeavor, and were excited to scale up our design practices! With that said, well be working closely with you, the community, and iterating and experimenting as we go to see what works best. As a continual work in progress, we aspire to release early and often. Not only that, we are committed to working with developers to create a seamless and easy handoff. If youre curious to see how we grow and would like to play a role in that growth, check out the issues in this GitHub repo!
Backstage Open Source is a newly launched endeavor, and were excited to scale
up our design practices! With that said, well be working closely with you, the
community, and iterating and experimenting as we go to see what works best. As a
continual work in progress, we aspire to release early and often. Not only that,
we are committed to working with developers to create a seamless and easy
handoff. If youre curious to see how we grow and would like to play a role in
that growth, check out the issues in this GitHub repo!
### Collaborative
The Backstage Design Team is small but mighty, and we truly cherish the amazing opportunity we have to work with the Backstage Open Source community! Have an idea? A component request? Feel free to communicate with us via [Discord](https://discord.gg/EBHEGzX) (*#design* channel). Collaboration trumps individual speed, and we want to work with you to make Backstage work for all of our users.
The Backstage Design Team is small but mighty, and we truly cherish the amazing
opportunity we have to work with the Backstage Open Source community! Have an
idea? A component request? Feel free to communicate with us via
[Discord](https://discord.gg/EBHEGzX) (_#design_ channel). Collaboration trumps
individual speed, and we want to work with you to make Backstage work for all of
our users.
### Transparent
There are a lot of exciting things coming up and we want to keep you in the loop! Keep an eye on our Milestones in GitHub to see where were headed. Well also be posting updates in the *#design* channel on [Discord](https://discord.gg/EBHEGzX). Not only that, we want to keep you informed on the decisions weve made and why weve made them.
There are a lot of exciting things coming up and we want to keep you in the
loop! Keep an eye on our Milestones in GitHub to see where were headed. Well
also be posting updates in the _#design_ channel on
[Discord](https://discord.gg/EBHEGzX). Not only that, we want to keep you
informed on the decisions weve made and why weve made them.
## 🛠 Our Practice
The chart below details how we work. ***Stay tuned***: We are currently in the process of securing a Figma workspace for Backstage Open Source, and we plan on referencing Figma documents to share specs and prototypes with the community.
The chart below details how we work. **_Stay tuned_**: We are currently in the
process of securing a Figma workspace for Backstage Open Source, and we plan on
referencing Figma documents to share specs and prototypes with the community.
### Creating a New Design Component
| Step 1 | Step 2 | Step 3 | Step 4 | Step 5 | Step 6 |
|:---|:---|:---|:---|:---|:---|
| Platform design team submits an issue to **spotify/Backstage GitHub** with a potential component. | Backstage community offers feedback or approval on **spotify/Backstage GitHub**. | Platform design team adjusts accordingly (as they see fit) and update the Figma DLS document. | Designed component is added to **spotify/Backstage GitHub** as an issue. | External or internal Backstage open source contributors build the component. | External or internal contributors add the component to the **Backstage Storybook**. 🎉 |
| Step 1 | Step 2 | Step 3 | Step 4 | Step 5 | Step 6 |
| :------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------- | :--------------------------------------------------------------------------- | :------------------------------------------------------------------------------------- |
| Platform design team submits an issue to **spotify/Backstage GitHub** with a potential component. | Backstage community offers feedback or approval on **spotify/Backstage GitHub**. | Platform design team adjusts accordingly (as they see fit) and update the Figma DLS document. | Designed component is added to **spotify/Backstage GitHub** as an issue. | External or internal Backstage open source contributors build the component. | External or internal contributors add the component to the **Backstage Storybook**. 🎉 |
### Building for Backstage
| Step 1 | Step 2 | Step 3 | Step 4 |
|:---|:---|:---|:---|
| External or internal contributors use Backstage and come up with an idea of an entity to build for Backstage. |External or internal contributors refer to the Backstage Open Source design system documentation in the Figma DLS document. | External or internal contributors leverage the components and tokens from the Backstage Storybook. | External or internal contributors build their Backstage entity. |
| Step 5 | Step 6 | Step 7 | Step 8 |
|:---|:---|:---|:---|
| External or internal contributors make a pull request for their entity on spotify/Backstage GitHub for review. | Platform designers and devs review the entity and submit feedback or approval on spotify/Backstage GitHub. | External or internal contributors make the changes, pull request is approved and the entity is merged. Its live on Backstage! 🎉 | If the entity happens to be or include a UX component, its added to Backstage Storybook as well. |
| Step 1 | Step 2 | Step 3 | Step 4 |
| :------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------- |
| External or internal contributors use Backstage and come up with an idea of an entity to build for Backstage. | External or internal contributors refer to the Backstage Open Source design system documentation in the Figma DLS document. | External or internal contributors leverage the components and tokens from the Backstage Storybook. | External or internal contributors build their Backstage entity. |
| Step 5 | Step 6 | Step 7 | Step 8 |
| :------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------ |
| External or internal contributors make a pull request for their entity on spotify/Backstage GitHub for review. | Platform designers and devs review the entity and submit feedback or approval on spotify/Backstage GitHub. | External or internal contributors make the changes, pull request is approved and the entity is merged. Its live on Backstage! 🎉 | If the entity happens to be or include a UX component, its added to Backstage Storybook as well. |
The following diagram shows the relationship between the Backstage Design System and our foundation, which comprises of [Material UI](https://material-ui.com/) that is shaped by user experience and user interface decisions made by our Backstage Design Team. Also note, we encourage you to take the core experience weve crafted and add custom theming to better represent your organization!
The following diagram shows the relationship between the Backstage Design System
and our foundation, which comprises of [Material UI](https://material-ui.com/)
that is shaped by user experience and user interface decisions made by our
Backstage Design Team. Also note, we encourage you to take the core experience
weve crafted and add custom theming to better represent your organization!
![dls](DLS.png)
## ✅ Our Priorities
### Backstage Design System
This is the set of building blocks for Backstage contributors to leverage as they create rad plugins for Backstage! Why reinvent the wheel when you can use components that have already been vetted by our team and the Backstage community? In the spirit of crafting a cohesive and consistent user experience across all of Backstage, we strongly urge all plugin developers to utilize our Storybook as a reference. Our design system is new and evolving, and well be building it up with your help!
This is the set of building blocks for Backstage contributors to leverage as
they create rad plugins for Backstage! Why reinvent the wheel when you can use
components that have already been vetted by our team and the Backstage
community? In the spirit of crafting a cohesive and consistent user experience
across all of Backstage, we strongly urge all plugin developers to utilize our
Storybook as a reference. Our design system is new and evolving, and well be
building it up with your help!
### Core Backstage User Experience
This is the universal user experience that is shared amongst all Backstage users. From more concrete aspects like the plugins marketplace to more abstract ones like end-to-end workflows on Backstage, well be working with the community to create a core user experience that best serves you and your organization.
This is the universal user experience that is shared amongst all Backstage
users. From more concrete aspects like the plugins marketplace to more abstract
ones like end-to-end workflows on Backstage, well be working with the community
to create a core user experience that best serves you and your organization.
## ⭐️ How to Contribute
### Pick up an issue!
In the beginning, most of our issues will be centered around creating universal components for our Backstage Design System and adding them to our Storybook so plugin developers can reference them. Well also be creating issues that are focused on building up our core Backstage user experience. Well be labeling our issues in GitHub with design and/or storybook - so feel free to browse and tackle the tasks that interest you. If you have any questions regarding an issue, you can ask them in the comments section of the issue or on [Discord](https://discord.gg/EBHEGzX). We absolutely adore our external contributors and will send you virtual semlas for your contributions!
### Pick up an issue!
In the beginning, most of our issues will be centered around creating universal
components for our Backstage Design System and adding them to our Storybook so
plugin developers can reference them. Well also be creating issues that are
focused on building up our core Backstage user experience. Well be labeling our
issues in GitHub with design and/or storybook - so feel free to browse and
tackle the tasks that interest you. If you have any questions regarding an
issue, you can ask them in the comments section of the issue or on
[Discord](https://discord.gg/EBHEGzX). We absolutely adore our external
contributors and will send you virtual semlas for your contributions!
### Request a component.
Create an issue (label it design and assign it to katz95) or send us a message on [Discord](https://discord.gg/EBHEGzX) (*#design* channel) with details of what the component is and its relevant use cases. Your request will be reviewed by our design team and you should hear back from us within 1-2 business days. Well get back to you and let you know whether your requested component will get picked up by our team as something to be added to our design system.
Create an issue (label it design and assign it to katz95) or send us a message
on [Discord](https://discord.gg/EBHEGzX) (_#design_ channel) with details of
what the component is and its relevant use cases. Your request will be reviewed
by our design team and you should hear back from us within 1-2 business days.
Well get back to you and let you know whether your requested component will get
picked up by our team as something to be added to our design system.
## ✏️ Resources
**[Storybook](http://storybook.backstage.io/)** - where you can view our components. If youd like to help build up our design system, you can also add components weve designed to the Storybook as well.
**[Discord](https://discord.gg/EBHEGzX)** - all design questions should be directed to the *#design* channel.
**[Storybook](http://storybook.backstage.io/)** - where you can view our
components. If youd like to help build up our design system, you can also add
components weve designed to the Storybook as well.
**[Discord](https://discord.gg/EBHEGzX)** - all design questions should be
directed to the _#design_ channel.
**Documentation**
- Patterns (stay tuned)
- Figma files/libraries (stay tuned)
## 🔮 Future
### Contributions from designers
Are you a designer at an organisation thats implementing Backstage? A designer whos fascinated by the developer productivity problem space? A designer whos curious about open source design? Wed love for you to contribute. Behind the scenes, were setting up a few foundational elements to make sure that contributing to Backstage as a designer is easy. From styling guidelines to UX principles to Figma documents, well make sure youre equipped to chip in on this project. Were excited to work with you! In the meantime, wed love to hear from you on [Discord](https://discord.gg/EBHEGzX).
### Contributions from designers
Are you a designer at an organisation thats implementing Backstage? A designer
whos fascinated by the developer productivity problem space? A designer whos
curious about open source design? Wed love for you to contribute. Behind the
scenes, were setting up a few foundational elements to make sure that
contributing to Backstage as a designer is easy. From styling guidelines to UX
principles to Figma documents, well make sure youre equipped to chip in on
this project. Were excited to work with you! In the meantime, wed love to hear
from you on [Discord](https://discord.gg/EBHEGzX).
[Back to Docs](../README.md)
+10 -8
View File
@@ -2,10 +2,10 @@
Backstage plugins provide features to a Backstage App.
Each plugin is treated as a self-contained web app and can include almost any type of content.
Plugins all use a common set of platform APIs and reusable UI components.
Plugins can fetch data from external sources using the regular browser APIs or by depending on
external modules to do the work.
Each plugin is treated as a self-contained web app and can include almost any
type of content. Plugins all use a common set of platform APIs and reusable UI
components. Plugins can fetch data from external sources using the regular
browser APIs or by depending on external modules to do the work.
<!-- MOVED TO create-a-plugin.md ## Creating a new plugin
On your command line, invoke the `backstage-cli` to create a new plugin:
@@ -27,17 +27,19 @@ plugin directly by navigating to `http://localhost:3000/my-plugin`.*
- Consider writing plugins in `TypeScript`.
- Plan the directory structure of your plugin so that it becomes easy to manage.
- Prefer using the Backstage components, otherwise go with [Material-UI](https://material-ui.com/).
- Prefer using the Backstage components, otherwise go with
[Material-UI](https://material-ui.com/).
- Check out the shared Backstage APIs before building a new one.
## Plugin concepts / API
### Routing
Each plugin is responsible for registering its components to corresponding routes in the app.
Each plugin is responsible for registering its components to corresponding
routes in the app.
The app will call the `createPlugin` method on each plugin, passing in a `router` object with a set
of methods on it.
The app will call the `createPlugin` method on each plugin, passing in a
`router` object with a set of methods on it.
```jsx
import { createPlugin, createRouteRef } from '@backstage/core';
+3 -1
View File
@@ -2,7 +2,9 @@
## Creating a Plugin
The value of Backstage grows with every new plugin that gets added. Here is a collection of tutorials that will guide you through setting up and extending an instance of Backstage with your own plugins.
The value of Backstage grows with every new plugin that gets added. Here is a
collection of tutorials that will guide you through setting up and extending an
instance of Backstage with your own plugins.
- [Development Environment](development-environment.md)
- [Create a Backstage plugin](create-a-plugin.md)
+34 -10
View File
@@ -1,14 +1,21 @@
# Custom App Themes
Backstage ships with a default theme with a light and dark mode variant. The themes are provided as
a part of the [@backstage/theme](https://www.npmjs.com/package/@backstage/theme) package, which also includes
utilities for customizing the default theme, or creating completely new themes.
Backstage ships with a default theme with a light and dark mode variant. The
themes are provided as a part of the
[@backstage/theme](https://www.npmjs.com/package/@backstage/theme) package,
which also includes utilities for customizing the default theme, or creating
completely new themes.
## Creating a Custom Theme
The easiest way to create a new theme is to use the `createTheme` function exported by the [@backstage/theme](https://www.npmjs.com/package/@backstage/theme) package. You can use it to override so basic parameters of the default theme such as the color palette and font.
The easiest way to create a new theme is to use the `createTheme` function
exported by the
[@backstage/theme](https://www.npmjs.com/package/@backstage/theme) package. You
can use it to override so basic parameters of the default theme such as the
color palette and font.
For example, you can create a new theme based on the default light theme like this:
For example, you can create a new theme based on the default light theme like
this:
```ts
import { createTheme, lightTheme } from '@backstage/theme';
@@ -19,15 +26,29 @@ const myTheme = createTheme({
});
```
If you want more control over the theme, and for example customize font sizes and margins, you can use the lower-level `createThemeOverrides` function exported by [@backstage/theme](https://www.npmjs.com/package/@backstage/theme) in combination with [createMuiTheme](https://material-ui.com/customization/theming/#createmuitheme-options-args-theme) from [@material-ui/core](https://www.npmjs.com/package/@material-ui/core). See the [@backstage/theme source](https://github.com/spotify/backstage/tree/master/packages/theme/src) and the implementation of the `createTheme` function for how this is done.
If you want more control over the theme, and for example customize font sizes
and margins, you can use the lower-level `createThemeOverrides` function
exported by [@backstage/theme](https://www.npmjs.com/package/@backstage/theme)
in combination with
[createMuiTheme](https://material-ui.com/customization/theming/#createmuitheme-options-args-theme)
from [@material-ui/core](https://www.npmjs.com/package/@material-ui/core). See
the
[@backstage/theme source](https://github.com/spotify/backstage/tree/master/packages/theme/src)
and the implementation of the `createTheme` function for how this is done.
You can also create a theme from scratch that matches the `BackstageTheme` type exported by [@backstage/theme](https://www.npmjs.com/package/@backstage/theme). See the [material-ui docs on theming](https://material-ui.com/customization/theming/) for more information about how that can be done.
You can also create a theme from scratch that matches the `BackstageTheme` type
exported by [@backstage/theme](https://www.npmjs.com/package/@backstage/theme).
See the
[material-ui docs on theming](https://material-ui.com/customization/theming/)
for more information about how that can be done.
## Using your Custom Theme
To add a custom theme to your Backstage app, you pass it as configuration to `createApp`.
To add a custom theme to your Backstage app, you pass it as configuration to
`createApp`.
For example, adding the theme that we created in the previous section can be done like this:
For example, adding the theme that we created in the previous section can be
done like this:
```ts
import { createApp } from '@backstage/core';
@@ -44,4 +65,7 @@ const app = createApp({
})
```
Note that your list of custom themes overrides the default themes. If you still want to use the default themes, they are exported as `lightTheme` and `darkTheme` from [@backstage/theme](https://www.npmjs.com/package/@backstage/theme).
Note that your list of custom themes overrides the default themes. If you still
want to use the default themes, they are exported as `lightTheme` and
`darkTheme` from
[@backstage/theme](https://www.npmjs.com/package/@backstage/theme).
@@ -4,7 +4,8 @@
> A Story basically represents a single visual state of a component.
To create a new story, create a new file located alongside the component you want to document on Storybook.
To create a new story, create a new file located alongside the component you
want to document on Storybook.
See below an example of the structure:
@@ -17,16 +18,20 @@ core
└── Progress.stories.tsx
```
> _Note: make sure your component story file has the following format componentName.stories.tsx_
> _Note: make sure your component story file has the following format
> componentName.stories.tsx_
## Running locally
Go to `packages/storybook`, run `yarn install` and install the dependencies, then run the following on your command line: `yarn start`
Go to `packages/storybook`, run `yarn install` and install the dependencies,
then run the following on your command line: `yarn start`
![](running-storybook.png)
_You should see a log like the image above._
If everything worked out, your server will be running on **port 6006**, go to your browser and navigate to `http://localhost:6006/`. You should be able to navigate and see the Storybook page.
If everything worked out, your server will be running on **port 6006**, go to
your browser and navigate to `http://localhost:6006/`. You should be able to
navigate and see the Storybook page.
![](storybook-page.png)
+13 -8
View File
@@ -4,7 +4,9 @@ A Backstage Plugin adds functionality to Backstage.
## Create a plugin
To create a new plugin, make sure you've run `yarn install` and installed dependencies, then run the following on your command line (invoking the `backstage-cli`).
To create a new plugin, make sure you've run `yarn install` and installed
dependencies, then run the following on your command line (invoking the
`backstage-cli`).
```bash
yarn create-plugin
@@ -14,24 +16,27 @@ yarn create-plugin
<img src='https://github.com/spotify/backstage/raw/master/docs/getting-started/create-plugin_output.png' width='600' alt='create plugin'>
</p>
This will create a new Backstage Plugin based on the ID that was provided. It will be built and
added to the Backstage App automatically.
This will create a new Backstage Plugin based on the ID that was provided. It
will be built and added to the Backstage App automatically.
_If `yarn start` is already running you should be able to see the default page for your new
plugin directly by navigating to `http://localhost:3000/my-plugin`._
_If `yarn start` is already running you should be able to see the default page
for your new plugin directly by navigating to
`http://localhost:3000/my-plugin`._
<p align='center'>
<img src='https://github.com/spotify/backstage/raw/master/docs/getting-started/my-plugin_screenshot.png' width='600' alt='my plugin'>
</p>
You can also serve the plugin in isolation by running `yarn start` in the plugin directory. Or by using the yarn workspace command, for example:
You can also serve the plugin in isolation by running `yarn start` in the plugin
directory. Or by using the yarn workspace command, for example:
```bash
yarn workspace @backstage/plugin-welcome start # Also supports --check
```
This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads.
It is only meant for local development, and the setup for it can be found inside the plugin's `dev/` directory.
This method of serving the plugin provides quicker iteration speed and a faster
startup and hot reloads. It is only meant for local development, and the setup
for it can be found inside the plugin's `dev/` directory.
[Next Step - Structure of a plugin](structure-of-a-plugin.md)
+20 -10
View File
@@ -1,11 +1,12 @@
# Development Environment
This section describes how to get set up for doing development on the Backstage repository.
This section describes how to get set up for doing development on the Backstage
repository.
## Cloning the Repository
After you have cloned the Backstage repository, you should run the following commands
once to set things up for development:
After you have cloned the Backstage repository, you should run the following
commands once to set things up for development:
```bash
$ yarn install # fetch dependency packages - may take a while
@@ -15,18 +16,22 @@ $ yarn tsc # does a first run of type generation and checks
## Serving the Example App
Open a terminal window and start the web app by using the following command from the project root.
Make sure you have run the above mentioned commands first.
Open a terminal window and start the web app by using the following command from
the project root. Make sure you have run the above mentioned commands first.
```bash
$ yarn start
```
This should open a local instance of Backstage in your browser, otherwise open one of the URLs printed in the terminal.
This should open a local instance of Backstage in your browser, otherwise open
one of the URLs printed in the terminal.
By default, backstage will start on port 3000, however you can override this by setting an environment variable `PORT` on your local machine. e.g. `export PORT=8080` then running `yarn start`. Or `PORT=8080 yarn start`.
By default, backstage will start on port 3000, however you can override this by
setting an environment variable `PORT` on your local machine. e.g.
`export PORT=8080` then running `yarn start`. Or `PORT=8080 yarn start`.
Once successfully started, you should see the following message in your terminal window:
Once successfully started, you should see the following message in your terminal
window:
```
You can now view example-app in the browser.
@@ -37,11 +42,16 @@ You can now view example-app in the browser.
## Editor
The Backstage development environment does not require any specific editor, but it is intended to be used with one that has built-in linting and type-checking. The development server does not include any checks by default, but they can be enabled using the `--check` flag. Note that using the flag may consume more system resources and slow things down.
The Backstage development environment does not require any specific editor, but
it is intended to be used with one that has built-in linting and type-checking.
The development server does not include any checks by default, but they can be
enabled using the `--check` flag. Note that using the flag may consume more
system resources and slow things down.
## Package Scripts
There are many commands to be found in the root [package.json](package.json), here are some useful ones:
There are many commands to be found in the root [package.json](package.json),
here are some useful ones:
```python
yarn start # Start serving the example app, use --check to include type checks and linting
+25 -8
View File
@@ -1,6 +1,7 @@
# Structure of a newly created plugin
Nice, you have a new plugin! We'll soon see how we can develop it into doing great things. But first off, let's look at what we get out of the box.
Nice, you have a new plugin! We'll soon see how we can develop it into doing
great things. But first off, let's look at what we get out of the box.
## Folder structure
@@ -30,13 +31,21 @@ new-plugin/
tsconfig.json
```
You might note a thing or two. Yes, a plugin looks like a mini project on it's own with a `package.json` and a `src` folder. And this is because we want plugins to be separate packages. This makes it possible to ship plugins on npm and it lets you work on a plugin in isolation, without loading all the other plugins in a potentially big Backstage app.
You might note a thing or two. Yes, a plugin looks like a mini project on it's
own with a `package.json` and a `src` folder. And this is because we want
plugins to be separate packages. This makes it possible to ship plugins on npm
and it lets you work on a plugin in isolation, without loading all the other
plugins in a potentially big Backstage app.
The `index.ts` files are there to let us import from the folder path and not specific files. It's a way to have control over the exports in one file per folder.
The `index.ts` files are there to let us import from the folder path and not
specific files. It's a way to have control over the exports in one file per
folder.
## Base files
In the root folder you have some configuration for typescript and jest, the test runner. You get a readme to populate with info about your plugin and a package.json to declare the plugin dependencies, metadata and scripts.
In the root folder you have some configuration for typescript and jest, the test
runner. You get a readme to populate with info about your plugin and a
package.json to declare the plugin dependencies, metadata and scripts.
## The plugin file
@@ -59,13 +68,20 @@ export const plugin = createPlugin({
});
```
This is where the plugin is created and where it hooks into the app by declaring what component should be shown on what url. See reference docs for [createPlugin](../reference/createPlugin.md) or [router](../reference/createPlugin-router.md).
This is where the plugin is created and where it hooks into the app by declaring
what component should be shown on what url. See reference docs for
[createPlugin](../reference/createPlugin.md) or
[router](../reference/createPlugin-router.md).
## Components
The generated plugin includes two example components to showcase how we structure our plugins. There are usually one or multiple page components and next to them you can split up the UI in as many components as you feel like.
The generated plugin includes two example components to showcase how we
structure our plugins. There are usually one or multiple page components and
next to them you can split up the UI in as many components as you feel like.
We have the `ExamplePage` to show an example Backstage page component. The `ExampleFetchComponent` show cases the common task of making an async request to a public API and plot the response data in a table using Material-UI components.
We have the `ExamplePage` to show an example Backstage page component. The
`ExampleFetchComponent` show cases the common task of making an async request to
a public API and plot the response data in a table using Material-UI components.
You may tweak these components, rename them and/or replace them completely.
@@ -76,6 +92,7 @@ There are two things needed for a Backstage app to start making use of a plugin.
1. Add plugin as dependency in `app/package.json`
2. `import` plugin in `app/src/plugins.ts`
Luckily these two steps happen automatically when you create a plugin with the Backstage CLI.
Luckily these two steps happen automatically when you create a plugin with the
Backstage CLI.
[Back to Getting Started](README.md)
+87 -51
View File
@@ -2,26 +2,30 @@
## Introduction
Backstage Plugins strive to be self-contained, with as much functionality
as possible residing within the plugin itself and its backend APIs. There will however
always be a need for plugins to communicate outside of its boundaries, both with other
plugins and the app itself.
Backstage Plugins strive to be self-contained, with as much functionality as
possible residing within the plugin itself and its backend APIs. There will
however always be a need for plugins to communicate outside of its boundaries,
both with other plugins and the app itself.
Backstage provides two primary methods for plugins to communication across
their boundaries in client-side code. The first one being the `createPlugin` API and the registration hooks
passed to the `register` method, and the second one being Utility APIs.
While the `createPlugin` API is focused on the initialization plugins and the app,
the Utility APIs provide ways for plugins to communicate during their entire life cycle.
Backstage provides two primary methods for plugins to communication across their
boundaries in client-side code. The first one being the `createPlugin` API and
the registration hooks passed to the `register` method, and the second one being
Utility APIs. While the `createPlugin` API is focused on the initialization
plugins and the app, the Utility APIs provide ways for plugins to communicate
during their entire life cycle.
## Usage
Each Utility API is tied to an `ApiRef` instance, which is a global singleton object
without any additional state or functionality, its only purpose is to reference Utility APIs.
`ApiRef`s are create using `createApiRef`, which is exported by `@backstage/core`.
There are many predefined Utility APIs defined in `@backstage/core`, and they're all exported with a name of the pattern `*ApiRef`, for example `errorApiRef`.
Each Utility API is tied to an `ApiRef` instance, which is a global singleton
object without any additional state or functionality, its only purpose is to
reference Utility APIs. `ApiRef`s are create using `createApiRef`, which is
exported by `@backstage/core`. There are many predefined Utility APIs defined in
`@backstage/core`, and they're all exported with a name of the pattern
`*ApiRef`, for example `errorApiRef`.
To access one of the Utility APIs inside a React component, use the `useApi` hook exported by
`@backstage/core`, or the `withApis` HOC if you prefer class components. For example, the `ErrorApi` can be accessed like this:
To access one of the Utility APIs inside a React component, use the `useApi`
hook exported by `@backstage/core`, or the `withApis` HOC if you prefer class
components. For example, the `ErrorApi` can be accessed like this:
```tsx
import React, { FC } from 'react';
@@ -39,13 +43,20 @@ export const MyComponent: FC<{}> = () => {
};
```
Note that there is no explicit type given for `ErrorApi`. This is because the `errorApiRef` has the type embedded, and `useApi` is able to infer the type.
Note that there is no explicit type given for `ErrorApi`. This is because the
`errorApiRef` has the type embedded, and `useApi` is able to infer the type.
Also note that consuming Utility APIs is not limited to plugins, it can be done from any component inside Backstage, including the ones in `@backstage/core`. The only requirement is that they are beneath the `AppProvider` in the react tree.
Also note that consuming Utility APIs is not limited to plugins, it can be done
from any component inside Backstage, including the ones in `@backstage/core`.
The only requirement is that they are beneath the `AppProvider` in the react
tree.
## Registering Utility API Implementations
The Backstage App is responsible for providing implementations for all Utility APIs required by plugins. The example app in this repo registers its APIs inside [src/apis.ts](/packages/app/src/apis.ts). Here's an example of how to wire up the `ErrorApi` inside an app:
The Backstage App is responsible for providing implementations for all Utility
APIs required by plugins. The example app in this repo registers its APIs inside
[src/apis.ts](/packages/app/src/apis.ts). Here's an example of how to wire up
the `ErrorApi` inside an app:
```ts
import {
@@ -72,16 +83,20 @@ const app = createApp({
});
```
The `ApiRegistry` is used to register all Utility APIs in the app and associate them with `ApiRef`s. It implements the `ApiHolder` interface, which enables it to provide an API implementation given an `ApiRef`.
The `ApiRegistry` is used to register all Utility APIs in the app and associate
them with `ApiRef`s. It implements the `ApiHolder` interface, which enables it
to provide an API implementation given an `ApiRef`.
Note that our `ErrorApi` implementation depends on another Utility API, the `AlertApi`. This is the method with which APIs can depend on other APIs, using manual dependency injection at the initialization of the app. In general, if you want
to depend on another Utility API in an implementation of an API, you import the type for that API and make it a
constructor parameter.
Note that our `ErrorApi` implementation depends on another Utility API, the
`AlertApi`. This is the method with which APIs can depend on other APIs, using
manual dependency injection at the initialization of the app. In general, if you
want to depend on another Utility API in an implementation of an API, you import
the type for that API and make it a constructor parameter.
## Custom implementations of Utility APIs
Defining a custom implementation of a utility API is easy, you simply need to export a class
that `implements` the target API, for example:
Defining a custom implementation of a utility API is easy, you simply need to
export a class that `implements` the target API, for example:
```ts
export class IgnoringErrorApi implements ErrorApi {
@@ -91,51 +106,72 @@ export class IgnoringErrorApi implements ErrorApi {
}
```
The `IgnoringErrorApi` would then be imported in the app, and wired up like this:
The `IgnoringErrorApi` would then be imported in the app, and wired up like
this:
```ts
builder.add(errorApiRef, new IgnoringErrorApi());
```
Note that the above line will cause an error if `IgnoreErrorApi` does not fully
implement the `ErrorApi`, as it is checked by the type embedded in the `errorApiRef` at compile time.
implement the `ErrorApi`, as it is checked by the type embedded in the
`errorApiRef` at compile time.
## Defining custom Utility APIs
The pattern for plugins defining their own Utility APIs is not fully established yet. The current way is for the plugin to export its own `ApiRef` and type for the API, along with one or more implementations. It is then up to the app to import, and register those APIs. See for example the [lighthouse](/plugins/lighthouse/src/api.ts) or [graphiql](/plugins/graphiql/src/lib/api/types.ts) plugins for examples of this.
The pattern for plugins defining their own Utility APIs is not fully established
yet. The current way is for the plugin to export its own `ApiRef` and type for
the API, along with one or more implementations. It is then up to the app to
import, and register those APIs. See for example the
[lighthouse](/plugins/lighthouse/src/api.ts) or
[graphiql](/plugins/graphiql/src/lib/api/types.ts) plugins for examples of this.
The goal is to make this process a bit smoother, but that requires work in other parts of Backstage, like configuration management. So it remains as a TODO. If you have more questions regarding this, or have an idea for an API that you want to share outside your plugin, hit us up in [GitHub issues](https://github.com/spotify/backstage/issues/new/choose) or the [Backstage Discord server](https://discord.gg/EBHEGzX).
The goal is to make this process a bit smoother, but that requires work in other
parts of Backstage, like configuration management. So it remains as a TODO. If
you have more questions regarding this, or have an idea for an API that you want
to share outside your plugin, hit us up in
[GitHub issues](https://github.com/spotify/backstage/issues/new/choose) or the
[Backstage Discord server](https://discord.gg/EBHEGzX).
## Architecture
The `ApiRef` instances mentioned above provide a point of indirection between consumers and producers of
Utility APIs. It allows for plugins and components to depend on APIs in a type-safe way, without
having a direct reference to a concrete implementation of the APIs. The Apps are also given a lot
of flexibility in what implementations to provide. As long as they adhere to the contract established
by an `ApiRef`, they are free to choose any implementation they want.
The `ApiRef` instances mentioned above provide a point of indirection between
consumers and producers of Utility APIs. It allows for plugins and components to
depend on APIs in a type-safe way, without having a direct reference to a
concrete implementation of the APIs. The Apps are also given a lot of
flexibility in what implementations to provide. As long as they adhere to the
contract established by an `ApiRef`, they are free to choose any implementation
they want.
The figure below shows the relationship between <span style="color: #82b366">different Apps</span>, that
provide <span style="color: #6c8ebf">different implementations</span> of the
<span style="color: #9673a6">FooApi</span>. <span style="color: #d6b656">Components</span> within Plugins
then access the <span style="color: #9673a6">FooApi</span> via the <span style="color: #b85450">fooApiRef</span>.
The figure below shows the relationship between
<span style="color: #82b366">different Apps</span>, that provide
<span style="color: #6c8ebf">different implementations</span> of the
<span style="color: #9673a6">FooApi</span>.
<span style="color: #d6b656">Components</span> within Plugins then access the
<span style="color: #9673a6">FooApi</span> via the
<span style="color: #b85450">fooApiRef</span>.
<div style="text-align:center">
<img src="utility-apis-fig1.svg" alt="Figure showing the relationship between utility APIs, the apps that provide them, and the plugins that consume them">
</div>
The current method for connecting Utility API providers and consumers is via the React tree using
an `ApiProvider`, which is added to the `AppProvider` of the `App`. In the future there may potentially
be more ways to do this, in ways that are not tied to react. A design goal of the Utility APIs was to
not have them directly tied to React.
The current method for connecting Utility API providers and consumers is via the
React tree using an `ApiProvider`, which is added to the `AppProvider` of the
`App`. In the future there may potentially be more ways to do this, in ways that
are not tied to react. A design goal of the Utility APIs was to not have them
directly tied to React.
The indirection provided by Utility APIs also makes it straightforward to test components that depend
on APIs, and to provide a standard common development environment for plugins. A proper test wrapper
with mocked API implementations is not yet ready, but it will provided as a part of
`@backstage/test-utils`. It will provide mocked variants of APIs, with additional methods for asserting
a component's interaction with the API.
The indirection provided by Utility APIs also makes it straightforward to test
components that depend on APIs, and to provide a standard common development
environment for plugins. A proper test wrapper with mocked API implementations
is not yet ready, but it will provided as a part of `@backstage/test-utils`. It
will provide mocked variants of APIs, with additional methods for asserting a
component's interaction with the API.
The common development environment for plugins is included in `@backstage/dev-utils`, where the exported
`createDevApp` function creates an application with implementations for all core APIs already present.
Contrary to the method for wiring up Utility API implementations in an app created with `createApp`, `createDevApp`
uses automatic dependency injection. This is to make it possible to replace any API implementation, and
having that be reflected in dependents of that API.
The common development environment for plugins is included in
`@backstage/dev-utils`, where the exported `createDevApp` function creates an
application with implementations for all core APIs already present. Contrary to
the method for wiring up Utility API implementations in an app created with
`createApp`, `createDevApp` uses automatic dependency injection. This is to make
it possible to replace any API implementation, and having that be reflected in
dependents of that API.
+9 -8
View File
@@ -9,8 +9,8 @@ packages, and any new versions will automatically be published to NPM.
### Creating a new release
Version bumps are made through release PRs. To create a new release, checkout out
a new branch that you will use for the release, e.g.
Version bumps are made through release PRs. To create a new release, checkout
out a new branch that you will use for the release, e.g.
```sh
$ git checkout -b new-release
@@ -22,16 +22,17 @@ Then, from the root of the repo, run
$ yarn release
```
This will bring up the lerna release CLI where you choose what type of version bump
you want to make, (major/minor/patch/prerelease). The CLI will take you through choosing
a version, previewing all changes, and then approving the release. Once the release
is approved, a new commit is created that you can submit as a PR. Push the branch to GitHub:
This will bring up the lerna release CLI where you choose what type of version
bump you want to make, (major/minor/patch/prerelease). The CLI will take you
through choosing a version, previewing all changes, and then approving the
release. Once the release is approved, a new commit is created that you can
submit as a PR. Push the branch to GitHub:
```sh
$ git push origin -u new-release
```
And then create a PR. Once the PR is approved and merged into master, the master build
will publish new versions of all bumped packages.
And then create a PR. Once the PR is approved and merged into master, the master
build will publish new versions of all bumped packages.
[Back to Docs](README.md)
+5 -2
View File
@@ -1,6 +1,8 @@
# createPlugin - feature flags
The `featureFlags` object passed to the `register` function makes it possible for plugins to register Feature Flags in Backstage for users to opt into. You can use this to split out logic in your code for manual A/B testing, etc.
The `featureFlags` object passed to the `register` function makes it possible
for plugins to register Feature Flags in Backstage for users to opt into. You
can use this to split out logic in your code for manual A/B testing, etc.
```typescript
export type FeatureFlagsHooks = {
@@ -24,7 +26,8 @@ export default createPlugin({
## Using with useApi
To use it, you'll first need to register the `FeatureFlags` API via `ApiRegistry` in your `apis.ts` in your App:
To use it, you'll first need to register the `FeatureFlags` API via
`ApiRegistry` in your `apis.ts` in your App:
```tsx
import {
+4 -2
View File
@@ -1,7 +1,9 @@
# createPlugin - router
The router that is passed to the `register` function makes it possible for plugins to hook into routing of the Backstage app and provide the end users with new views to navigate to.
This is done by utilising the following methods on the `router`:
The router that is passed to the `register` function makes it possible for
plugins to hook into routing of the Backstage app and provide the end users with
new views to navigate to. This is done by utilising the following methods on the
`router`:
```typescript
addRoute(