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
}
}
Related
I'm fairly new to GraphQL and currently familiarizing myself by making a quiz application using React on the front-end.
At the moment, I'm busy with my back-end. After successfully setting up queries and mutations, I am finding it difficult to get subscriptions working. When using GraphiQL, I am getting null as an output instead of "Your subscription data will appear here..."
Queries and the mutation for adding the quiz works.
The entry point of my server, app.js:
const express = require("express");
const mongoose = require("mongoose");
const { graphqlHTTP } = require("express-graphql");
const schema = require("./graphql/schema");
const cors = require("cors");
const port = 4000;
//Subscriptions
const { createServer } = require("http");
const { SubscriptionServer } = require("subscriptions-transport-ws");
const { execute, subscribe } = require("graphql");
const subscriptionsEndpoint = `ws://localhost:${port}/subscriptions`;
const app = express();
app.use(cors());
mongoose.connect("mongodb://localhost/quizify", {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true,
useFindAndModify: false
});
mongoose.connection.once("open", () => console.log("connected to database"));
app.use("/graphql", graphqlHTTP({
schema,
graphiql: true,
subscriptionsEndpoint,
}));
const webServer = createServer(app);
webServer.listen(port, () => {
console.log(`GraphQL is now running on http://localhost:${port}`);
//Set up the WebSocket for handling GraphQL subscriptions.
new SubscriptionServer({
execute,
subscribe,
schema
}, {
server: webServer,
path: '/subscriptions',
});
});
Below is from the schema definitions, schema.js:
const { graphqlHTTP } = require("express-graphql");
const graphql = require("graphql");
const { PubSub } = require("graphql-subscriptions");
const pubsub = new PubSub();
//Import of Mongoose Schemas:
const Quiz = require("../models/quiz");
const {
GraphQLObjectType,
GraphQLList,
GraphQLSchema,
GraphQLNonNull,
GraphQLID,
GraphQLString,
GraphQLBoolean
} = graphql;
const QuizType = new GraphQLObjectType({
name: "Quiz",
fields: () => ({
id: { type: GraphQLID },
title: { type: GraphQLString },
questions: {
type: new GraphQLList(QuestionType),
resolve(parent, args) {
return Question.find({ quizId: parent.id });
}
},
creator: {
type: UserType,
resolve(parent, args) {
return User.findById(parent.creatorId);
}
}
})
});
const NEW_QUIZ_ADDED = "new_quiz_added";
const Subscription = new GraphQLObjectType({
name: "Subscription",
fields: {
quizAdded: {
type: QuizType,
subscribe: () => {
pubsub.asyncIterator(NEW_QUIZ_ADDED);
},
},
}
});
const Mutation = new GraphQLObjectType({
name: "Mutation",
fields: {
createQuiz: {
type: QuizType,
args: {
title: { type: new GraphQLNonNull(GraphQLString) },
creatorId: { type: new GraphQLNonNull(GraphQLID) }
},
resolve(parent, args) {
const newQuiz = new Quiz({ //Quiz imported from Mongoose schema.
title: args.title,
creatorId: args.creatorId,
});
pubsub.publish(NEW_QUIZ_ADDED, { quizAdded }); //NEW_QUIZ_ADDED - a constant defined above for easier referencing.
return newQuiz.save();
}
},
},
});
module.exports = new GraphQLSchema({
query: RootQuery,
mutation: Mutation,
subscription: Subscription,
});
I've looked around, however I'm not finding an method that works for this kind of site. I know it might sound like a simple problem. Any assistance would be greatly appreciated!
I'm writing an Apollo server plugin for node.js, and my goal is to improve my teams debugging experience. My plugin currently looks something like this:
export function eddyApolloPlugin(): ApolloServerPlugin {
return {
requestDidStart(requestContext) {
// Set requestId on the header
const requestId = (requestContext?.context as EddyContext)?.requestId;
if (requestId) {
requestContext.response?.http?.headers.set('requestId', requestId);
}
return {
willSendResponse(context) { // <== Where do I find the "path" in the schema here?
// Inspired by this: https://blog.sentry.io/2020/07/22/handling-graphql-errors-using-sentry
// and the official documentation here: https://docs.sentry.io/platforms/node/
// handle all errors
for (const error of requestContext?.errors || []) {
handleError(error, context);
}
},
};
},
};
}
I would like to know if I can access the path in the schema here? It's pretty easy to find the name of mutaiton/query with operation.operationName, but where can I get the name of the query/mutation as defined in the schema?
Solution
export function eddyApolloPlugin(): ApolloServerPlugin {
return {
requestDidStart(requestContext) {
// Set requestId on the header
const requestId = (requestContext?.context as EddyContext)?.requestId;
if (requestId) {
requestContext.response?.http?.headers.set('requestId', requestId);
}
return {
didResolveOperation(context) {
const operationDefinition = context.document
.definitions[0] as OperationDefinitionNode;
const fieldNode = operationDefinition?.selectionSet
.selections[0] as FieldNode;
const queryName = fieldNode?.name?.value;
// queryName is what I was looking for!
},
};
},
};
}
Your requirement is not very clear. If you want to get the name of the query/mutation to distinguish which query or mutation the client sends.
You could get the name from context.response.data in willSendResponse event handler.
E.g.
server.ts:
import { ApolloServer, gql } from 'apollo-server';
import { ApolloServerPlugin } from 'apollo-server-plugin-base';
import { parse, OperationDefinitionNode, FieldNode } from 'graphql';
function eddyApolloPlugin(): ApolloServerPlugin {
return {
requestDidStart(requestContext) {
return {
didResolveOperation(context) {
console.log('didResolveOperation');
const obj = parse(context.request.query!);
const operationDefinition = obj.definitions[0] as OperationDefinitionNode;
const selection = operationDefinition.selectionSet.selections[0] as FieldNode;
console.log('operationName: ', context.request.operationName);
console.log(`${context.operation!.operation} name:`, selection.name.value);
},
willSendResponse(context) {
console.log('willSendResponse');
console.log('operationName: ', context.request.operationName);
console.log(`${context.operation!.operation} name:`, Object.keys(context.response.data!)[0]);
},
};
},
};
}
const typeDefs = gql`
type Query {
hello: String
}
type Mutation {
update: String
}
`;
const resolvers = {
Query: {
hello() {
return 'Hello, World!';
},
},
Mutation: {
update() {
return 'success';
},
},
};
const server = new ApolloServer({ typeDefs, resolvers, plugins: [eddyApolloPlugin()] });
const port = 3000;
server.listen(port).then(({ url }) => console.log(`Server is ready at ${url}`));
GraphQL Query:
query test {
hello
}
the logs of the server:
didResolveOperation
operationName: test
query name: hello
willSendResponse
operationName: test
query name: hello
GraphQL Mutation:
mutation test {
update
}
the logs of the server:
didResolveOperation
operationName: test
mutation name: update
willSendResponse
operationName: test
mutation name: update
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})
},
};
I would like to create small project using GraphqQL, ApolloServer, but I encountered a problem, that I can't solve. I wrote it based on several documentation.
const { ApolloServer, gql } = require('apollo-server');
const { RESTDataSource } = require('apollo-datasource-rest');
const typeDefs = gql`
type Word {
id: ID!
word: String!
translation: String!
}
type Query {
words: [Word]
word(id: ID): Word
}
`;
class WordsAPI extends RESTDataSource {
constructor() {
super();
this.baseURL = 'localhost:5001/'
}
async getWord(id) {
return this.get(`word/${id}`)
}
async getAllWords() {
return this.get('words')
}
async getSpecifiedWords(SpecWord) {
return this.get(`words/${SpecWord}`)
}
}
const resolvers = {
Query: {
words: (_, __, { dataSources }) =>
dataSources.wordsAPI.getAllWords(),
word: async (_source, { id }, { dataSources }) => {
return dataSources.wordsAPI.getWord(id);
}
}
}
const server = new ApolloServer({
typeDefs,
resolvers,
dataSources: () => {
return {
wordsAPI: new WordsAPI()
};
},
context: () => {
return {
};
},
});
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
After
query {
words {
translation
}
}
In playground I am getting Invalid URL: words.
At localhost:5001/words is database and in Postman it works.
What did I bad?
Im trying to return a string with React and GraphQL but I'm getting stuck at the first stage. Here is my attempt:
import { makeExecutableSchema } from 'graphql-tools';
const typeDefs = `
type Query {
author: Person
}
type Person {
name: String
}
`;
const resolvers = {
Query: {
author: { name: 'billy' },
},
};
const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
createApolloServer({ schema });
And this is my understanding of that code:
In my schema I've defined a Query called author which should return a Person.
A Person has a name field which is a string.
My resolver has a Query called author which should return an object with a name field of value 'billy'
However in my Graphicool browser tools this query:
query {
author{
name
}
}
Returns this:
{
"data": {
"author": null
}
}
Resolvers are functions which GraphQL will call when resolving that particular field. That means your resolvers object should look more like this:
const resolvers = {
Query: {
author: () => ({ name: 'billy' }),
},
}
Or, alternatively,
const resolvers = {
Query: {
author() {
return { name: 'billy' }
},
},
}
You can check out the docs for more information.
import { createApolloServer } from 'meteor/apollo';
import { makeExecutableSchema } from 'graphql-tools';
import merge from 'lodash/merge'; // will be useful later when their are more schemas
import GroupsSchema from './Groups.graphql';
import GroupsResolvers from './resolvers';
const typeDefs = [GroupsSchema];
const resolvers = merge(GroupsResolvers);
const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
createApolloServer({ schema });
In ./Groups.graphql:
type Query {
hi: String
groups: [Group]
group: Group
}
type Group {
name: String
}
In './resolvers':
export default {
Query: {
hi() {
return 'howdy';
},
groups() {
return [{ name: 'one', _id: '123' }, { name: 'two', _id: '456' }];
// return Groups.find().fetch();
},
group() {
return { name: 'found me' };
},
},
};
In a React component:
const mainQuery = gql`
{
groups {
name
}
}
`;
export default graphql(mainQuery)(ComponentName);