tot-ra / graphql-schema-registry

GraphQL schema registry
MIT License
372 stars 68 forks source link

Add supergraph endpoint #189

Open agologan opened 1 year ago

agologan commented 1 year ago

Problem

In order to use Apollo Router #162 one needs a composed supergraph to provide to it. While this can be done using Rover CLI or via @apollo/composition it's not very easy to do it and may require an extra infrastructure component.

Changes

New /schema/supegraph that can be passed directly to the router. Endpoint is cached in memory (without other deps), and is invalidated on subgraph changes.

Example Helm values for Apollo Router:

router:
  args:
    - --hot-reload
    - -s
    - /tmp/supergraph.graphql
extraContainers:
  - name: update-graph
    image: quay.io/curl/curl:8.4.0
    command: ["watch"]
    args: ["-n", "60", "curl", "-v", "http://graphql-schema-registry/schema/supergraph", "-o", "/tmp/supergraph.graphql"]
    volumeMounts:
    - name: schema
      mountPath: /tmp/
extraVolumes:
  - name: schema
    emptyDir: {}
extraVolumeMounts:
  - name: schema
    mountPath: /tmp/
    readOnly: true
tot-ra commented 11 months ago

hey @agologan . Thanks for the effort. How is supergraph different from GET /schema/latest ? Is it because of text/plain.. or different format? Also, some tests would be nice

agologan commented 11 months ago
router.get('/schema/latest', asyncWrap(schema.composeLatest));
export async function composeLatest(req, res) {
    const schema = await getAndValidateSchema(connection, false, false);

    return res.json({
        success: true,
        data: schema,
    });
}

At a quick glance it just returns the schema wrapper in JSON object, but looking further..

export async function getAndValidateSchema(
    trx,
    services = false,
    validate = true
) {
    const schemas = services
        ? await schemaModel.getSchemaByServiceVersions({ trx, services })
        : await schemaModel.getLastUpdatedForActiveServices({ trx });

    logger.info(
        'Validating schema. Got services schemas from DB transaction..',
        {
            schemas,
        }
    );

    if (validate && schemas && schemas.length) {
        federationHelper.composeAndValidateSchema(schemas);
    }

    return schemas;
}

So getAndValidateSchema composes the schema for validation but throws away the composition (supergraph) and in turns returns a list of schemas.

Put together GET /schema/latest returns a json object indicating composition success and the list of schemas that would make up the supergraph. One can also see this in the examples where the gateway needs to compose the graph i.e. https://github.com/tot-ra/graphql-schema-registry/blob/master/examples/gateway_service_hard_coded_urls/supergraph.js#L88

By comparison, the new endpoint composes, caches and returns the supergraph to be used directly in a gateway or router.

In case of Apollo Router it does not do it's own composition as seen here https://www.apollographql.com/docs/router/migrating-from-gateway#servicelist--introspectandcompose So if one wanted to use it they'd either have to add another infrastructure component to do the composition or have the registry do it since it already runs that code but it throws away the result.

tot-ra commented 10 months ago

please add tests and examples of how its used

agologan commented 8 months ago

Added a sanity test to schema.itest.ts to ensure composition works. Added new tests to router/index.itest.ts using supertest which call via express router testing the caching layer as well.

LE: Provided a working compose example and updated the examples docs. See: 34ab51a69daaea940cf5df421eaabb803319a766