Type-graphql: Support for Apollo Federation

Created on 31 May 2019  ·  56Comments  ·  Source: MichalLytek/type-graphql

Is your feature request related to a problem? Please describe.
I guess it would be about to add some decorators for @key, @extend etc, to be able to declare federation schema services.

Describe the solution you'd like
https://blog.apollographql.com/apollo-federation-f260cf525d21

Community Discussion Enhancement

Most helpful comment

Hi @MichalLytek, do you have any update regarding @typegraphql/federation, can I help in some way?

All 56 comments

I guess it would be about to add some decorators for @key, @extend etc

So it's blocked by #77 😞
But when #77 is ready, you would be able to use Apollo Federation with TypeGraphQL without any problem, so no further integration needed 🔒

@19majkel94 I see, I've read the issue and saw that you agreed (february) with guys from graphql-js, but not sure what does that mean for current situation? Is it going to be blocked, or something is happening?

I am waiting for changes in GraphL spec and graphql-js that add interchangeability between extensions and directives, so both approaches (SDL and code-first) could be used and handled by universal directives and other tools like prisma or apollo federation.

And @Hossein-s is working on a workaround to modify astNodes that are currently used by all JS tools to consume directives metadata from SDL.

I guess it would be about to add some decorators for @key, @extend etc,

Looks like it's not only about directives but also the extend keyword, which is handled in #228 🔑

@19majkel94 is it the extend keyword in #228 though? Doesn't #228 do the whole, type User extends BaseType.. The extend keyword in federation goes before the type, it's a way they can make the root graphql service have field calls different services.

@j I know, in #228 I was thinking about both merging typedefs from different file and extend type Foo syntax if it's possible with graphql-js.

Edit: extendSchema exist but I would have to also use graphql-tools to merge the schema with resolvers or find a way to separate the extends to SDL and keeps the field resolvers for unknown fields? Needs some exploration 😕

@19majkel94 bummer, yeah looks like most "code first" node.js implementations are going to have a rough time from what I was reading. I saw a post of someone requesting a different way of making schemas to make it easier, but I'm not sure what it'd take. Apollo Federation is the next step for our company's infrastructure and will have to move away from type-graphql if support doesn't come to, and that'll be a sad day. :(

Maybe I could just drop building schema using graphql-js and create the SDL string on my own (like in the reflection plugin PoC), and then just plug in the resolvers using graphql-tools to create an executable schema if it's needed (or not in the case of the type-graphql core).

@19majkel94 that sounds pretty flexible? Also, again, I haven't dug into how to do it within your library. Are you looking at the SDL apollo-federation came up with? Or the actual graphql schema?

Examples:
https://pw678w138q.sse.codesandbox.io/graphql (sibling service)
https://v368r9ml47.sse.codesandbox.io/ (gateway)

You wouldn't need to do anything that the gateway does, just be able to output what the sibling does per spec.

But just outputting string schema's seems like the most flexible?

Another issue to help solve code first libraries: https://github.com/apollographql/apollo-server/issues/2769

Another note I saw regarding "extends". https://www.apollographql.com/docs/apollo-server/federation/federation-spec/

The spec actually supports this:

type User @key(fields: "id") @extends {
  id: ID! @external
  reviews: [Review]
}

