# Composite Schemas Spec

The GraphQL Composite Schemas Spec is being actively worked upon on [GitHub](https://github.com/graphql/composite-schemas-spec). Most importantly for readers are:

- [Source Schema directives](https://github.com/graphql/composite-schemas-spec/blob/main/spec/Section%202%20--%20Source%20Schema.md) defining the directives that may be used by subgraphs such as `@lookup`.
- [FieldSelectionMap specification](https://github.com/graphql/composite-schemas-spec/blob/main/spec/Appendix%20A%20--%20Field%20Selection.md) used in `@is` and `@require`.

The Grafbase Gateway partially implements the specification with the goal of implementing it fully over time. In addition, we also extend the specification to better fit the use case of non-GraphQL data sources.

The current document describe both the current implemented parts of the specification and any additions we made.

# Directives

Directives can be imported with `@link` similarly to Apollo Federation's directives:

```graphql
extend schema
  @link(
    url: "https://specs.grafbase.com/composite-schemas/v1"
    import: ["@lookup", "@key"]
  )
```

In all examples within this document, directives will be imported with this `@link` directive if not specified explicitly.

It is not possible to use both Apollo Federation and Composite Schemas together in the same subgraphs. But a super graph can be composed of subgraphs used either.

## Not implemented

- `@internal`
- `@require` (coming soon)

## Common directives with Apollo Federation

`@external`, `@inaccessible`, `@shareable`, `@provides` and `@override` are implemented in the same way for both Apollo Federation and Composite Schemas.

`@key` in Composite Schemas is treated as a non-resolvable Apollo Federation `@key`. So `@key(fields: "id")` is implicitly `@key(fields: "id", resolvable: false)`.
Resolvable in that context means that the subgraph can provide the entity through Apollo Federation's custom `_entities` field. So `@key` in Composite Schemas only
defines the key fields, without specifying how the entities can be retrieved. A subgraph only defining the following does not provide any means for the supergraph to
retrieve a `Post` entity. `_entities` and `_service` are not assumed to exist. See `@lookup` directive to provide entities.

```graphql
extend schema
  @link(
    url: "https://specs.grafbase.com/composite-schemas/v1"
    import: ["@key"]
  )

type Post @key(fields: "id") {
  id: ID!
}
```

## @lookup

```graphql
directive @lookup on FIELD_DEFINITION
```

`@lookup` defines how entities can be accessed by the supergraph. In the following example `@lookup` would automatically be used to a `Post` entity.

```graphql
type Query {
  post(id: ID!): Post! @lookup
}

type Post @key(fields: "id") {
  id: ID!
}
```

Today the Grafbase Gateway only supports the batch variant, which doesn't exist in the Composite Schemas spec, for extensions like [Postgres](/extensions/postgres):

```graphql
type Query {
  posts(ids: [ID!]): [Post!] @lookup
}

type Post @key(fields: "id") {
  id: ID!
}
```

`@lookup` will automatically detect the right argument to inject for single and composite keys as long as there isn't any ambiguity. Only one `@lookup` can exists in a subgraph for any given `@key`.

Support for single lookup for any resolver extensions/GraphQL subgraphs and explicit mapping with `@is` is coming soon.

## @derive

```graphql
directive @derive on FIELD_DEFINITION
```

The `@derive` directive creates a virtual entity field when the original data only exposes ids.
It doesn't exist in the Composite Schemas Spec, we added it to simplify the integration of non-Graphql data sources such as REST or gRPC.

```graphql
type Query {
  posts: [Post!]!
}

type Post {
  authorId: ID!
  # This field is not provided by the subgraph
  author: User! @derive
}

type User @key(fields: "id") {
  id: ID!
}
```

The `author` field is resolved by the supergraph, derived from the `authorId` field. This simple adjustment makes it possible to query fields from other subgraphs:

```graphql
{
  posts {
    author {
      id
      name
    }
  }
}
```

`@derive` will, similarly to `@lookup`, automatically detect the relevant fields based on their name and type given the defined `@key`.
A derived field _must_ match at least one `@key`.
It may provide more fields or multiple keys, and you can also hide the real subgraphs fields from your final API schema with `@inaccessible`:

```graphql
type Post {
  authorId: ID! @inaccessible
  # This field is not provided by the subgraph
  author: User! @derive
}
```

`@derive` supports multiple use cases and can be explicitly specified with the help of the `@is` directive:

- Single key:

```graphql
type Post {
  authorId: ID!

  author: User! @derive
  # or
  author: User! @derive @is(field: "{ id: authorId }")
}

type User @key(fields: "id") {
  id: ID!
}
```

- Composite key:

```graphql
type Post {
  authorTenantId: ID!
  authorEmail: String!

  author: User! @derive
  # or
  author: User!
    @derive
    @is(field: "{ tenantId: authorTenantId email: authorEmail }")
}

type User @key(fields: "tenantId email") {
  tenantId: ID!
  email: String!
}
```

- Single key list

```graphql
type Post {
  commentIds: [ID!]

  comments: [Comment!]! @derive
  # or
  comments: [Comment!]! @derive @is(field: "commentIds[{ id: . }]")
}

type Comment @key(fields: "id") {
  id: ID!
}
```

- Composite key list

```graphql
type Post {
  reviewersTenantIdAndEmail: [TenantIdAndEmail!]!
  reviewers: [User!]! @derive
  # or
  reviewers: [User!]!
    @derive
    @is(field: "reviewersTenantIdAndEmail[{ tenantId email }]")
}

type TenantIdAndEmail {
  tenantId: ID!
  email: String!
}

type User @key(fields: "tenantId email") {
  tenantId: ID!
  email: String!
}
```

The only limit to `@derive` today is that nested fields are not supported.

## @is

See `@lookup` and `@derive` for more information.