graphql-nexus / nexus

Code-First, Type-Safe, GraphQL Schema Construction
https://nexusjs.org
MIT License
3.4k stars 275 forks source link

Apollo Federation support #148

Open jferrettiboke opened 5 years ago

jferrettiboke commented 5 years ago

Apollo Federation was open sourced a few days ago by Apollo team. I would like to know if there are plans to integrate Apollo Federation into Nexus in some ways. I just pretend to discuss deeper how this would look like.

I think the way that Nexus provides to extend types is similar to what Apollo Federation does. However, Apollo Federation allows us to create a gateway from different URLs (other GraphQL servers as services) and each URL is a service running independently. I would like to discuss this with some of you guys because doing things in a similar way is easier to maintain and it's better for teams at scale.

The good thing about this is that every team can use the programming language they want (I read somewhere that Apollo plans to expand to other programming languages), and using the gateway in Apollo Federation, all URLs can be merged at once easily and clients will see all graphs connected. Think this like a microservices pattern for GraphQL services.

Nexus is great, but if we try to implement everything within the same server, it will not provide freedom to teams since everything must be done in the same language because Nexus has no ways yet to connect easily to other GraphQL services. In an attempt to mitigate this, I created an issue in prisma/nexus regarding plugins, but it is still in discussion.

Also, running a solo-server using serverless is not efficient and costs are higher. We use Zeit Now and we like to split everything as much as we can so that we are pretty sure that once a function is called, it only does one thing (but very well). This scales so well. With Apollo Federation, you can have as many servers as services you have, plus the server which acts as a gateway.

I think having something like this is a big plus. We can discuss this as much as you want, guys. I would be more than happy to contribute to the future and success of Nexus.

tgriesser commented 5 years ago

Not yet, but I am interested in taking a look and seeing what the opportunity for integration might be here - been on vacation the last week or so, so I'm just getting caught up, haven't had a chance to take a look at the specifics of how Apollo Federation is spec'ed to work.

I created an issue in prisma/nexus regarding plugins, but it is still in discussion.

Interested to see what you think of the changes merged in #143 and if it'll address anything you're looking to do re: plugins

Nayni commented 5 years ago

After reading through the current Federation spec I think there is a great opportunity to build additional functionality on top of nexus to adhere to the Federation spec and allow building these types of schema's.

Most of the spec is actually fairly straight forward, I think implementing the __resolveReference resolver is rather trivial. The only thing that is a little more clunky is the extra work that is needed to provide the schema with additional meta data for the federated graphs. Creating additional types on the schema (as described in the spec) is fairly doable I only dislike one detail of the spec in it's current form, which is that the _service field is a String that has to return a fully printed schema SDL with the federated directives attached at the right locations. I think this is where it gets tricky in approaches that start from code-first. In theory this is possible but it requires additional mechanisms to re-construct the schema, attached all the directives in their corresponding locations and print this schema, coming a code-first approach this is harder to do because we don't have the fully decorated original schema like an SDL-first approach.

Because I am rather interested to hear what their idea and vision is behind this printed SDL approach. Since the printed SDL is mainly used for meta data I feel there is an opportunity to put this meta data in the schema in a more conventional way which would be easier to construct in code-first approaches. Out of personal interests I opened a ticket already at apollo: https://github.com/apollographql/apollo-server/issues/2769 to discuss this.

brafdlog commented 5 years ago

Any updates on this?

0xR commented 5 years ago

You can add federation to you nexus schema using graphql-transform-federation. It will not give you the build time type safety that nexus does, but it does perform strict checking on startup.

Let me know what you think.

jhalborg commented 5 years ago

Good work @0xR ! 👏 It look functional, but I think it'll be a bit awkward at scale. What do you think about the approach @tgriesser ?

0xR commented 5 years ago

I agree it's awkward at scale, I see it as a temporary solution until nexus has better federation support.

However, for situations where you don't have control over your schema it is the only solution. For example a managed graphql service or generated graphql schemas.

nayaabkhan commented 5 years ago

Just in case if someone wants to see it in action: https://github.com/nayaabkhan/nexus-federation-example. Hoping to evolve the example further with complex cases and finding patterns to use it at scale (e.g. having separate objects in each type and merging into a final configuration for transformSchemaFederation).

