MichalLytek / type-graphql

Create GraphQL schema and resolvers with TypeScript, using classes and decorators!
https://typegraphql.com
MIT License
8.02k stars 675 forks source link

Support for Apollo Federation #351

Open voslartomas opened 5 years ago

voslartomas commented 5 years ago

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

MichalLytek commented 5 years ago

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 🔒

voslartomas commented 5 years ago

@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?

MichalLytek commented 5 years ago

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.

MichalLytek commented 5 years ago

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 🔑

j commented 5 years ago

@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.

MichalLytek commented 5 years ago

@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 😕

j commented 5 years ago

@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. :(

j commented 5 years ago

https://github.com/prisma/nexus/issues/148 for reference

MichalLytek commented 5 years ago

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).

j commented 5 years ago

@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?

j commented 5 years ago

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

j commented 5 years ago

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, :(

MichalLytek commented 4 years ago

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. 😉

lgabeskiria commented 4 years ago

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

MichalLytek commented 4 years ago

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

lgabeskiria commented 4 years ago

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

Sector95 commented 4 years ago

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.

lgabeskiria commented 4 years ago

@MichalLytek tested the feature, looks good to me!

lgabeskiria commented 4 years ago

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

MichalLytek commented 4 years ago

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

tuxmachine commented 4 years ago

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

adamovittorio commented 4 years ago

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?

MichalLytek commented 4 years ago

@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

adamovittorio commented 4 years ago

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

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

MichalLytek commented 4 years ago

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

adamovittorio commented 4 years ago

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

Sector95 commented 4 years ago

Is #521 expected to change how this is implemented?

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

MichalLytek commented 4 years ago

@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

SamXDesc commented 4 years ago

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

Can you provide a demo repository for us?

tuxmachine commented 4 years ago

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

graysonhicks commented 4 years ago

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

tuxmachine commented 4 years ago

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

david-golightly-leapyear commented 4 years ago

@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?

gperdomor commented 4 years ago

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

tuxmachine commented 4 years ago

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

gperdomor commented 4 years ago

@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 ðŸĪ”

tuxmachine commented 4 years ago

@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 type-graphql@0.18.0-beta.11

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

gperdomor commented 4 years ago

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

henry-young commented 4 years ago

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?

bsparks commented 4 years ago

@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

MichalLytek commented 4 years ago

@bsparks done in 7ddface 💊

andrei-bitca-dc commented 4 years ago

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;
MichalLytek commented 4 years ago

@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

andrei-bitca-dc commented 4 years ago

@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;
}
MichalLytek commented 4 years ago

@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

andrei-bitca-dc commented 4 years ago

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

MichalLytek commented 4 years ago

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

andrei-bitca-dc commented 4 years ago

@MichalLytek FYI: https://github.com/apollographql/apollo-server/issues/4143.

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 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...

MichalLytek commented 3 years ago

@yaacovCR https://typegraphql.com/docs/extensions.html