How to query data using graphQL based on slug value - graphql

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"
},
...
}

Related

Query not refetching after delete mutation

I have a simple delete mutation, which is supposed to refetch all the searches using the RecentSearchesQuery.
const onDeletePress = async (searchResultId: string) => {
await deleteSearch({
variables: {
_id: searchResultId,
},
refetchQueries: [{ query: RecentSearchesQuery }, "getUserSearches"],
});
};
const {
data: recentsData,
error: recentsError,
loading: recentsLoading,
} = useQuery(RecentSearchesQuery, {
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
});
When checking my DB, i do see the search being deleted, but the recentsData contains the old data, and the recentsLoading isn't true after the mutation. I am using the same structure in other places in my code, which works. What am I doing wrong here?

How do I query my API for a single entity by its "slug" with 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
}
}

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

Post data to a graphql server with request-promise

I'm using the request-promise library to make http request to a graphql server. To achieve a query, I'm doing this:
const query = `
{
user(id:"123173361311") {
_id
name
email
}
}
`
const options = {
uri: "http://localhost:5000/graphql",
qs: { query },
json: true
}
return await request(options)
The above code is working fine. However I'm confused about how to go about a mutation since I need to specify both the actual mutation and the inputData like this:
// Input
{
name: "lomse"
email: "lomse#lomse.com"
}
const mutation = `
mutation addUser($input: AddUserInput!){
addUser(input: $input) {
_id
name
email
}
}
`
const option = {
uri: "http://localhost:5000/graphql",
formData: {mutation},
json: true,
// how to pass the actual data input
}
request.post(option)
Or is it that the request-promise library isn't designed for this use case?
Use body, not formData. Your body should consist of three properties:
query: The GraphQL document you're sending. Even if the operation is a mutation, the property is still named query.
variables: A map of your variable values serialized as a JSON object. Only required if your operation utilized variables.
operationName: Specifies which operation to execute. Only required if your document included multiple operations.
request.post({
uri : '...',
json: true,
body: {
query: 'mutation { ... }',
variables: {
input: {
name: '...',
email: '...',
},
},
},
})
The graphql-request library seems to do what I needed the request-promise library to do.
import { request } from 'graphql-request'
const variables = {
name: "lomse",
email: "lomse#lomse.com"
}
const mutation = `
mutation addUser($input: AddUserInput!){
addUser(input: $input) {
_id
name
email
}
}
`
response = await request(uri, mutation, {input: variables})

Gatsby - making a dynamic query to Contentful

I'm working on a simple e-commerce based on Gatsby and Contentful.
One of my pages is CartPage.
So I'm trying to make a query to Contenful to get the necessary info about my products, so I want to pass a list of product ids that are in the user's cart.
Here's my component and query:
export interface CartPageProps {
data: {
products: GQL.ContentfulProductConnection;
};
}
const CartPage = ({ data }: CartPageProps) => {
const mockCart = [1, 2];
console.log(data);
return (
<IndexLayout>
<Page>
//
// Layout goes here
//
</Page>
</IndexLayout>
)
}
export default CartPage;
const pageQuery = graphql`
query ProductsInCart($mockCart: [Int], $lang: String) {
products: allContentfulProduct(filter: { idProduct: { in: [1, 2] }, node_locale: { eq: "en" } }) {
edges {
node {
id
idProduct
price
title
quantity
photo {
resize(height: 200, width: 200) {
src
}
}
slug
}
}
}
}
`;
Now all I get on a console.log(data) is undefined.
If I do the same query but with useStaticQuery with all the data hardcoded - I get the desired data. But useStaticQuery doesn't accept variables.
Am I doing something wrong here? How can I pass variables to the query and put my data to the props?
Gatsby uses GraphQL at build-time and not for live sites. as said here So it's not intended for making GraphQL queries on the go.

Resources