How do I query my API for a single entity by its "slug" with GraphQL? - graphql

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
}
}

Related

How to query data using graphQL based on slug value

I am working with GatsbyJS and GraphQL for the first time and trying to generate certain pages dynamically based on slug values.
Currently I am able to generate all of the relevant pages using the createPages API extension, but the pages are blank, no data is being retrieved.
I'm not quite sure how to retrieve the data from within the template. When I write the query I get errors like "GraphQLError: Syntax Error: Expected Name, found \"$\".", when adding $slug: string! for example.
Any help would be appreciated on this!
gatsby-node.js
exports.createPages = async function ({ actions, graphql }) {
const { data } = await graphql(`
query {
dataJson {
work {
slug
}
}
}
`)
data.dataJson.work.forEach(edge => {
const slug = edge.slug;
actions.createPage({
path: `work/${slug}`,
component: require.resolve(`./src/templates/work-article.tsx`),
context: { slug: slug },
})
})
}
template.js
export const query = graphql`
query($slug: String!) {
dataJson {
work(slug: $slug) {
slug
title
}
}
}
`;
gatsby-config.js
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'data', // Identifier. Will then be queried as `dataJson`
path: './src/assets/data', // Source folder containing the JSON files
},
},
Example of the data saved in data.json
{
"work": [
{
"slug": "my-slug",
"title": "My Title",
"img": "/static/img.jpg"
},
...
}

How to read GrqphQL resolver

While I try to learn GraphQL-Tools.I found this article.In this article,example resolvers are described as follows.
const resolvers = {
Query: {
posts: () => posts,
author: (_, { id }) => find(authors, { id })
},
Mutation: {
upvotePost: (_, { postId }) => {
const post = find(posts, { id: postId })
if (!post) {
throw new Error(`Couldn't find post with id ${postId}`)
}
post.votes += 1
return post
}
},
Author: {
posts: author => filter(posts, { authorId: author.id })
},
Post: {
author: post => find(authors, { id: post.authorId })
}
}
I understood that Queryand MutationandSubscriptionis main function of Graphql resolvers.
My question is what is Author,Post in this resolver ?
I can understand QueryandMutationsection of this resolver. but how to think about Authorand Post?
It seems that it defined functions.
Thanks
That is Trival Resolver
https://graphql.org/learn/execution/#trivial-resolvers
Until Graphql has a completely understandable return value.
Graphql goes on to the next search with the returned type.
Those functions [Author, Post] tell to Graphql how to find the object's value.
For exameple if "PostFindById" reoslver promised to graphql that will return "author" field But instead it return "Post" Graphql will excute Trival Rsolver to find value of author field with given "Post".
So it will execute this resolver with given 'post'
Post: {
author: post => find(authors, { id: post.authorId })
}

Gastby - Add a GraphQL query with parameters in gastby-node.js

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

Query variables not being passed down from vue component in apollo

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.

Pass ID from React to Apollo to find correct result?

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.

Resources