I have just started using graphql for the first time as I have integrated my NEXTJS app with strapi. But I have received this error message Cannot destructure property 'data' of '(intermediate value)' as it is undefined.
I followed this tutorial - enter link description here
Just modified it to what I wanted. This is my graphql:
query {
posts {
data {
attributes {
heading
}
}
}
}
And this is my vs code:
export async function getStaticProps() {
const client = new ApolloClient({
url: 'http://localhost:1337/graphql/',
cache: new InMemoryCache(),
})
const { data } = await client.query({
query: gql`
query {
posts {
data {
attributes {
heading
}
}
}
}
`,
})
return {
props: {
posts: data.posts,
},
}
}
FULL CODE:
import { ApolloClient, InMemoryCache, gql } from '#apollo/client'
export default function Blog({ posts }) {
console.log('posts', posts)
return (
<div>
{posts.map(post => {
return (
<div>
<p>{posts.heading}</p>
</div>
)
})}
</div>
)
}
export async function getStaticProps() {
const client = new ApolloClient({
url: 'http://localhost:1337/graphql/',
cache: new InMemoryCache(),
})
const { data } = await client.query({
query: gql`
query {
posts {
data {
attributes {
heading
}
}
}
}
`,
})
return {
props: {
posts: data.posts,
},
}
}
I really don't know where to begin with this.
Firstly check whether or not you are receiving empty data from API.
If its array, check its length or use methods like Array.isArray(myArray).
If its object, make a function like this to check objects.
function isObjectEmpty(obj) {
return (
!!obj && // 👈 null and undefined check
Object.keys(obj).length === 0 &&
obj.constructor === Object
)
}
export default isObjectEmpty
if the data is empty return notFound prop as true to show your 404 page.
// This function gets called at build time
export async function getStaticProps({ params, preview = false }) {
// fetch posts
// check validity data
return isObjectEmpty(pageData)
? { notFound: true }
: {
props: {
posts
}
}
}
Secondly add a failsafe mechanism like the use of optional-chaining to securely access nested values/properties.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
export default function Blog({ posts }) {
console.log('posts', posts)
return (
<div>
{posts?.length && posts?.map(post => {
return (
<div>
<p>{posts?.heading}</p>
</div>
)
})}
</div>
)
}
I was running into the same error while testing using Jest.
It turns out I was mocking all of graphql, but I had to specifically mock the return value.
Related
I'm building a webpage that consumes the spaceX graphQL api, using apollo as a client. On the landing page I want to display a 'launches' card, that when clicked on, directs to a new page with details about that particular launch, as below:
index.js
import { ApolloClient, InMemoryCache, gql } from "#apollo/client"
import Link from 'next/link'
export const getStaticProps = async () => {
const client = new ApolloClient({
uri: 'https://api.spacex.land/graphql/',
cache: new InMemoryCache()
})
const { data } = await client.query({
query: gql`
query GetLaunches {
launchesPast(limit: 10) {
id
mission_name
launch_date_local
launch_site {
site_name_long
}
links {
article_link
video_link
mission_patch
}
rocket {
rocket_name
}
}
}
`
});
return {
props: {
launches: data.launchesPast
}
}
}
export default function Home({ launches }) {
return (
<div>
{launches.map(launch => {
return(
<Link href = {`/items/${launch.id}`} key = {launch.id}>
<a>
<p>{launch.mission_name}</p>
</a>
</Link>
)
})}
</div>
)
}
I've set up a new page items/[id].js to display information about individual launches, but this is where the confusion is. Using a standard REST api I'd simply use fetch, then append the id to the end of the url to retrieve the desired data. However I'm not sure how to do the equivalent in graphQL, using the getStaticPaths function. Any suggestions?
Here's items/[id]/js, where I'm trying to render the individual launch data:
import { ApolloClient, InMemoryCache, gql } from "#apollo/client"
export const getStaticPaths = async () => {
const client = new ApolloClient({
uri: "https://api.spacex.land/graphql/",
cache: new InMemoryCache(),
});
const { data } = await client.query({
query: gql`
query GetLaunches {
launchesPast(limit: 10) {
id
}
}
`,
});
const paths = data.map((launch) => {
return {
params: { id: launch.id.toString() },
};
});
return {
paths,
fallback:false
}
};
export const getStaticProps = async (context) => {
const id = context.params.id
// not sure what to do here
}
const Items = () => {
return (
<div>
this is items
</div>
);
}
export default Items;
for getStaticPaths
export const getStaticPaths = async () => {
const { data } = await client.query({
query: launchesPastQuery, // this will query the id only
});
return {
paths: data.CHANGE_THIS.map((param) => ({
params: { id: param.id },
})),
fallback: false,
};
};
CHANGE_THIS is the Query Type that follows data in the JSON response.
for getStaticProps
export const getStaticProps = async ({
params,
}) => {
const { data } = await client.query({
query: GetLaunchPastByID ,
variables: { LaunchID: params.id, idType: "UUID" }, // the idType is optional, and the LaunchID is what you'll use for querying by it*
});
return {
props: {
launchesPast: data.CHANGE_THIS,
},
};
The launchPastQueryByID is like:
const GetLaunchPastByID = gql`
query LaunchPastByID($LaunchID: UUID!) { // UUID is id type
CHANGE_THIS(id: $LaunchID) {
id
//...
}
}
`;
sorry for not giving you the correct queries, spacex.land is currently down.
I am newbie with graphql. I have a front-end project (nextjs) and back-end(strapi).
This is my code
import { ApolloClient, InMemoryCache, gql } from '#apollo/client'
export default function Blog({ posts }) {
console.log('posts', posts)
return (
<div>
{posts.map(post => {
return (
<div>
<p>{posts.heading}</p>
</div>
)
})}
</div>
)
}
export async function getStaticProps() {
const client = new ApolloClient({
url: 'http://localhost:1337/graphql/',
cache: new InMemoryCache(),
})
const { data } = await client.query({
query: gql`
query {
posts {
data {
attributes {
heading
}
}
}
}
`,
})
return {
props: {
posts: data.posts,
},
}
}
alongside this, I also get this message "cannot destructure property of intermediate value". Does anybody know why, i'm sure the code is correct.
Firstly i recommend creating a folder in root and put all graphql codes and you have also a syntax error:
let me give you an example:
/index.js
import { ApolloClient, InMemoryCache, gql } from "#apollo/client";
const client = new ApolloClient({
uri: API,
cache: new InMemoryCache(),
defaultOptions: {
query: {
fetchPolicy: "network-only",
},
},
});
//get category ids
const getCatIds = async () => {
try {
const { data } = await client.query({
query: gql`
query categoriesId {
categories(sort: "id:ASC") {
id
name
}
}
`,
});
return data.categories;
} catch {
console.log("error appolo");
}
};
export {getCatIds}
I am using Gatsby.
I need to increase the limit of 3 to each onCLick.
I've tried to follow this post, but with no success, so I removed the edits, and here is the original code...
This will help me to load more posts.
export const LatestNews = ({data}) => {
console.log(data);
return (
<section id="news_posts_section">
<p>some data</p>
</section>
);
};
export const latestNewsQuery = graphql`
query myquery{
allMarkdownRemark(
filter: { frontmatter: { layout: { eq: "news" } } }
sort: { fields: frontmatter___date, order: DESC }
limit: 2
) {
nodes {
frontmatter {
layout
title
path
date
featuredImage
thumbnail
}
excerpt(pruneLength: 325)
}
}
}
`;
Here is my gatsby-node:
exports.createPages = ({ actions, graphql }) => {
const { createPage } = actions;
const blogPostTemplate = path.resolve('src/templates/blog-post/BlogPost.js');
const newsTemplate = path.resolve('src/templates/news-single/NewsSingle.js');
const latestNewsPage = path.resolve(
'src/components/pages-implementation/news/sections/LatestNews.js',
);
const blog = graphql(`
{
blog: allMarkdownRemark(
filter: { frontmatter: { layout: { eq: "blog" } } }
) {
edges {
node {
frontmatter {
path
}
}
}
}
}
`).then((result) => {
if (result.errors) {
result.errors.forEach((e) => console.error(e.toString()));
return Promise.reject(result.errors);
}
const posts = result.data.blog.edges;
posts.forEach((edge) => {
const { path } = edge.node.frontmatter;
createPage({
path: path,
component: blogPostTemplate,
context: {},
});
});
});
const news = graphql(`
{
news: allMarkdownRemark(
filter: { frontmatter: { layout: { eq: "news" } } }
) {
edges {
node {
frontmatter {
path
}
}
}
}
}
`).then((result) => {
if (result.errors) {
result.errors.forEach((e) => console.error(e.toString()));
return Promise.reject(result.errors);
}
const news = result.data.news.edges;
news.forEach((edge) => {
const { path } = edge.node.frontmatter;
createPage({
path: path,
component: newsTemplate,
context: {},
});
});
news.forEach(edge => {
createPage({
path: `/news/`,
component: latestNewsPage,
context: {
// This time the entire product is passed down as context
product: edge
}
});
});
});
return Promise.all([blog, news]);
};
Edit 21 November:
I replaced the above code with my attempt to use a non-static query
I added the gatsby-node config
I give here a little explanation: the BlogPost.js and NewsSingle.js are templates that create new pages for each post or news post (from Netlify CMS)
The LatestNews.js is a component in a page. This is the main page of the news. Where are shown all the news? With a static query, it works fine, however, I need to make the "limit" a variable in order to apply a load more button, that onClick will increase the limit, thus showing more news posts on the loop.
With the above configuration it says:
warning The GraphQL query in the non-page component "/home/user/projectname/src/components/pages-implementation/news/sections/LatestNews.js" will not be run.
Exported queries are only executed for Page components. It's possible you're
trying to create pages in your gatsby-node.js and that's failing for some
reason.
useStaticQuery (hence the name) does not allow to receive variables. If you take a look at the docs:
useStaticQuery does not accept variables (hence the name “static”),
but can be used in any component, including pages
The only way to pass variables in a GraphQL query is by using the context API in the gatsby-node.js. For example:
queryResults.data.allProducts.nodes.forEach(node => {
createPage({
path: `/products/${node.id}`,
component: productTemplate,
context: {
// This time the entire product is passed down as context
product: node
}
});
});
};
In the snippet above, will be a product variable in the context with the whole node. It can be accessed through pageContext prop in the destination template or used as a query parameter.
i'm writing a small search app which starts a search over a RESTapi. Until the search is finished the api respond with a 260 status code or a 250 if the search is finished.
For this purpose i've written a component with a Query component which polls the RESTapi:
import React from 'react';
import gql from "graphql-tag";
import { Query } from "react-apollo";
export const GET_SEARCH_RESULTS = gql`
query GetSearchResults($sid: String!) {
getSearchResults(sid: $sid) {
status,
clusteredOffers {
id,
bookingId,
totalPrice
}
}
}
`;
export default class extends React.PureComponent {
client = null;
updateStatus = (data) => {
this.client.writeData({ data: { currentSearch: { status:
data.getSearchResults.status, __typename: 'CurrentSearch' }} })
};
render() {
const { sid } = this.props;
return (
<Query
query={GET_SEARCH_RESULTS}
variables={{ sid }}
pollInterval={500}
onCompleted={this.updateStatus}
>
{({ loading, error, data, stopPolling, client }) => {
this.client = client;
if (loading) return 'Loading...';
if (error) return `Error! ${error.message}`;
if(data.getSearchResults.status &&
data.getSearchResults.status !== "260") stopPolling();
return (
<div>
{data.getSearchResults.clusteredOffers.map(offer => (
<div key={offer.id} >
<div>{offer.id}</div>
<div>{offer.bookingId}</div>
<div>{offer.totalPrice}</div>
</div>
))}
</div>
)
}}
</Query>
)
}
}
In a parent component i also need the status of the current search. So i've added a clientState to my ApolloClient to write the remote data to the client cache:
import ApolloClient from "apollo-boost";
import Search from './components/Search';
const client = new ApolloClient({
uri: "http://localhost:4000/graphql",
clientState: {
defaults: {
currentSearch: {
__typename: 'CurrentSearch',
status: "0"
}
},
}
});
....
const GET_CURRENT_SEARCH_STATE = gql`
{
currentSearch #client {
status
}
}
`;
As u can see i'm using onCompleted inside my polling query component to write the current status to the local cache.
Does it make sense or should i read the status from the cached remote data? If so, how i get the cached remote data?
thank you!
Hi I've been trying to learn vuejs and vuex while trying to get response from an api call with vuex concept I got the following error.Please help.
This error occurred
Error TypeError: Cannot read property 'dispatch' of undefined
at app.js:12012
loginAction.js
export const getUsersList = function (store) {
let url = '/Apis/allUsers';
Vue.http.get(url).then((response) => {
store.dispatch('GET_USER_RES', response.data);
if (response.status == 200) {
}
}).catch((response) => {
console.log('Error', response)
})
}
loginStore.js
const state = {
userResponse: []
}
const mutations = {
GET_USER_RES (state, userResponse) {
state.userResponse = userResponse;
}
}
export default {
state, mutations
}
login.vue
import {getUsersList} from './loginAction';
export default {
created () {
try{
getUsersList();
}catch(e){
console.log(e);
}
},
vuex: {
getters: {
getUsersList: state => state.userResponse
},
actions: {
getUsersList
}
}
}
</ script>
If you call the actions manually (like in your try/catch) they'll not get the store context as the first argument. You could use getUsersList(this.store) I think, but instead I would use dispatch to reach all your actions. (I edited just a little bit to get a minimal running example, but I think you get the point!)
new Vue({
render: h => h(App),
created() {
this.$store.dispatch('getUsersList');
},
store: new Vuex.Store({
getters: {
getUsersList: state => state.userResponse
},
actions: {
getUsersList
}
})
}).$mount("#app");
Also, use commit to reach the mutations instead of dispatch. ie:
export const getUsersList = function ({commit}) {
let url = '/Apis/allUsers';
Vue.http.get(url).then((response) => {
commit('GET_USER_RES', response.data); // because GET_USER_RES is a mutation
...