How can I get data from a json via GraphQL? - javascript

enter code here {
"compdata": [
{
"id": 1,
"title": "FlexBox",
},
{
"id": 2,
"title": "Grid layout",
},
]
}
enter code here **file in:-- src-data-data.json**
enter code here export const IndexQuery = graphql`
query IndexQuery {
dataJson {
compdata {
id
example
}
}
}
`
Blockquote giving me error Cannot read properties of undefined (reading 'compdata')

You only need to follow the docs about Sourcing from JSON.
That said, you don't need to use GraphQL when sourcing from a JSON local file, you can just import the object such as:
import React from "react"
import JSONData from "../../content/My-JSON-Content.json"
const SomePage = () => (
<div style={{ maxWidth: `960px`, margin: `1.45rem` }}>
<ul>
{JSONData.compdata.map((data, index) => {
return <li key={`content_item_${index}`}>{data.title}</li>
})}
</ul>
</div>
)
export default SomePage
In this case, you can import your JSON data as JSONData and loop it through the array of compdata.
However, if you still want to use GraphQL, you will need to use the gatsby-transformer-json plugin, what will create a queryable GraphQL node from your JSON source:
Install it by:
npm install gatsby-transformer-json
And use it your gatsby-config.js:
module.exports = {
plugins: [
`gatsby-transformer-json`,
{
resolve: `gatsby-source-filesystem`,
options: {
path: `./src/your/json/folder`,
},
},
],
}
The alias of the node will rely on your folder structure and your naming (which hasn't been provided), in the docs example, given a letters.json such as:
[{ "value": "a" }, { "value": "b" }, { "value": "c" }]
So in your GraphiQL playground (localhost:8000/___graphql) you will be able
to query allLettersJson node:
{
allLettersJson {
edges {
node {
value
}
}
}
}
You can add a typeName option to fix your naming such as:
{
resolve: `gatsby-transformer-json`,
options: {
typeName: `Json`,
},
},
In that case, the created node will be allJson.

Related

Strapi: Get all nested properties for deeply nested relation

I recently started working with strapi and have been figuring out how to go about content relations and so on... Now I have reached a point when I have multiple content relations dependent on each other.
This is my strucute:
Collection Types:
Categories
Articles
with content relation: Article has one category
Single Types:
Homepage
with content relation: Homepage has many articles
Now what I want to do is to to get all nested properties of a category for articles that are assigned to homepage just by simply making a GET request from /homepage
What I currently get is a json structure like this:
{
"id": 1,
"hero": {
....
},
"featuredArticles": {
....
},
"displayedArticles": [
{
"id": 2,
"category": 5,
}
]
}
What is the expected output:
{
"id": 1,
"hero": {
....
},
"featuredArticles": {
....
},
"displayedArticles": [
{
"id": 2,
"category": [
{
"id": 5,
"title": "Foundation"
}
],
}
]
}
My suspicion is that the properties of categories is basically too nested when trying to fetching from /homepage rather than /articles directly.
I found out that handling this could work with modifying the controllers right in the strapi directory but I haven't figured it out quite.
Strapi Controller Docs
Is here anybody who knows solution to this?
Firstly you'll need a custom controller function for this. In /api/homepage/controllers/homepage.js you can export your custom find function.
There you can define which fields you want to populate:
module.exports = {
find: ctx => {
return strapi.query('homepage').find(ctx.query, [
{
path: 'displayedArticles',
populate: {
path: 'category',
},
},
]);
}
};
For reference see the most recent beta docs:
Customization
Second way: populate it as per requirement
module.exports = {
find: async (ctx) => {
const homePage = await strapi.query('homepage').find(ctx.query);
const categories = await strapi.query('categories').find();
if (homePage[0].displayedArticles) {
homePage[0].displayedArticles.forEach(async (content) => {
if(categories){
content.category = categories.find((category) => {
return category.id === content.category;
});
}
});
}
if (homePage[0].displayedComponents) {
homePage[0].displayedComponents.forEach(async (content) => {
if(categories){
content.category = categories.find((category) => {
return category.id === content.category;
});
}
});
}
return homePage;
}
};

Objects are not valid as a React child - is my JSON data bad?

I am aware this has been asked several times on Stack Overflow, but other solutions don't seem to work for me.
I am trying to access the notifications in this JSON file.
My JSON file: https://dinkyapp.000webhostapp.com/db.json
Is my JSON file poorly structured? I know it's pretty big. In the world outside of React, the JSON file works fine. But in React, it's unworkable, no matter if I parse, convert to arrays etc. Should I make multiple smaller files instead?
In my example, I'm trying to access the notifications and return them in the JSX.
I get the error, 'Objects are not valid as a React child'.
I also get notifs.map is not a function. Presumably this is because the data being pulled through is not in an array format.
My component code:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const GetData = () => {
let [notifs, setNotifs] = useState([]);
useEffect(() => {
axios.get('db.json').then((res) => {
var data = res.data.clients.clientID_1.notifications;
setNotifs(data);
});
}, []);
const getIDs =
notifs.map(item => {
return (
<p key={notifs.reqID}>{notifs.text}</p>
)
})
return <div>{getIDs}</div>;
};
export default GetData;
I'm nearly about to switch back to Vanilla JS as I have tried so many ways. But I think it may be my JSON data being poor. If anyone could please advise?
Many thanks
that's because you are trying to map over objects, for simplify your JSON could be
"notifications": [
{
"user_id": 1 // move user id here
"timestamp": 1613777053000,
"reqID": 100012,
"seen": true,
"text": "Aaron F accepted your shift swap"
},
{
"user_id": 2, // move user id here
"timestamp": 1613777053000,
"reqID": 100012,
"seen": true,
"text": "Aaron F accepted your shift swap"
}]
then you now map safely
If look at https://dinkyapp.000webhostapp.com/db.json res.data.clients.clientID_1.notifications it is object like:
const notifs = {
userID_1: [
{
timestamp: 1613777053000,
reqID: 100012,
seen: true,
text: 'Aaron F accepted your shift swap',
},
],
userID_2: [
{
timestamp: 1614676200000,
reqID: 199290,
seen: true,
text: 'Sean W approved your Annual Leave request',
},
{
timestamp: 1610719942000,
reqID: 184828,
seen: true,
text: 'Sean W approved your Annual Leave request',
},
],
};
Not array, so you need work with it as an object, example:
const getIDs = Object.keys(notifs).map(key => {
return (
<p key={key}>
From {key}
{notifs[key].map(n => (
<p key={n.reqID}>{n.text}</p>
))}
</p>
);
});
Try changing it to this:
<p key={item.reqID}>{item.text}</p>

Introspection Query is invalid for buildClientSchema

I am trying to convert from an introspection query to a GraphQL schema using the npm GraphQL library.
It keeps stating:
devAssert.mjs:7 Uncaught Error: Invalid or incomplete introspection result. Ensure that you are passing "data" property of introspection response and no "errors" was returned alongside: { ... }
Issue is I am getting it directly from GraphiQL Shopify and can't figure out how to validate my introspection return is correct.
Code:
var introspection = `
{
"data": {
"__schema": {
"types": [
{
"name": "Boolean"
},
{
"name": "String"
},
{
"name": "QueryRoot"
},
{
"name": "Job"
},
{
"name": "ID"
},
{
"name": "Node"
},
{
"name": "Order"
}
]
}
},
"extensions": {
"cost": {
"requestedQueryCost": 2,
"actualQueryCost": 2,
"throttleStatus": {
"maximumAvailable": 1000,
"currentlyAvailable": 980,
"restoreRate": 50
}
}
}
}`;
let schema = buildClientSchema(introspection);
//console.log(schema.printSchema());
I thought the introspection result could be a string? Or is there not enough info to build a schema? Do I need to expand the number of fields? What's the bare minimum needed to exchange an introspection result into a schema?
You should use getIntrospectionQuery to get the complete introspection query needed. If the response you're working with is a string, it should then be parsed to an object before being passed to buildClientSchema -- the function accepts an object, not a string.
Here's an example directly querying a schema using the graphql function -- you'll need to modify it based on how you're actually executing the query.
const { getIntrospectionQuery, buildClientSchema, graphql } = require('graphql')
const schema = new GraphQLSchema(...)
const source = getIntrospectionQuery()
const { data } = await graphql({ source, schema })
const clientSchema = buildClientSchema(data)
Make sure that you are only passing in the data portion of the response. The object you pass in should look like this:
{
__schema: {
// ...more properties
}
}

Vuex doesn't react with complex object

I've started to use Vuex in order to remplace the EventBus, because the data in my app has started to get a little bit complex.
In my context, I have a question entity, with multiple answers, when the user insert another answer I want to show the last one; (here I use two different components: one to show the answers and other to answer the question) but when the server response OK with the new answer, and the mutation change the state.answers, the computed property doesn't react and doesn't show the new answer:
Here is my data structure:
"answers": {
"118": {
"id": 118,
"description": "objective",
"created_at": "2019-11-12T19:12:36.015Z",
"dojo_process_id": 1,
"question_id": 1,
"user_id": 10
}
"127": {
"id": 127,
"description": "asdddd",
"created_at": "2019-11-12T19:38:19.233Z",
"dojo_process_id": 1,
"question_id": 1,
"user_id": 10
},
"128": {
"id": 128,
"description": "asddddasddd",
"created_at": "2019-11-12T20:00:17.572Z",
"dojo_process_id": 1,
"question_id": 1,
"user_id": 10
}
},
Here is the code for my store:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
...
answers: {},
...
},
getters: {
findBy: state=> filter => {
let result= Object.values(state[filter.model]).
filter(data => data[filter.field] === filter.criteria);
return result;
}
},
mutations: {
setAnswers(state, answers) {
state.answers = answers;
},
setAnswer(state, answer) {
state.answers[answer.id] = answer;
},
},
actions: {
replaceCompleteProcess(context, data) {
...
context.commit('setAnswers',data.answers);
...
},
cleanCompleteProcess(context) {
...
context.commit('setAnswers',{});
...
},
saveAnswer(context, answer) {
context.commit('setAnswer', answer);
}
}
});
And this is how the script of my component is structured:
export default {
name: "show-question",
computed: {
question: function () {
return this.$store.getters.question(this.questionId)
},
answers: function () {
return this.$store.getters.findBy({
model: 'answers',
field: 'question_id',
criteria: this.questionId,
sort: true
});
},
answer: function () {
if (this.answers && this.answers.length > 0) {
return this.answers[0].description;
} else {
return '';
}
}
},
props: {
questionId: {
type: [Number],
default: 0
}
},
data() {
return {
sending: false,
answerData: this.answer
}
},
methods: {
sendAnswer () {
this.sending = true;
questionConnector.answerQuestion(this,this.question.id,this.dojoProcessId, this.answerData)
},
// this one is called from AXIOS
answerWasOK(answer) {
this.sending = false;
this.$store.dispatch('saveAnswer', answer);
this.answerData = '';
}
}
}
So, if I understand how to use Vuex, when I call this.$store.dispatch('saveAnswer', answer), the state will be updated, and the computed property answers would be updated, and I'll be able to show the new changes in the component, But it doesn't work.... the computed property just doesn't react.
I had read a lot about vuex and how "it not work well" with complex data, so I normalize my data. but it is the same... also I tried to use vuex-orm, but I have a lot of problems with the one-many relation, and I cant do it work.
EDIT: Solution
I did a small test with the ideas from the answers and it works
setAnswer(state, answer) {
let newAnswers = state.answers;
state.answers = {};
newAnswers[answer.id] = answer;
state.answers = newAnswers;
}
When you are working with Objects you have to do it like this
setAnswer(state, answer) {
Vue.set(state.answers, answer.id, answer);
},
This is clearly mentioned in the documentation.
When adding new properties to an Object, you should either:
Use Vue.set(obj, 'newProp', 123), or
Replace that Object with a fresh one. For example, using the object spread syntax we can write it like this:
state.obj = { ...state.obj, newProp: 123 }
You are storing a list of answers inside an object. Nothing wrong with that, since you know how to deal with. It turns out that Vue.js observers don't track new object attributes (which is exactly what you are doing there, creating new attributes, instead of modifying a list/array).
My first suggestion is to change this object to an array. But if you can't, due to your ORM or other reason of your project standard, you should take a look about Reactivity of Vue.js. The quickest solution, in my opinion, is to use a watcher:
https://v2.vuejs.org/v2/api/#watch
Some links that can be helpful to understand Vue.js reactivity:
Reactivity in Depth
https://v2.vuejs.org/v2/guide/reactivity.html
How to actively track an object property change?
https://forum.vuejs.org/t/how-to-actively-track-an-object-property-change/34402/1

