# Composition for Grafbase extensions

In the Grafbase CLI and the schema registry in the Grafbase Platform, composition is handled by our open source [graphql-composition crate](https://github.com/grafbase/grafbase/tree/main/crates/graphql-composition). It has baked in support for Grafbase extensions.

When composition detects that an imported type or directive comes from an extension, it will add extra information about it in the composed execution schema (the one used by the Gateway, not the public API), so that the Gateway has all the information it needs on which directive, in which subgraph, is associated with which extension.

This information is necessary for resolvers, but optional for authorization and contracts extensions. The later can only rely on the `@link` url as presented in the second section.

## Extension directives

For a directive to be composed as an extension directive, it must be imported from an `@link`ed schema, and that schema's URL must either:

- Use the `file:` scheme.
- Have a `url` that starts with `https://grafbase.com/extensions`

In the following example, all `@link` directives would be interpreted as linking to extensions, and all directives from these extensions would be composed as extension directives:

```graphql
extend schema
    @link(url: "file:///path/to/extension", import: ["@test"])
    @link(url: "https://grafbase.com/extensions/kafka/0.2.1")
    @link(url: "file:///path/to/another/extension/build", as: "alias")
    @link(
      url: "https://grafbase.com/extensions/rest/0.5.0"
      import: ["@restEndpoint", "@rest"]
    )
```

Concretely, composition will produce an `extension__Link` enum referencing all the extensions, and an `@extension__directive` directive wrapping each application of each directive from an extension with information about what subgraph it originates from. It can look like this:

```graphql
type Doctor
  @join__type(graph: APPOINTMENTS, key: "id")
  @join__type(graph: DOCTORS, key: "id")
{
  appointments: [Appointment!]! @join__field(graph: APPOINTMENTS)
  firstName: String! @join__field(graph: DOCTORS)
  id: ID!
  lastName: String! @join__field(graph: DOCTORS)
  phone: String @extension__directive(graph: DOCTORS, extension: REST, name: "call", arguments: {method: GET, url: "https://my-phone_registry/doctors/{id}"}) @join__field(graph: DOCTORS)
  specialty: String! @join__field(graph: DOCTORS)
}

enum extension__Link
{
  KAFKA @extension__link(url: "https://grafbase.com/extensions/kafka/v1.0.0")
  ORACLE_CONNECTOR @extension__link(url: "file:///home/lellison/src/oracle-grafbase-extension/dist")
  REST @extension__link(url: "https://grafbase.com/extensions/rest")
}
```

The gateway primarily relies on the name and version detected through the [`@link`](https://specs.apollo.dev/link/v1.0/) specification with a few differences:

- the `v` prefix for the version number is optional
- file path may have a `build` directory suffix.
- in case of file paths, the gateway will try to load a `manifest.json`, the one generated by `grafbase extension build`. If it finds one it will the name and version specified by it and otherwise fallback on the `@link` name & version.


So for `https://grafbase.com/extensions/kafka/0.2.1` it's interpreted as the `kafka` extension version `0.2.1`. And for both `file:///path/to/extension` and `file:///path/to/another/extension/build`, they're mapped to the extension named `extension` if no `manifest.json` was found.

## Associated link URLs

The former section is most relevant if you download extensions from our marketplace or work locally on an extension. But extensions allow for more flexibility by allowing any `@link` URL to be associated with them. By specifying the following:

```toml
# or `authorization.directives` for authorization extensions.
[contracts.directives]
link_urls = ["https://example.com/tag"]
```

And using `@composeDirective` in your subgraph schema to propagate the directives as such:

```graphql
extend schema 
    @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@composeDirective"])
    @link(url: "https://example.com/tag", import: ["@tag"])
    @composeDirective(name: "@tag")
```

This allows you to associate your custom namespace to an extension directive. The `@tag` directive here will be treated as an extension directive by the gateway. You can associate multiple `@link` URLs to a single extensions, but they must be exact matches. The gateway will still validate the directive names and arguments according to the `definitions.graphql` file, they must be present and defined there.