Apollo federation can't start gateway - apollo-server

Hello I want to use federation.I followed this tutorial. I can start my subgraph but when I start my gateway, I get this error:
Error: A valid schema couldn't be composed. The following composition errors were found:
Cannot extend type "Query" because it is not defined. Did you mean "User"?
Cannot extend type "Mutation" because it is not defined.
I even extended Query and Mutation but got another error.
my gateway code:
import fastify from "fastify";
import { ApolloServer } from "apollo-server-fastify";
import { ApolloGateway } from "#apollo/gateway";
const PORT = process.env.PORT || 4000;
const IP = "0.0.0.0";
const app = fastify({ trustProxy: true });
const gateway = new ApolloGateway({
serviceList: [{ name: "amazon", url: "http://localhost:4001/graphql" }],
});
(async () => {
try {
const { schema, executor } = await gateway.load();
const server = new ApolloServer({ schema, executor });
server.start().then(() => {
app.register(server.createHandler({ path: "/graphql" }));
app.listen(PORT, IP, (err) => {
if (err) {
console.error(err);
} else {
console.log("Server is ready at port 4000");
}
});
});
} catch (error) {
console.log("dick");
console.log("err", error);
}
})();
my schema in subgraph:
type Query {
getUsers: [User]!
}
type Mutation {
createUser(name: String!): Boolean!
}
type User #key(fields: "id") {
id: ID!
name: String!
}

Most likely you're using graphql version 16. Following https://www.apollographql.com/docs/federation/gateway/ you must use 15 for now (December 2021). Try:
yarn add graphql#15
Another detail you can do on latest versions is simply pass the gateway to ApolloServer, like this:
const server = new ApolloServer({ gateway });

Related

Apollo v3 GraphQL Subscription Error: Must provide document

I'm testing out subscription on Apollo v3 using the example setup on the docs. But I get the above error. I'm not sure what I'm missing.
Here's the complete reproducible code on Github gist
const typeDefs = gql`
type Subscription {
incremented: Int
}
`;
const resolvers = {
Subscription: {
incremented: {
subscribe: () => pubsub.asyncIterator('NUMINCREMENTED'),
},
},
};
(async function () {
const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
const subscriptionServer = SubscriptionServer.create(
{
schema,
execute,
subscribe,
},
{ server: httpServer }
);
const server = new ApolloServer({
schema,
plugins: [
{
async serverWillStart() {
return {
async drainServer() {
subscriptionServer.close();
},
};
},
},
],
});
})();
Here's the error when I try the subscription on Apollo Studio.
I had the same problem. Downgrade apollo-server-express to 3.1.2.
Then everything should work.

How to run a mutation in ApolloServer using the GraphQL Playground?

I'm using node.js, express and apollo-server-express. With the following code:
const express = require('express');
const { ApolloServer, gql } = require('apollo-server-express');
const typeDefs = gql`
type Book { title: String author: String }
type Query { books: [Book] }
type Mutation { change_title(new_title: String): Book }
`;
const books = [
{ title: 'The Awakening', author: 'Kate Chopin', },
{ title: 'City of Glass', author: 'Paul Auster', },
];
const resolvers = {
Query: { books: () => books, },
Mutation: {
change_title: (parent, args) => {
books[0].title = args.new_title
return books[0]
}
}
};
const server = new ApolloServer({ typeDefs, resolvers, });
const app = express();
server.applyMiddleware({ app });
app.listen({ port: 4000 }, () =>
console.log(`Server ready at http://localhost:4000${server.graphqlPath}`)
);
When I enter the mutation in the GraphQL Playground, like so:
{
change_title(new_title: "Something") {
title
}
}
I get the following error: "Cannot query field "change_title" on type "Query"."
My goal is to be able to run mutations. If I should be doing it another way or if there's eror, please let me know. Thanks!
GraphQL playground treats all types as queries unless otherwise specified.
mutation {
change_title(new_title: "Something") {
title
}
}

Graphql Api Chuck Norris