But still looks like a lot of work, :(

I've added an example of apollo-federation (a modified federation demo from @j PR):
https://github.com/MichalLytek/type-graphql/tree/20c81f4f7ee82779595002d25784fce3a8ff8b9b/examples/apollo-federation

I will leave this PR open to collect some feedback about the integration.
Also in the future I plan to make @typegraphql/federation package with @FederationKey decorator and others like defining resolverReference, etc. 😉

Hi guys! Thanks for this amazing library!
Do you have any ETA when can we expect this feature in production?

@lgabeskiria Have you tried the latest beta release? Without testing and confirmation I won't release "stable" package.

@MichalLytek will try to provide a feedback this week, thanks!

Got it working in our (currently) very simple project today, so far so good... Will continue working with it over the next couple of days and will report back as well.

@MichalLytek tested the feature, looks good to me!

Hi @MichalLytek is it possible to release this feature as beta release?

@lgabeskiria
Have you checked the npm site and typed npm i type-graphql@beta?

Tested the beta release with the GraphQL module in nestjs and works like a charm.

Hi All, I've installed the beta, used the same approach described in the example with buildFederatedSchema and copying in my source code createResolversMap
and his dependencies because they are not exported from type-graphql.

It seems to work to me but the schema generated doesn't have the @key annotation.

player.ts

import { ObjectType, Field, ID, Directive } from "type-graphql";

@Directive(`@key(fields: "id")`)
@ObjectType()
export default class Player {
  @Field(_type => ID)
  id: string;

  @Field()
  username: string;
}

resolver.ts

import { Query, Resolver } from "type-graphql";
import Player from "./player";

@Resolver(_of => Player)
export default class PlayerResolver {
  player = {
    id: "123",
    username: "victorious",
  };

  @Query(_returns => Player)
  async me(): Promise<Player> {
    return this.player;
  }
}

schema.graphql

# -----------------------------------------------
# !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!!
# !!!   DO NOT MODIFY THIS FILE BY YOURSELF   !!!
# -----------------------------------------------

directive @extends on INTERFACE | OBJECT

directive @external on FIELD_DEFINITION | OBJECT

directive @key(fields: String!) on INTERFACE | OBJECT

directive @provides(fields: String!) on FIELD_DEFINITION

directive @requires(fields: String!) on FIELD_DEFINITION

type Player {
  id: ID!
  username: String!
}

type Query {
  me: Player!
}

Am I doing something wrong?

@adamovittorio

It seems to work to me but the schema generated doesn't have the @key annotation.

image

https://typegraphql.ml/docs/next/directives.html

@MichalLytek - Thank you very much, I miss that part 😅.

Regarding buildFederatedSchema, will it be exposed from type-graphql/@typegraphql/federation?

In the future yes, in @typegraphql/federation I think 😉

Hi @MichalLytek, do you have any update regarding @typegraphql/federation, can I help in some way?

Is #521 expected to change how this is implemented?

ie. Will we be using the @Extensions annotation in some way instead?

@Sector95 In the future yes 😉
Apollo team has to expose a way to build federated schema from existing GraphQLSchema object and use extensions instead of AST directives:
https://github.com/apollographql/apollo-server/pull/3013

Tested the beta release with the GraphQL module in nestjs and works like a charm.

Can you provide a demo repository for us?

Can you provide a demo repository for us?

There's a small demo in my fork of the NestJS GraphQL package (PR still open)

https://github.com/tuxmachine/graphql/tree/feat/enable-code-first/tests/type-graphql-federation

Is there an open PR for this feature or has it already been merged somewhere?

Is there an open PR for this feature or has it already been merged somewhere?

The PR currently supports federation using regular schemas, type-graphql support is included but disabled. It can be enabled once the federation support in this repository is no longer beta.

https://github.com/nestjs/graphql/pull/455

@tuxmachine It appears that Nest.js has merged and published the linked PR (which you're the author of!). Is there an update on enabling type-graphql support?

Hi... Same question here... Any update about enabling type-graphql support?

No updates. It can be enabled once the federation support in this repository is no longer beta.

Currently it would require type-graphql as a beta dependency. Tell me if I'm missing the point, but it seems strange for a _stable_ release of @nestjs/graphql to have a _beta_ dependency. If you can make a good argument for it, the fix is as simple as removing this line

EDIT: + updating the package.json of course

@tuxmachine well, if we consider beta anything with version minor to 1.0.0, then TypeORM is also a beta... and as we know, is one of the best ORM for typescript, and the default ORM for nestjs 🤔

@gperdomor No, I consider it beta because it's listed under "unreleased" in the changelogs of type-graphql, and the code enabling Directives is packaged inside [email protected]

Edit: to clear up confusion, type-graphql itself is not beta. The directives feature is.

ohh I see... In that case I think we should wait...

I've been playing around with the typegrahpql federation example and I don't know how to get the auth checker to validate the @Authorized() fields on ObjectTypes resolved using the __resolveReference() function, as in the example. Is there a way to trigger the auth checks manually from the __resolveReference() function?

@MichalLytek I have been looking into using the federation example now with rc1, I think the createResolversMap util method needs to be exposed externally in order to use it outside this repo? https://github.com/MichalLytek/type-graphql/blob/master/examples/apollo-federation/helpers/buildFederatedSchema.ts#L10

@bsparks done in 7ddface 💪

Hello,



I ran into a problem when trying to build the federation schema using buildFederatedSchema example - It seems that when defining default values for an input enum type property and the enum resolver has different values than keys, it doesn't recognize the default enum value:

Example:

Schema:

enum TestEnum {
   A
}

input TestInput {
  prop: TestEnum = A
}

TestEnum resolver:

enum TestEnum {
  A = "some_different_value_than_A",
}

registerEnumType(TestEnum, {
  name: "TestEnum"
});

Error on example:

GraphQLError: Enum "TestEnum" cannot represent value: "A"

Possible solution:

Change https://github.com/MichalLytek/type-graphql/blob/e32a34fe065745bd209eadc5c36ba8bef6d893f4/src/utils/createResolversMap.ts#L41 to:

enumMap[name] = name;

@andrei-bitca-dc
It cannot be changed to enumMap[name] = name because enum values are runtime value and enum keys are enum names in schema.

This is the behavior of the official GraphQL implementation, so you should use internal values as default values:
https://github.com/graphql/graphql-js/issues/1604

@MichalLytek The problem is that it throws me this error: GraphQLError: Enum "TestEnum" cannot represent value: "A" on generating the federation schema, with the following InputType definition:

@InputType()
export class TestInput {
  @Field(_type => EnumType, { defaultValue: EnumType.A })
  public prop: EnumType = EnumType.A;
}

buildFederatedSchema.ts:

import { addResolversToSchema, GraphQLResolverMap } from "apollo-graphql";
import { GraphQLSchema, specifiedDirectives } from "graphql";
import gql from "graphql-tag";
import { BuildSchemaOptions, buildSchemaSync } from "type-graphql";
import { createResolversMap } from "type-graphql/dist/utils/createResolversMap";

import { buildFederatedSchema as buildApolloFederationSchema, printSchema } from "@apollo/federation";
import federationDirectives from "@apollo/federation/dist/directives";

export function buildFederatedSchema(
  options: Omit<BuildSchemaOptions, "skipCheck">,
  referenceResolvers?: GraphQLResolverMap<any>,
): GraphQLSchema {
  const schema = buildSchemaSync({
    ...options,
    directives: [...specifiedDirectives, ...federationDirectives, ...(options.directives || [])],
    skipCheck: true,
  });
  const federatedSchema = buildApolloFederationSchema({
    typeDefs: gql(printSchema(schema)),
    resolvers: <any>createResolversMap(schema),
  });
  if (referenceResolvers) {
    addResolversToSchema(federatedSchema, referenceResolvers);
  }
  return schema;
}

@andrei-bitca-dc Can you reproduce that with the normal buildSchema? Maybe apollo federation is doing something wrong or they have tried to "fix" that issue

@MichalLytek buildSchemaSync works. It doesn't throw any error.

So please try to reproduce the case with raw graphql-js and raise the issue on theirs side 😉

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 TypeGraphQL Just checking first if TypeGraphQL 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 TypeGraphQL specific info about extensions that I should be aware of...

Looks pretty straightforward, thanks!

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!

TypeGraphQL support is live with stitching directives:

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

note that although TypeGraphQL 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)

it does not seem to support adding actual directive entities (the actual definitions of custom directives).

I'm not sure if I understand that correctly but I think it does:

image

Woops, fantastic!

Example updated. We don't need skipCheck true even!

Was this page helpful?
0 / 5 - 0 ratings