I'm getting the following error despite the mutation being wrapped in the gql tag:
Invariant Violation: Expecting a parsed GraphQL document. Perhaps you need to wrap the query string in a "gql" tag? http://docs.apollostack.com/apollo-client/core.html#gql
This issue is only caused by the mutation code below, I have a query that works.
Code:
<script>
import gql from 'graphql-tag'
export default {
apollo: {
createTask: {
mutation: gql`
mutation Task(
$taskName: String!
$taskDesc: String!
) {
setSession(
taskName: $taskName
taskDesc: $taskDesc
) {
id
taskName
}
}
`,
variables() {
return {
taskName: this.res.data.task_name,
taskDesc: this.res.data.task_description,
}
},
},
},
data() {
return {
createTask: '',
}
},
}
<script>
Smart queries are declared using the apollo object and are ran when the component mounts. If you have queries that you'd like to run this way, then your code would look something like this:
export default {
apollo: {
someQuery: gql`...`,
}
}
However, when working with mutations, you generally don't want to run them on mount. Instead, you want to execute the mutation in response to some user action, like a button click. So the above syntax won't work for mutations. Instead, you need to call this.$apollo.mutate directly inside one of your methods:
export default {
methods: {
createTask() {
this.$apollo.mutate({
mutation: gql`...`,
variables: {...},
// other options
}).then(result => {
// do something with the result
})
}
}
}
I had this problem recently and it was because of an issue with an async import
I am using vue-apollo
async account () {
return {
query: (await import('src/modules/accounts/graphql/accounts.list.query.gql')),
......
I just had to replace that import with a require and it was happy again.
async account () {
return {
query: require('src/modules/accounts/graphql/accounts.list.query.gql'),
......
Related
I'm trying to figure out how to run mutations using Apollo Client.
Here's the mutation I'm trying to run:
export const CREATE_POST = gql`
mutation CreatePost($title: String) {
createPost(
title: $title
body: "Test body, whatever..."
) {
title
body
slug
}
}
`
Here's the functional component that renders a form, and tries to run this mutation once I submit the form:
export default function post() {
const [createPost] = useMutation(CREATE_POST)
async function handleSubmit(event) {
event.preventDefault()
const { data } = await createPost({
variables: { title: "test title" }
})
}
return (<rendering the form here>)
}
I'm getting an error:
[GraphQL error]: Message: Variable "$title" of type "String" used in position expecting type "String!".
If I remove the $title variable from here: mutation CreatePost($title: String) {, the error disappears. It seems like I'm failing to pass it the variable. But as far as I can tell, this part of the code is correct:
const { data } = await createPost({
variables: { title: "test title" }
})
That's how you're supposed to pass variables to mutations, right? What am I doing wrong? How can I debug this?
The full code for the component is here
Query code is here
Solved it thanks to #xadm.
Had to use mutation CreatePost($title: String!) instead of mutation CreatePost($title: String).
I am creating a Next.js blog that uses an API created with KeystoneJS. I am extremely confused by how I can get an individual post on a dynamic route from the post's slug.
The Query
This is how I thought the query should be:
query Post($slug: String) {
Post(where: { slug: $slug }) {
id
}
}
And this was queried like so in a file called post.service.js:
export async function getBySlug(slug) {
return apolloClient
.query({
query: gql`
query Post($slug: String) {
Post(where: { slug: $slug }) {
id
}
}
`,
})
.then((result) => {
return result.data.Post;
});
}
Unsurprisingly, that causes an ApolloError because how would the query know what slug to query the API for when accessing posts/[slug].js?
It's also worth noting that KeystoneJS say on their guides that:
The single entity query accepts a where parameter which must provide an id.
How would I pass the post's ID to the query depending on what slug was accessed at [slug].js and does this mean I can't query by the slug at all?
On [slug].js I am using getStaticPaths() and getStaticProps() like this:
export async function getStaticPaths() {
const posts = await getAll();
const paths = posts.map((post) => ({
params: { slug: post.slug },
}));
return { paths, fallback: false };
}
export async function getStaticProps({ params }) {
const term = await getBySlug(params.slug);
return { props: { post } };
}
How can I do this?
If you're using a where clause rather than matching on id, you have to query allPosts rather than Post.
A tested example, matching a user by their email address:
query($email: String!) {
allUsers(where : {email: $email}){
id
}
}
Variables:
{
"email": "user#email.com"
}
So I think you want:
query($slug: String!) {
allPosts(where: {slug: $slug}) {
id
}
}
Inside gatsby-node.jsI have two queries that gets its data from Contentful. I want to add a new query that loads the data for a particular content based on its slug (a field set in the content model in Contentful).
This is what I have:
return graphql(
`
{
allContentfulBlogPost {
edges {
node {
id
slug
}
}
}
allContentfulCaseStudy(filter: { slug: { ne: "dummy-content" } }) {
edges {
node {
id
slug
}
}
}
contentfulCaseStudy(slug: { eq: $slug }) { // <=== Here is the problem
title
overview
}
}
`
)
.then(result => {
if (result.errors) {
console.log("Error retrieving contentful data", result.errors)
}
})
.catch(error => {
console.log("Error retrieving contentful data", error)
})
}
So, I want to query that particular case study passing the slug in contentfulCaseStudy(slug: { eq: $slug }) but it doesn't work. It throws this error when I start npm run develop:
ERROR #85901 GRAPHQL
There was an error in your GraphQL query:
Variable "$slug" is not defined.
File: gatsby-node.js:13:10
Error retrieving contentful data [
GraphQLError: Variable "$slug" is not defined.
at Object.leave (C:\Edited\edited\edited\edited\node_modules\graphql\validation\rules\NoUndefinedVariables.js:38:33)
at Object.leave (C:\Edited\edited\edited\edited\node_modules\graphql\language\visitor.js:345:29)
at Object.leave (C:\Edited\edited\edited\edited\node_modules\graphql\language\visitor.js:395:21)
at visit (C:\Edited\edited\edited\edited\node_modules\graphql\language\visitor.js:242:26)
at validate (C:\Edited\edited\edited\edited\node_modules\graphql\validation\validate.js:73:24)
at GraphQLRunner.validate (C:\Edited\edited\edited\edited\node_modules\gatsby\dist\query\graphql-runner.js:79:44)
at GraphQLRunner.query (C:\Edited\edited\edited\edited\node_modules\gatsby\dist\query\graphql-runner.js:144:25)
at C:\Edited\edited\edited\edited\node_modules\gatsby\dist\bootstrap\create-graphql-runner.js:40:19
at Object.exports.createPages (C:\Edited\edited\edited\edited\gatsby-node.js:13:10)
at runAPI (C:\Edited\edited\edited\edited\node_modules\gatsby\dist\utils\api-runner-node.js:259:37)
at Promise.catch.decorateEvent.pluginName (C:\Edited\edited\edited\edited\node_modules\gatsby\dist\utils\api-runner-node.js:378:15)
at Promise._execute (C:\Edited\edited\edited\edited\node_modules\bluebird\js\release\debuggability.js:384:9)
at Promise._resolveFromExecutor (C:\Edited\edited\edited\edited\node_modules\bluebird\js\release\promise.js:518:18)
at new Promise (C:\Edited\edited\edited\edited\node_modules\bluebird\js\release\promise.js:103:10)
at C:\Edited\edited\edited\edited\node_modules\gatsby\dist\utils\api-runner-node.js:377:12
at tryCatcher (C:\Edited\edited\edited\edited\node_modules\bluebird\js\release\util.js:16:23) {
locations: [ [Object], [Object] ]
}
Is it possible to request a particular case study passing the slug as parameter? If so, how it's done?
The short answer is that you can't directly. You can filter with a hardcoded parameter, not with a dynamic pre-queried value.
However, what you are trying to do with $slug is to pass a variable via context API.
The flow that are you trying to achieve is:
Fetch and create pages from Contentful data for allContentfulCaseStudy
Use the slug of allContentfulCaseStudy in contentfulCaseStudy to filter your query for each contentfulCaseStudy.
So, you need to move your contentfulCaseStudy into your template.js modifying your gatsby-node.js like this:
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
const result = await graphql(
`
{
allContentfulCaseStudy(filter: { slug: { ne: "dummy-content" } }) {
edges {
node {
id
slug
}
}
}
}
`
)
if (result.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`)
return
}
const caseStudyTemplate= path.resolve(`src/templates/case-study.js`)
result.data.allContentfulCaseStudy.edges.forEach(({ node }) => {
createPage({
path,
component: caseStudyTemplate,
context: {
slug: node.slug,
},
})
})
}
Now, in your case-study.js you have available the slug variable since you are passing it via context in your page query. So:
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"
export default function CaseStudy({ data }) {
const caseStudy= data.contentfulCaseStudy
return (
<Layout>
<div>
<h1>{caseStudy.title}</h1>
</div>
</Layout>
)
}
export const query = graphql`
query($slug: String!) {
contentfulCaseStudy(slug: { eq: $slug }) {
title
overview
}
}
`
Check your localhost:8000/___graphql playground to see if the nested title and overview are under contentfulCaseStudy or if you need to modify the query structure.
Further Reading:
Creating Pages from Data Programmatically
How to add query variables to a page query
Using this mutation:
import produce from 'immer
const [createItem] = useMutation(CREATE_ITEM, {
update (client, { data: { createItem } }) {
const queryResults = client.readQuery({
query: GET_LATEST_ORDER,
variables: { orderDepth: 1 }
})
client.writeQuery({
query: GET_LATEST_ORDER,
variables: { orderDepth: 1 },
data: produce(queryResults, draft => {
draft.orders[0].items.push(createItem)
})
})
}
})
I am unable to get
const { loading, data, refetch } = useQuery(GET_LATEST_ORDER, {
variables: { orderDepth: 1 }
})
to show updated data after the mutation.
The apollo cache is updated correctly. But data on the useQuery does not change.
The issue ended up being the returned object from the mutation was not exactly the same. It was missing an #client field.
While obvious in hindsight no where I searched described this as a reason except for a comment I saw mentioning perhaps missing the __typename.
This would have been obvious had apollo thrown an error. However, no error was thrown, nor existed on the useQuery.
Im using React with Apollo (Apollo Client v2). I have group query which needs to return a single group.
This code is working but I've hard coded HARD-CODED-ID. How can I instead pass the ID as a string from the React component?
In my React component:
const groupQuery = gql`
query Group {
group {
_id
name
}
}
`;
export default graphql(groupQuery, {
props: ({ data }) => ({ ...data }),
})(GroupPage);
My resolver:
Query: {
groups() {
return Groups.find().fetch();
},
group() {
return Groups.findOne('HARD-CODED-ID');
},
}
There's three things that you'll need to do:
1.) If you haven't already, modify the schema on your server so that your query accepts the id as an input, for example:
type Query {
#other queries
group(id: ID!): Group
}
2.) Modify your resolver so that it handles the passed-in id. Assuming you're using graphql-tools:
group(root, { id }) {
return Groups.findOne(id); // did you mean something like findOne({id}) ?
},
3.) Modify your client-side code. Typically, you'll make the id a prop you pass in to your component, and then use that as a variable in your request.
const groupQuery = gql`
query Group($id: ID!) {
group(id: $id) {
_id
name
}
}
`;
// assuming that the component prop is called groupId
export default graphql(groupQuery, {
options: ({ groupId }) => ({
variables: { id: groupId },
}),
})(GroupPage);
Instead of an object, options can be a function, in which case it's passed the component's props as its first parameter. You can then use those props to define the variables your query will use. You can read more about using variables with Apollo client here and here.