I need help building a GraphQL Api that wraps the ChuckNorris.io API
The API sholud have aQuery type that resolves all Categories
(https://api.chuckmorris.io/jokes/categories)
The Api should have Query type that resolves a random joke given as an argument (https://api.chucknorris.io/jokes/random?category={category})
const express=require('express');
const {ApolloServer,gql}=require('apollo-server-express');
const fetch=require('node-fetch');
const typeDefs=gql`
type Joke{
icon_url:String,
id:String,
url:String
value: String
}
type Category{
animal:String
career:String
celebrity:String
dev:String
explicit:String
fashion:String
food:String
history:String
money:String
movie:String
music:String
political:Strig
religion:String
science:String
sport:String
travel:String
}
type Query{
getCategory(category:String!):Joke
category:Category
}
`
const resolvers={
Query:{
getCategory: async(_,{category})=>{
const response=await fetch(`https://api.chucknorris.io/jokes/random?category=${category}`)
return response.json();
},
category: async(_,{})=>{
const response=await fetch('https://api.chucknorris.io/jokes/categories')
return response.json();
}
}
}
const server= new ApolloServer({typeDefs,resolvers});
const app=express();
server.applyMiddleware({app});
app.listen({port:4000},()=>
console.log('Now browse to http://localhost:4000' + server.graphqlPath)
)
your query for type category should return a list of strings (array)
so
export const typeDefs = gql`
type Joke {
value: String!
id:ID!
icon_url:String!
}
type Query {
getAllCategories:[String!]!
randomJoke(category: String!):Joke
}
`;
for your resolver, you don't need fetch. apollo provides datasources to connect to external REST APIs like the one you have.
so install the npm package "apollo-datasource-rest" and add it to your instance of apollo server like so
const server = new ApolloServer({
typeDefs,
resolvers,
dataSources: ()=>({
jokeinstance : new Jokenorris
})
})
then create the datasource class for Jokenorris and import appropriately or do everything in one src file as you did.
import pkg from "apollo-datasource-rest";
const { RESTDataSource } = pkg;
export class Jokenorris extends RESTDataSource {
constructor() {
super();
this.baseURL = "https://api.chucknorris.io/jokes";
}
async getAllCategories() {
const res = await this.get("categories");
return res;
}
async getRandomJoke({ category }) {
const response = await this.get("random", { category: category });
return response;
}
}
then your resolveer can look like so, you can ignore the exports and imports if you chunked everything in one file
export const resolvers = {
Query: {
allJokeCategories: (_, __, { dataSources }) =>
dataSources.jokeinstance.getAllCategories(),
randomJoke: (_, {category}, {dataSources})=>
dataSources.jokeinstance.getRandomJoke({category:category})
},
};

Is it possible to have partially federated gateway?

I'd like to federate services, but let the federation gateway also hold own schema and logic that would proxy REST API endpoints for simplicity. Now it looks like I need to have federation gateway service, federated graphql service(s) and the rest<->graphql bridge service separately. Anyhow in our case the rest-graphql gateway could be living in the federation-gateway at least for the time being to avoid unnecessary bootstrapping & maintenance.
Looks like Apollo federation gateway has localServiceList that seemingly serves exactly this purpose. An example config:
const gateway = new ApolloGateway({
serviceList: [
{ name: "some-service", url: "http://localhost:40001/graph" }
],
localServiceList: [
{ name: "rest-bridge", typeDefs }
]
});
But it does not do the trick: If there is localServiceList, it skips the serviceList.
So the question is: Is this possible to hold also own schema & logic in Apollo Federation gateway?
Yes, this can be done:
import { buildFederatedSchema } from '#apollo/federation';
import {
ApolloGateway,
LocalGraphQLDataSource,
RemoteGraphQLDataSource
} from '#apollo/gateway';
import gql from 'graphql-tag';
const localServices = {
foo: {
schema: {
typeDefs: gql`
// ...
`,
resolvers: {
// ...
}
}
},
bar: {
schema: {
typeDefs: gql`
// ...
`,
resolvers: {
// ...
}
}
}
};
const remoteServices = {
baz: {
url: 'http://baz.local/graphql'
},
qux: {
url: 'http://qux.local/graphql'
}
};
const services = {
...localServices,
...remoteServices
};
// By providing a protocol we trick ApolloGateway into thinking that this is a valid URL;
// otherwise it assumes it's a relative URL, and complains.
const DUMMY_SERVICE_URL = 'https://';
const gateway = new ApolloGateway({
// We can't use localServiceList and serviceList at the same time,
// so we pretend the local services are remote, but point the ApolloGateway
// at LocalGraphQLDataSources instead...
serviceList: Object.keys(services).map(name => ({
name,
url: services[name].url || DUMMY_SERVICE_URL
})),
buildService({ name, url }) {
if (url === DUMMY_SERVICE_URL) {
return new LocalGraphQLDataSource(
buildFederatedSchema(
services[name].schema
)
);
} else {
return new RemoteGraphQLDataSource({
url
});
}
}
});
const apolloServer = new ApolloServer({
gateway,
subscriptions: false
});

Apollo react: combining rest and graphql with link-state

I am trying to use REST endpoints to post data and GraphQL for query and fetch along with apollo-link-state. My rest endpoint is getting hit and application is getting created. But when I try to run the query to write to cache it's not hitting the graphql endpoint. and I keep getting the following error:
Unhandled Rejection (Error): Can't find field findApplicationByUuid({"uuid":"912dc46d-2ef8-4a77-91bc-fec421f5e4bc"}) on object (ROOT_QUERY) {
"application": {
"type": "id",
"id": "$ROOT_QUERY.application",
"generated": true
}
}.
Here are my GQL query
import gql from 'graphql-tag';
const START_APP = gql`
mutation startApp($type: String!) {
application: startApp( input: { applicationType: $type})
#rest(type: "Application", path: "v1/member/application/create", method: "POST") {
uuid: applicationUuid
}
}
`;
const GET_APP = gql`
query getAppByUuid($uuid: String!) {
application: findApplicationByUuid(uuid: $uuid) {
uuid,
type,
version,
}
}
`;
export {
START_APP,
GET_APP,
};
Here is my resolver:
import { START_APP, GET_APP } from './application'
import client from '../apolloClient';
const startApp = async (_, { type }, { cache }) => {
client.mutate({
variables: { type },
mutation: START_APP,
}).then(({ data: { application } }) => {
const { uuid } = application;
const { data } = cache.readQuery({
query: GET_APP,
variables: { uuid },
});
cache.writeQuery({
query: GET_APP,
data,
});
});
};
const resolvers = {
Mutation: {
startApp,
},
};
Here are my links:
import { resolvers, defaults } from './resolvers';
const cache = new InMemoryCache();
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.map(({ message, locations, path }) =>
console.log(`[GQL Error]: Msg: ${message}, Loc: ${locations}, Path: ${path}`));
if (networkError) console.log(`[Network error]: ${networkError}`);
});
const stateLink = withClientState({
cache,
defaults,
resolvers,
});
const restLink = new RestLink({
uri: 'http://localhost:7010/api/',
credentials: 'include',
});
const batchHttpLink = new BatchHttpLink({
uri: 'http://localhost:7010/api/graphql',
credentials: 'include',
});
const httpLink = new HttpLink({
uri: 'http://loaclhost:7010/api/graphql',
credentials: 'include',
});
const link = ApolloLink.from([
errorLink,
stateLink,
restLink,
httpLink,
]);
my client
const client = new ApolloClient({
link,
cache,
});
My react component:
// Remote Mutation
const START_APP = gql`
mutation startApp($type: String!) {
startApp(type: $type) #client {
uuid
}
}
`;
const StartApp = ({ match }) => {
const { type } = match.params;
return (
<Mutation mutation={START_APP} variables={{ type }}>
{startApp => (<button onClick={startApp}>click me</button>)}
</Mutation>
)
};
When I hit the button it calls create endpoint and creates the app and returns the uuid. But the following I want to happen is hit the graphql endpoint and query for the application using the uuid returned from the rest request, and write that data to the cache/state.

Resources