In an NX monorepo I'm building 3 nestjs application, an auth-service + user-service and a gateway to start off with. They're all powered by apollo graphql and following the official nestjs documentation.
The issue that I'm having is that with both the user-service and auth-service up and processing requests successfully as individual servers, at the same time, the gateway throws
Couldn't load service definitions for "auth" at http://localhost:3100/apis/auth-service/graphql: 400: Bad Request
The services themselves are standard graphql applications, nothing basic.
The definitions for the gateway are as such:
{
server: {
debug: true,
playground: true,
autoSchemaFile: './apps/gateway/schema.gql',
sortSchema: true,
introspection: true,
cors: ['*'],
path: '/apis/gateway/graphql';
},
gateway: {
supergraphSdl: new IntrospectAndCompose({
subgraphHealthCheck: true,
subgraphs: [
{
name: 'user',
url: resolveSubgraphUrl('user'),
},
{
name: 'auth',
url: resolveSubgraphUrl('auth'),
},
],
}),
}
}
I have created an nx based monorepository that allows you to reproduce this issue easily, by spinning up all 3 servers in watch mode at the same time, on localhost, on different ports.
Everything is mapped as it should. Link: https://github.com/sebastiangug/nestjs-federation.git
The README contains the two commands necessary to run it as well as the same set of instructions plus the health-checks queries available from each service.
Versions used:
"#apollo/subgraph": "2.1.3",
"#apollo/federation": "0.37.1",
"#apollo/gateway": "2.1.3",
"apollo-server-express": "3.6.7",
"graphql": "16.5.0",
"#nestjs/graphql": "10.1.3",
"#nestjs/platform-express": "9.0.8",
Any ideas what I'm doing wrong here or what further configuration is necessary to achieve this?
Thank you
Problem
In a federated nest app, a gateway collects all the schemas from other services and form a complete graph. The question is, how to re-run the schema collection after a sub-schema has been changed?
Current Workaround
Restarting the gateway solves the problem, but it does not seem like an elegant solution.
Other Resources
Apollo server supports managed federation which essentially reverts the dependency between the gateway and the services. Sadly I couldn't find anything relating it to NestJS.
When configuring gateway application with NestJS, and when already have integrated with Apollo studio, then you need not define any serviceList in GraphQLGatewayModule. This is how your module initialization should look like:
GraphQLGatewayModule.forRootAsync({
useFactory: async () => ({
gateway: {},
server: {
path: '/graphql',
},
}),
})
Following environment variables should be declared on the machine hosting your gateway application:
APOLLO_KEY: "service:<graphid>:<hash>"
APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT: "https://uplink.api.apollographql.com/"
Post deployment of Federated GraphQL service, you may need to run apollo/rover CLI service:push command like below to update the schema which writes to schema registry and then gets pushed to uplink URL which is polled by gateway periodically:
npx apollo service:push --graph=<graph id> --key=service:<graph id>:<hash> --variant=<environment name> --serviceName=<service name> --serviceURL=<URL of your service with /graphql path> --endpoint=<URL of your service with /graphql path>
You can add a pollIntervalInMs option to the supergraphSdl configuration.
That will automatically poll the services again in each interval.
#Module({
imports: [
GraphQLModule.forRootAsync<ApolloGatewayDriverConfig>({
driver: ApolloGatewayDriver,
useFactory: async () => ({
server: {
path: '/graphql',
cors: true
},
gateway: {
supergraphSdl: new IntrospectAndCompose({
subgraphs: [
{ name: 'example-service', url: 'http://localhost:8081/graphql' },
],
pollIntervalInMs: 15000,
})
},
})
})
],
})
export class AppModule {}
We use apollo-server-express to expose a graphql server.
For this server, we have set the introspection variable to false to hide our schema from the outer world which works fine for Graphql calls that go over rest calls.
However, when we set up a websocket connection with this same server, we manage to execute introspection queries, even though that during the instantiation of the apollo server, the introspection is explicitly set to false
the config for booting the Apollo-server looks something like this:
{
schema: <schema>,
context: <context_function>,
formatError: <format_error_function>,
debug: false,
tracing: false,
subscriptions: {
path: <graphQl_path>,
keepAlive: <keep_alive_param>,
onConnect: <connect_function>,
onDisconnect: <disconnect_function>
},
introspection: false,
playground: false
};
Did someone had a similar issue? And if yes, were you able to solve it and how?
apollo-server-express version = 2.1.0
npm version = 6.4.1
node version = 10.13.0
What ApolloServer does internally is prevent you from using the __schema and __type resolvers. I assume you could do the same thing:
export const resolvers = {
Query: {
__type() {
throw new Error('You cannot make introspection');
},
__schema() {
throw new Error('You cannot make introspection');
}
}
}
I have an Angular/Apollo GraphQL implementation generating typescript code based on GraphQl endpoint which is surfacing a schema. I can hit the endpoint via Postman with a query and results are returned. However, when I run "graphql-codegen --config codegen.yml" via npm I get this error:
"Error: Query root type must be provided"
The server side is .Net Core implementation using GraphQL ASPNetCore. I have 4 different queries defined and each one works via graphiql.
Any ideas on why query root type is now being returned as null?
GraphQL must have at least one #Query() to be considered valid. So maybe only need add any Query to your Resolver code will be helpful.
Ex:
export class FooResolver {
#Query(() => String)
sayHello(): string {
return 'Hello World!';
}
}
This error throws when your schema stiching/definitions are incorrect. Please check the check your root schema definitions
https://www.advancedgraphql.com/content/schema-stitching
I was having the same issue while using graphql-codegen.
my codegen.yml is
overwrite: true
schema: "http://localhost:3001/graphql"
documents: "src/app/graphql/*.graphql"
generates:
src/generated/graphql.ts:
plugins:
- typescript
- typescript-operations
- typescript-apollo-angular
The issue was coming when I used the plugin typescript-apollo-angular.
I'm using Nodejs with graphql as backend.
The issue got resolved when I renamed the type
RootQuery -> Query
and
RootMutation -> Mutation
in backend schema.
Before
type RootQuery {
_empty: String
}
type RootMutation {
_empty: String
}
schema {
query: RootQuery
mutation: RootMutation
}
After
type Query {
_empty: String
}
type Mutation {
_empty: String
}
schema {
query: Query
mutation: Mutation
}
I ended up reverting back to a previous version of the codebase and reapplied my modifications manually and it works now. The only thing I can think of is I ran npm update which updated apollo-angular from 1.8.3 to 1.10.0.
EDIT Here is my code:
codegen.yml (used to generate code from npm command):
overwrite: true
schema: "https://to_end_point/Prod/api/v1/GraphQL"
documents: "src/**/*.graphql"
generates:
src/generated/graphql.ts:
plugins:
- "typescript"
- "typescript-operations"
- "typescript-apollo-angular"
./graphql.schema.json:
plugins:
- "introspection"
After reverting back to a previous version of Angular code then re-applying my code modifications, GraphQl code generation worked again. The only thing I can think of which could have caused this issue was when I ran npm update. Below is a screenshot of before/after of package.json:
I disabled all plugins on the server except point-of-view.
fastify.register(require('point-of-view'), {
engine: {
nunjucks: require('nunjucks')
},
templates: 'server/views',
includeViewExtension: true
})
The handler is calling the view function.
reply.view('/v1/main', {
nonce: nanoid(1),
token: nanoid(1)
})
The complete error message is:
{"level":50,"time":1547208496144,"msg":"client error","pid":16013,"hostname":"jer-ryzentwo","err":{"type":"Error","message":"Parse Error","stack":"Error: Parse Error","bytesParsed":0,"code":"HPE_INVALID_METHOD","rawPacket":{"type":"Buffer","data":[22,3,1,2,0,1,0,1,105,3,3,167,247,206,59,236,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},"v":1}
{"level":50,"time":1547208496145,"msg":"client error","pid":16013,"hostname":"jer-ryzentwo","err":{"type":"Error","message":"Parse Error","stack":"Error: Parse Error","bytesParsed":0,"code":"HPE_INVALID_METHOD","rawPacket":{"type":"Buffer","data":[10,0,1,0,2,2,20]}},"v":1}
I cannot pinpoint the source of this issue.
Any idea what and why a HPE_INVALID_METHOD error is being generated?