Also, something to be aware of https://github.com/0xR/graphql-transform-federation/issues/1 when using it with Nexus.

LexSwed commented 5 years ago

I wanted to implement simple schema stitching and old way is now deprecated in favour of Federation. So with nexus the only way to use is through the https://github.com/0xR/graphql-transform-federation The example for federated schema with the gateway is ok, but I don't need a gateway, I just need to federate my schema within one server instance. So buildFederatedSchema from @apollo/federation won't work, e.g.:

buildFederatedSchema([Account, SomethingElse].map(schema => parse(printSchema(schema))))

if Account is:

export default transformSchemaFederation(schema, {
  Query: {
    extend: true
  },
  Account: {
    keyFields: ['id'],
    resolveReference(reference: any) {
      return {
        id: reference.id
      };
    }
  }
});

Because it fails to federate Query. It just weird to see that such mature Apollo moves slowely in support of other tools for building something on top of it, it's been a while since Federation was open sourced....

jhalborg commented 5 years ago

@LexSwed

If you're just using it inside one server instance, I'd stay away from Federation as it is a tool to federate across multiple servers - it would complicate your development needlessly.

@tgriesser posted a great comment touching on this in another issue .

On another note, Apollo actually moves fairly fast if you consider how many depend on their tooling to be stable, but they do not seem keen to support a code-first approach to schemas at the moment, that's for sure. But give them a break - they actually just open sourced Federation this summer, and they probably have their plate full supporting the current first iteration before moving towards a v 2.0 😉

gabsn commented 4 years ago

Any update on this? The lack of federation support makes me very reluctant to choose nexus for my next project. It's sad that this federation thing is not part of GraphQL spec itself...

On one end we want a unified GraphQL API layer but on the other end it is not really adapted to large-scale applications and service-oriented architectures.

Nayni commented 4 years ago

I am still waiting on Apollo to give an update on my initial question https://github.com/apollographql/apollo-server/issues/2769 which has been open for quite a while now.

From what I can tell I don't see Apollo having any plans in their road-map to make Federation more code-first friendly. In fact it feels like they are promoting the SDL first approach more than ever and its being picked up by other languages which have been building implementation on supporting SDL first as a second option to create GraphQL services.

On the other hand I've personally come to the conclusion that Federation is only worthwhile if you are planning on designing your schema in a way that requires you to have multiple autonomous teams work on the same schema, which I see as an exception rather than the rule (even though I get that people hype this up greatly just as the more general term of microservices).

In my experience GraphQL is still delivering on the unified API layer promise and creating a great abstraction of this layer. The typical way I've built multiple API's now is that we start of as a monolith creating the schema and the implementation in a single GraphQL service. In time (as the product matures) this service starts to break down into multiple smaller services where each service is focusing more on its core domain but the exercise to get to what that domain exactly is can only be done by starting from the monolith and breaking it apart as we learn more about the product we build.

I think Federation is in this case a way to break down these services but there are other patterns too. I still think it's wrong from Apollo's perspective that since the launch of Federation the approach for Code First has been left in the dust and totally neglected but that doesn't mean there aren't other options. I've successfully broken down monoliths into smaller services using REST or gRPC as back-end services to supply the data for the unified schema. I realize that this means going back to REST (which we wanted to avoid) or in the case of gRPC introduce a new technology into the stack can be daunting but that doesn't mean you cannot solve the problem without Federation.

All that said I'm hoping that now that the field extensions feature has been merged in for a while (https://github.com/graphql/graphql-js/issues/1527) Apollo might re-consider their SDL directive parsing and put these information blobs into more Spec compliant places of the schema instead of an ugly SDL print with directives attached.

yaacovCR commented 3 years ago

GraphQL-Tools's schema stitching now supports type merging similar to Federation, as well as specification of merge instructions for the gateway via directives (see our docs and https://github.com/gmac/schema-stitching-handbook for examples).

We want to support specification of merge instructions via extensions for code first schemas such as Nexus. Just checking first if Nexus lets you specify extensions for types and fields, and want to inquire as to whether there is anything I should know about how you handle extensions within the framework. I imagine that we will follow supposed best practices and nest our type field extensions under a GRAPHQL_TOOLS_SCHEMA_STITCHING key... We will probably have options on the gateway to modify the location of the extensions as desired. Just inquiring whether there is any Nexus specific info about extensions that I should be aware of...