trying to connect data pulled in from airtable into gatsby graphql to algolia, plugin not working

https://github.com/algolia/gatsby-plugin-algolia
this plugin doesn't seem to be working in my gatsby-config when i run a build (doesn't populate my algolia index) -- i've already pushed data into my index using algoliasearch and a json file, but i want this to be automatically hooked up whenever i build -- so the data is always current with my airtable data.
i've tried the 'gatsby-plugin-algolia' approach via the documentation on github (placed in my gatsby-config.js file)
const myQuery = `{
allSitePage {
edges {
node {
# try to find a unique id for each node
# if this field is absent, it's going to
# be inserted by Algolia automatically
# and will be less simple to update etc.
objectID: id
component
path
componentChunkName
jsonName
internal {
type
contentDigest
owner
}
}
}
}
}`;
const queries = [
{
query: myQuery,
transformer: ({ data }) => data.allSitePage.edges.map(({ node }) => node),
indexName: 'cardDemo',
},
];
module.exports = {
plugins: [
{
resolve: 'gatsby-source-airtable-linked',
options: {
apiKey: process.env.MY_API_KEY,
tables: [
{
baseId: process.env.MY_BASE_ID,
tableName: 'Streams',
tableView: 'DO NOT MODIFY',
},
],
},
},
{
resolve: 'gatsby-plugin-algolia',
options: {
appId: process.env.MY_AGOLIA_APP_ID,
apiKey: process.env.MY_AGOLIA_API_KEY,
indexName: 'cardDemo',
queries,
chunkSize: 1000000,
},
},
],
};
i've also subbed out the 'myQuery' for a more specific instance that i'm using on a component via airtable, shown below
const myQuery = `{
items: allAirtableLinked(
filter: {
table: { eq: "Items" }
}
) {
edges {
node {
id
data {
title
thumbnail_url
thumbnail_alt_text
url_slug
uberflip_stream_id
uberflip_id
}
}
}
}
}`;
if anyone has this plugin running and working -- i could definitely use some hints as to how to get this working (not much documentation on this package)
thank you!
figured it out! anyone running into this same issue, do these steps:
check that you have the proper API key
check that the transformer method changes to match the object queried in graphql. mine had to change to this:
transformer: ({ data }) => data.items.edges.map(({ node }) => node)
check that your query works in graphql, make sure it's syntactically correct, and is pulling the correct data. the query i used was
const pageQuery = `query {
items: allAirtableLinked(
filter: {
table: { eq: "Items" }
data: { hubs: { eq: "cf4ao8fjzn4xsRrD" } }
}
) {
edges {
node {
id
data {
title
thumbnail_url
thumbnail_alt_text
duration
url_slug
asset_type
uberflip_stream_id
uberflip_id
}
}
}
}
}`;
lastly, it's cleaner if you abstract the query and queries into a ultil directory housed in src, then require that into the config file so it's cleaner :
i got this idea from this repo, very helpful! check out this example

Categories

Resources