Problems accessing data from a GraphQL query in Javascript with Svelte - javascript

Below I have my working function using a normal REST response that I want to try to convert into a GraphQL version, it fetches a JSON doc from my Phoenix Server and stores the object values from the JSON doc into an object. The problem is, here I can use await and then assign the new object values from the object within the JSON document, but using GraphQL I cannot access this data to assign it because there is no await function as its just a Query. (From what I know)
async function updatePageWithNewCompany(company){
const res = await fetch(`http://localhost:4000/${company}`);
profile = await res.json();
profile = profile.companyProfile
DashboardStore.update(currentData => {
return {
id: profile.id,
companyName: `${profile.company_name}`,
averageLength: profile.average_length,
}
})
Ultimately I am asking if there is a way to access and assign data from a GraphQL query in JavaScript so I can manipulate it before displaying it in my frontend Svelte app.
Example of current GraphQL query:
import { gql } from '#apollo/client'
import { client } from './apollo';
import { query, setClient } from "svelte-apollo";
setClient(client)
const GET_COMPANY = gql`
query{
companyProfile(){
companyName
averageLength
}
}
`;
const response = query(GET_COMPANY)
...

svelte-apollo queries return
a svelte store of promises that resolve as values come in
as stated in the example query displayed in the documentation.
As such, you can exploit that format directly in the script section. Here is an example:
...
async function getCompany() {
const companyStore = query(GET_COMPANY)
const company = (await $companyStore).data.companyProfile
return company // format will be an object with keys { companyName, averageLength } according to your query
}
...
On a side-note I would recommend always getting the id of objects in your GraphQL queries as it is usually the key used internally by the Apollo client to manage its cache (unless explicitly stated otherwise).

I have found a working solution with the help of #Thomas-Hennes
import { client } from '../apollo.js';
import { onMount } from "svelte";
import { gql } from '#apollo/client'
export const COMPANY_LIST = gql`
query {
listCompanies{
companyName
}
}
`;
async function listCompanies() {
await client.query({query: COMPANY_LIST})
.then(res => {
companyList.update( currentData =>
[...res.data.listCompanies.companyName])})
};
onMount(() =>{
listCompanies()
})
await didn't like being assigned to a variable or having its data manipulated so i used it as a promise instead and manipulated the response.
The below link helped me find the final piece of the puzzle.
https://docs.realm.io/sync/graphql-web-access/using-the-graphql-client

Related

How to use React hook that returns circular JSON as a dependent query for multiple React Query calls?

I have a library that exposes a React hook - usePlayground. This can only be called within the context of a function, and, when called, returns and object like this:
// object with circular references
const playground = {
getUserDetails: () => new Promise({userId: '1234'}),
apiKey: '1234',
}
I have a component, UserDetails that looks like this.
UserDetails.jsx
import {usePlayground} from 'my-providers';
function useUserData() {
const playground = usePlayground();
const userData = fetchUserData(playground);
return [{
apiKey: userData.apiKey,
userId: userData.userId
}]
}
function UserDetails() {
const [{userId, apiKey}] = useUserData();
const [{licenses}] = useLicenses(userId, apiKey);
const [{todos}] = useTodos(userId, apiKey)
return <div></div>
}
I have a react query service that should calculate the user ID and apiKey to pass to the consumer. I get a JSON circular reference detected from my devtools. I've also read that you aren't supposed to use an object like this as an argument to React query.
user-service.js
async function fetchUser(playground) {
const userDetails = await playground.getUserDetails()
return {
apiKey: userDetails.apiKey,
userId: userDetails.userId
};
}
// this throws an error because playground object has circular references
export async function fetchUserData(playground) {
return useQuery(['user', playground], fetchUser);
}
I would like to compute the apiKey and the userId once and access automatically in all of my react query hooks using dependent queries, but I am gettin the "JSON circular reference" error. How can I compute apiKey and userId once and use repeatedly in React query calls?

Using Apollo's useQuery to return a value, not render

Apologies as I'm fairly new to React and couldn't find the answer when searching but accept I might be using this wrong.
I have several pages that utilise useQuery from Apollo and when "loading" variable is false it can then process the data and render which is easy enough.
However, I want to create a new file called GetCustomerID.jsx and have an arrow function which, when supplied with a user ID, will run a gql query and return the customer ID value. Not render components, just return the value.
The trouble is that once this is called from within another page it always just returns "loading"
const myQuery = gql`
query GetCustomerID($id: ID!) {
user(id: $id) {
data {
id
attributes {
customer {
data {
id
}
}
}
}
}
}
`;
const GetCustomerID = () => {
const [userId, setUserID] = useState(localStorage.getItem("userid"));
const [custId, setCustId] = useState();
const { loading, error, data } = useQuery(myQuery, {
variables: {
id: userId
}
});
if(loading){
return "loading"
}
if (error) {
console.log(error);
return "error";
}
setCustId(data.attributes.customer.id);
console.log(custId);
return custId;
};
Call from elsewhere:
const [custId, setCustId] = useState();
let myVal = GetCustomerID();
setCustId(myVal)
Any help would be much appreciated and if I'm fundamentally misunderstanding useQuery for what I'm trying to achieve then apologies
try to define custom hook, like
useCustomerId
inside this hook you can call useQuery or useLazyQuery and return result of the query

"query is not a function" Next Js getServerSideprops and firebase error

I'm using NextJS and firebase as my primary database for the app that I'm currently building for an NGO. and I ran into an issue.
import {
collection,
where,
query,
getDocs
} from '#firebase/firestore';
import { db } from '../../../services/firebase';
export async function getServerSideProps({query}) {
const user = await getDocs(query(collection(db, 'members'), where('id', '==', query)))
return {
props: {
// VisionInfo: JSON.stringify(user.docs.map(item => item.data()))
json: JSON.stringify('Hello')
}
};
}
The only way to get Query from the URL in NextJS in serverSideProps is to use the keyword "query" but the same keyword is used to fetch firebase document.
The error shows "query is not a function"
Is there anyway I could get Query into serversideprops ?
The issue arises because you also have "query" in getServerSideProps parameters. Try naming the import from Firestore SDK (or the query param) as shown below:
import { query as fireQuery } from '#firebase/firestore';
export async function getServerSideProps({query}) {
const user = await getDocs(fireQuery(collection(db, 'members'), where('id', '==', query)))
// use fireQuery here ^^^
})

