I have a simple query which takes in an ID parameter, but it is not working. It says "TypeError: Cannot read property 'taskId' of undefined" . So I think it does not recognize the 'this' keyword for some reason.
Please take a look:
Apollo query from frontend component:
getCommentsByTask: {
query: GET_COMMENTS_BY_TASK,
variables: {
taskId: this.taskId
},
result({ data }) {
this.getComments = data;
console.log("data", data);
}
}
Defined the query in frontend:
query GET_COMMENTS_BY_TASK($taskId: ID!) {
getCommentsByTask(taskId: $taskId) {
id
parentId
ownerId
text
}
}
Resolver in server:
async getCommentsByTask (_, {taskId}, context) {
const userId = getUserId(context)
const user = await User.findById(userId)
if (!user) return
const comments = await Comment.findById(taskId)
return comments
}
Schema:
type Query {
getCommentsByTask(taskId: ID!): [Comment]
}
Assuming that's a smart query, variables should be a (regular, non-arrow) function if you need access to this.
Related
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
I'm getting an error when writing a query to store after a mutation. The mutation works and i'm able to read the query post mutation. When i write the same query to the store cache i get the following Error:
index.js:2178 Error: Error writing result to store for query:
query ($applicationId: Int) {
vApplicationApprovalChainList(ApplicationId: $applicationId) {
id
approvalOrder
approverId
name
applicationId
__typename
}
}
Cannot read property 'vApplicationApprovalChainList' of undefined
at writeToStore.js:101
at Array.forEach (<anonymous>)
at writeSelectionSetToStore (writeToStore.js:97)
at writeResultToStore (writeToStore.js:75)
at InMemoryCache../node_modules/apollo-cache-inmemory/lib/inMemoryCache.js.InMemoryCache.write (inMemoryCache.js:99)
Here is my code.. the mutation and store.readQuery works but the store.writeQuery gives above error. Thank you in advance for any feedback.
APPROVERSLIST_QUERY = gql`
query ($applicationId:Int){
vApplicationApprovalChainList(ApplicationId:$applicationId){
id
approvalOrder
approverId
name
applicationId
}
}
`;
handleClick() {
const { row, mutate} = this.props;
mutate({
variables: {
id: row.id
},
update: (store, { data: { deleteApprover } }) => {
const newdata = store.readQuery({
query: APPROVERSLIST_QUERY,
variables: { applicationId: row.applicationId }
});
console.log(newdata);
newdata.vApplicationApprovalChainList = newdata.vApplicationApprovalChainList.filter(approver => approver.id !== deleteApprover.id);
store.writeQuery({
query: APPROVERSLIST_QUERY, newdata });
}
});
}
You're not passing in the new data to writeQuery. The object passed to writeQuery must have a property named data containing the new data. Additionally, since your query contains variables, you will need to include that information as well.
store.writeQuery({
query: APPROVERSLIST_QUERY,
data: newdata,
variables: {
applicationId: row.applicationId,
},
});
Please see the official docs for more examples and a more thorough explanation of the two methods.
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.
I think I have the backend subscription setup correctly. I am using angular on the client side, when I try to call subscribe I got an error
passwordUpdatedSubscription = gql`
subscription passwordUpdated{passwordUpdated{name password}}
`;
// Apollo Subscription
var subscription = this.apollo.subscribe({
query: this.passwordUpdatedSubscription
});
subscription.subscribe(
{
next(data) {
console.log(data);
},
error(err) { console.error('err', err); },
}
);
And then this is the error appears in the console
{"type":"subscription_fail","id":0,"payload":{"errors":[{"message":"Cannot read property 'subscribe' of undefined"}]}}
Maybe I am missing something on the backend? Do I need to define the setupFunctions in the SubscriptionManager?
This is my SubscriptionManager
const sub = require('graphql-subscriptions');
const pubSub = new sub.PubSub();
const manager = new sub.SubscriptionManager({
schema,
pubSub
});
This is my schema in graphQL
const graphql = require('graphql');
var schema = graphql.buildSchema(`
type Subscription {
passwordUpdated: User
}
type Mutation {
setMessage(message: String): String,
updateUserPassword(userName: String, password: String): User!
}
type Query {
getMessage: String,
getUsers: [User],
findUsers(userName: String): [User]
}
type User {
name: String,
password: String
}
`);
Yes you are missing the setup function. You could take a look at this links graphql subscription docu or example.
Your subscription manager could look like this:
const manager = new sub.SubscriptionManager({
schema,
pubSub,
setupFunctions: {
passwordUpdated: (options, args) => ({ // name of your graphQL subscription
passwordUpdatedChannel: { // name of your pubsub publish-tag
filter: () => {
return true
},
},
}),
},
});
When you call the pubsub publish function you have to write it like this pubsub.publish("passwordUpdatedChannel").
Sidenode: You might want to add the id of the user that has the password changed to the subscription. If you do that you can add it to the filter option, could look like this filter: (user) => {return user.id === args.userId}