yaacovCR commented 3 years ago

Something in nexus akin to https://typegraphql.com/docs/extensions.html ??

See https://github.com/MichalLytek/type-graphql/issues/351#issuecomment-747999216

Thanks again!

yaacovCR commented 3 years ago

https://github.com/graphql-nexus/nexus/blob/main/src/definitions/objectType.ts#L160

Looks good from here!

yaacovCR commented 3 years ago

Decided to go a different way and follow Gatsby/graphql-compose convention of reading directives from extensions.directives -- see https://github.com/graphql/graphql-js/issues/1343#issuecomment-479871020 -- but I also allowed this to be customized as desired.

This is now released as canary, see https://github.com/ardatan/graphql-tools/pull/2391

To get this working:

  1. Add the prebuilt directives to the schema from @graphql-tools/stitching-directives
  2. Use the directives within the schema via extensions.directives
  3. Expose the SDL to the gateway via some resolver (_root, _args, _context, info) => printSchemaWithDirectives(info.schema) where printSchemaWithDirectives is an improved @graphql-tools/utils function that follows same convention as above.

For a peek at fully worked out examples of stitching with SDL-first approaches, take a look at https://github.com/gmac/schema-stitching-handbook, where @gmac takes you through step-by-step from the basics all the way to hot reloading/versioning releases.

Hopefully, soon an example will appear with code-first approach.

Thanks!

yaacovCR commented 3 years ago

Nexus support is live with stitching directives (in extensions):

https://github.com/yaacovCR/schema-stitching-demos/blob/code-first/code-first-schemas/services/inventory/schema.js

note that although nexus supports tagging schema entities (types, fields) with directive nodes (directive use on schema entities) via extensions, it does not seem to support adding actual directive entities (the actual definitions of custom directives).

I think this is incorrect behavior, a server might want to implement a custom directive on a query.

You can do this quite easily in graphql-js

new GraphQLSchema({
  ...
  directives: [...specifiedDirectives, ...your_array_of_custom_directives],
})

I worked around this by using extendSchema to add in more typeDefs, but see the graphql-js example where I just loaded in the actual directives. (https://github.com/yaacovCR/schema-stitching-demos/blob/code-first/code-first-schemas/services/accounts/schema.js)

coopbri commented 3 years ago

@yaacovCR Sounds awesome, would love to take a look. Was your schema-stitching-demos repo made private or removed?

yaacovCR commented 3 years ago

See main repo https://github.com/gmac/schema-stitching-handbook

coopbri commented 3 years ago

Awesome, thanks a lot! Super useful.

nayaabkhan commented 3 years ago

I also made an example repo of using Schema Stitching with Nexus here: https://github.com/nayaabkhan/nexus-stitching-example.

victoriris commented 3 years ago

Hi, any updates on how to use Nexus with Apollo Federation? I am struggling to create the subGraph with the directives

Sytten commented 3 years ago

Its going to be easy once the PR is merged

scottmccaskill commented 3 years ago

Any luck per victoridp's request?

mengqing commented 2 years ago

Any news to this?

chav-aniket commented 2 years ago

Another bump to any updates on federation support, heavily desired!

andreventuravale commented 1 year ago

Is it possible to create a similar GraphQL Federation spec? Apollo has designed a great architecture, but it seems to be blocking further progress. The community should initiate a boycott of Apollo Federation to explore alternative solutions.

trim21 commented 2 months ago

a not perfect solution for federation v1:

add _service query manally, this make it work as part of federation, but can't cross resolve

const _Service = objectType({
  name: '_Service',
  definition(t) {
    t.nonNull.string('sdl');
  },
});

const federationSupport = extendType({
  type: 'Query',
  definition(t) {
    t.nonNull.field('_service', {
      type: '_Service',
      async resolve(_parent, {}, { auth: { allowNsfw } }: Context) {
        return {
          sdl: await fsp.readFile(path.join(projectRoot, 'lib', 'graphql', 'schema.gen.graphql'), 'utf-8'),
        };
      },
    });
  },
});

export const schema = makeSchema({
  types: [types, _Service, federationSupport],
  outputs: {
    schema: path.join(projectRoot, 'lib', 'graphql', 'schema.gen.graphql'),
  },
});