Firestore Function DocumentReference.update() called with invalid data. Unsupported field value: a custom object

I'm following Firebase's instructions and my functions is as follows:
import { DataSource, DataSourceConfig } from "apollo-datasource";
import { KeyValueCache } from "apollo-server-caching";
import firebase from "firebase";
import admin from "firebase-admin";
import "#firebase/firestore";
import { Request } from "apollo-server-env";
export class FirebaseDataSource<TContext = any> extends DataSource {
context!: TContext;
db: firebase.firestore.Firestore;
constructor({
firebaseConfig,
serviceAccount,
databaseURL,
}: {
serviceAccount: any;
firebaseConfig: firebase.app.App;
databaseURL: string;
}) {
super();
this.context;
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
}
if (!admin.apps.length) {
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL,
});
}
if (!this.db) {
this.db = firebase.firestore();
}
}
async initialize(config: DataSourceConfig<TContext & { request: Request }>) {
this.context = config.context;
}
async user_updateRestaurantFavorites(data: {
uid: string;
someId: string;
add: boolean;
}) {
const collectionRef = this.db.collection("users");
const documentRef = collectionRef.doc(data.uid);
let favorites;
if (data.add) {
favorites = await documentRef.update({
favorites: admin.firestore.FieldValue.arrayUnion(
data.someId
),
});
} else {
favorites = await documentRef.update({
favorites: admin.firestore.FieldValue.arrayRemove(
data.someId
),
});
}
return favorites;
}
}
export default FirebaseDataSource;
I dubugged it and I do pass the uid, add, and someId correctly.
someId is a string and add is a boolean (true)
When I run this, I get:
Firestore Function DocumentReference.update() called with invalid data. Unsupported field value: a custom object (found in field favorites in document users/XXXXXXX)
I am just running their own function with a simple string.
Below is an image of my firestore showing the user record does indeed have an empty array ready to accept strings
What am I doing wrong?
You're mixing up the web client and admin client SDKs. You can't use the FieldValue objects exported by firebase-admin when calling methods exported by firebase. The error message is coming from the web client SDK, and it's effectively telling you that you passed an object that it doesn't understand (from the Admin SDK).
You should pick one or the other, and completely remove the one you aren't using in order to avoid problems. If this runs on a backend, you should only use the Firebase Admin SDK, and skip the web client altogether. If you do this, you will need to assign this.db using the admin SDK, probably as this.db = admin.firestore().
Firebase can only store primitive types, maps and array of same. In your case you are saving the result of admin.firestore.FieldValue.arrayUnion(...) for the property favorites.
My guess is that the result is not returning a supported type. I have not used FieldValue before ... is that the correct way to use the API?
It simply means that you need to send exact data which was received by the query. Partial object not allowed
db.collection("users").where("name", "==", somename).limit(1).get().then(query => {
console.log(query);
const thing = query.docs[0];
console.log(thing.data());
let tmp = thing.data();
tmp.current_game_play = tmp.current_game_play + 1;
console.log(tmp);
thing.ref.update(tmp);
});

How to use AsyncData with Promise.all to get data from multiple api's works client side but causes nginx to 504

I'm currently converting a vue application to use the NUXT framework. To add support for SSR to my application and have come across the following issue when trying to use asyncData with multiple data sources on a single page.
I'm trying to setup asyncData to get data from 2 separate data sources that are required for the page to work. Now the code works on the client-side when the Promise.all resolves it gets both sets of data. However, on the server-side the promises when console.log the promises are both pending and causes Nginx to timeout and give a 504 bad gateway error.
I have tried to get this to work use async/await and promise.all with no avail. see code samples for both below.
Import functions getData and getJsonFile are both using Axios and returning resolved promises with objects of data.
// Using async/await
export default {
async asyncData(context) {
const nameData = await getData('getInformationByNames', {
names: [context.params.name],
referingPage: `https://local.test.com${context.route.fullPath}`
});
const content = await getJsonFile(
`/data/pages/user/${context.params.id}`
);
return {
names: nameData,
content
};
}
}
// Using Promise.all
export default {
async asyncData(context) {
const [nameData, content] = await Promise.all([
getData('getInformationByNames', {
names: [context.params.name],
referingPage: `https://local.test.com${context.route.fullPath}`
}),
getJsonFile(`/data/pages/user/${context.params.id}`)
]);
return {
names: nameData,
content
};
}
}
// getJsonFile
import axios from 'axios';
import replaceStringTokens from '#/scripts/helpers/replaceStringTokens';
export default function getJsonFile(path, redirect = true) {
const jsonFilePath = `${path}.json`;
return axios.get(jsonFilePath).then((response) => {
if (typeof response.data === 'object') {
return replaceStringTokens(response.data);
}
return false;
});
}
// getData
import axios from 'axios';
import getUserDevice from '#/scripts/helpers/getUserDevice';
// require php-serialize node package to serialize the data like PHP would for the api endpoint.
const Serialize = require('php-serialize');
export default function getData(action, data) {
const dataApiAddress = '/api/getData.php';
const dataToPass = data || {};
// all actions available on the api will need to know the users device so add it to the data.
dataToPass.userDevice = getUserDevice();
// package the data like the api expects to receive it
const serializedAndEncodedData = encodeURIComponent(
Serialize.serialize(dataToPass)
);
const axiosParams = {
action,
data: serializedAndEncodedData
};
return axios
.get(dataApiAddress, {
params: axiosParams
})
.then((response) => {
return response.data.data;
})
.catch((error) => {
console.log(error);
return false;
});
}
I would expect that the promises resolve and both sets of data are returned an available for the page to use on both the client and server-side.
Is there a better way to get multiple sets of data or a way to debug the server-side so that I can see what is causing the promises to not resolve on the server?
Fixed the issue the problem was with some discrepancies in the data being queried on the API. The data in the database was using an uppercase letter at the start that must have been input incorrectly. So this was causing the promise to not resolve due to the API sending a query for the lowercase version and in turn causing Nginx to timeout.

Categories

Resources