How to access Apollo Result Object within VueJs3 - graphql

I'm brand new to using Vue-Apollo, and I'm having some confusion with how to access the retuerned data within Apollos result object within my Vue Template.
VueComponent.js
const store = useCaseFileStore();
import gql from "graphql-tag";
import { useQuery } from "#vue/apollo-composable";
const FILE_QUERY = gql`
query File($term: String!) {
lookupFile(term: $term) {
id
author
city
state
}
}
`;
const term = "101707058976";
const { result, loading, error } = useQuery(FILE_QUERY, {
term,
})
const cities = console.log(result.cities)
;
However the final const returns undefined, despite seeing the data within Vue Dev Tools.

Related

Graphql-codegen nested fragment use inside a React component

The nested fragment regularUserResponseFragmentDocument
import { graphql } from '../../generated/graphql';
export const regularErrorFragmentDocument = graphql(`
fragment regularError on FieldError {
field
message
}
`);
export const regularUserFragmentDocument = graphql(`
fragment regularUser on User {
id
username
}
`);
export const regularUserResponseFragmentDocument = graphql(`
fragment regularUserResponse on UserResponse {
errors {
...regularError
}
user {
...regularUser
}
}
`);
is used in a component like so:
if (response.data?.login) {
const data = useFragment(regularUserResponseFragmentDocument, response.data.login);
const errorsData = useFragment(regularErrorFragmentDocument, data.errors);
if (errorsData) {
setErrors(toErrorMap([...errorsData]));
}
const userData = useFragment(regularUserFragmentDocument, data.user);
if (userData) {
await router.push('/');
}
}
Is there a way to avoid use of useFragment on the nested fragments?
No there is not; this is the proper way to get nested masked fragments.
You will find a similar example here where useFragment() was renamed to getFragmentData() (from codegen config) to avoid React hooks rules issues: https://github.com/charlypoly/codegen-repros/blob/master/client-preset-nested-fragments-interface/src/App.tsx

Apollo useQuery hook on component mount

I'm new to hooks and trying to use them more
How can I get data (with Apollo) when a component mount ?
I'm trying to use useQuery inside a useEffect, my code so far looks like this
const MyComponent = () => {
const getME = () => {
const { loading, error, data } = useQuery(ME);
setMe(data.me) // useState hook
console.log('query me: ', me);
};
useEffect(getME);
return (<>
...
</>)
}
but this gives me an error
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
edit: this is the query
import { gql } from '#apollo/client';
export const ME = gql`
query me {
profile {
firstName
lastName
}
}
`;
Here is an example on how you should use the useQuery hook and then stock the data in the state
const { loading, data, error } = useQuery(SOME_QUERY)
const [state, setState] = React.useState([])
useEffect(() => {
// do some checking here to ensure data exist
if (data) {
// mutate data if you need to
setState(data)
}
}, [data])`enter code here`
from https://github.com/trojanowski/react-apollo-hooks/issues/158

Apollo client fragments not embedding data

This is the first time I've ventured into fragments and I can't see where I'm screwing up, but it definitely isn't working! In GraphiQL it's working fine:
query Tasks($taskIds: [String]!) {
tasks(taskIds: $taskIds) {
...taskDisplay
}
}
fragment taskDisplay on Task {
_id
name
description
status
children {
_id
}
}
Here's what's in my client app:
import { gql } from "#apollo/client";
export const TASK_FRAGMENT = gql`
fragment taskDisplay on Task {
_id
name
description
status
children {
_id
}
}
`;
export const TASKS = gql`
query Tasks($taskIds: [String]!) {
tasks(taskIds: $taskIds) {
...taskDisplay
}
}
${TASK_FRAGMENT}
`;
So, the server returns the data correct as I can see in the Network tab of Chrome, but the data received by the useQuery result is an empty object. What gives?
Using #apollo/client#3.2.0-beta.2 (I have downgraded to 3.1.0 with same results)
EDIT:
Adding more info. My code is about as simple as it could be using a hook. Here's what's happening:
import { useQuery, gql } from "#apollo/client";
import { TASK_FRAGMENT } from "../pages/task/queries";
const ROOT_TASK_QUERY = gql`
query Project($projectId: String!) {
rootTask(projectId: $projectId) {
...taskDisplay
}
}
${TASK_FRAGMENT}
`;
const useProject = ({ variables }) => {
return useQuery(ROOT_TASK_QUERY, {
variables,
});
};
export default useProject;
And this is just logging the query itself:
Your returned data is missing the __typename field

DIalogflow-Fullfilment GraphQL

anyone here implemented Dialog flow fullfilment on graphql server? How do you handle it? Do you handle fulfillment as a mutation or you implement it as a separate rest endpoint?
I am able to expose my local server using ngrok but I am not sure how to go about setting up the fulfillment. I had separated my DF code from GraphQL code such that the DF module only exposes the methods that handle event and text queries to Dialog flow:
// df/index.js
module.exports={
text: ()=>{
self=module.exports
// ...
return self.getResult()
},
event: ()=>{
self=module.exports
// ...
return self.getResult()
},
getResult:()=>{
//...
return{
query,
response,
cards,
quickReply
}
}
}
Then this is passed through the graphQL context and exposed to the bot.resolver.js module where respective mutations for handling text and events are defined as shown
// schema/resolvers/bot.resolver.js
module.exports={
// Mutation
Mutation:{
sendText: (parent,args,context) => {
const {df}=context;
const response = df.text(args);
return response;
},
sendEvent: (parent,args,context) => {
const {df}=context;
const response = df.event(args);
return response;
},
},
};
The corresponding graphQL types are defined in bot.type.js as shown:
const { gql } = require('apollo-server-express');
module.exports=gql`
type Course {
id:ID
header:String!
image:String!
description:String
price:String!
}
type Option {
value:String!
payload:String
link:String
}
type QuickReply {
text:String!
options:[Option!]!
}
type Bot {
query:String!,
response:String!
cards:[Course!]
quickReply:QuickReply
}
type Mutation {
sendText(text: String!, userId:String!, parameters:String): Bot!
sendEvent(name: String!, userId:String!, parameters:String): Bot!
}
`;
Please advise where I can write the code below that sets up dialog flow fulfillment
dialogflow-fulfillment setup code
😊Surprisingly it was as simple as writing it as a middleware on my graphQl api.
// import the required dependencies
const express = require('express');
const bodyParser = require('body-parser')
const cors = require('cors');
const { ApolloServer, } = require('apollo-server-express');
// do not forget your graphQl schema definition
const schema = require('./schema');
// we shall also need to import the data source.
// I will assume an array for our data source defined as below
const models ={
Course:[
{id:1, name:'Chatbots',}
{id:2, name:'React',}
{id:3, name:'Express',}
{id:4, name:'GraphQl',}
],
Book:[
{id:1, title:'Fundermentals in Chatbots',courseId:1},
{id:2, title:'Express for Newbies',courseId:3},
{id:3, title:'Advanced AI on Bots',courseId:1},
{id:4, title:'GraphQl Simplified',courseId:4},
]
}
// Import the WebhookClient class
const { WebhookClient } = require('dialogflow-fulfillment');
// Do the graphQl gymnastics ... I find Apollo server 2 just on point.
const server = new ApolloServer(schema);
const path='/'
const port = process.env.PORT || 4000
const app = express(); // we will merge express with the Apollo server 2
// do the express gymnastics ...
app.use(path,cors(),bodyParser.json(),)
// **IT'S HERE THAT WE DEFINE DIALOG FLOW'S WEB-HOOK AS A MIDDLEWARE**
app.use('/webhook', async (request,response,next)=>{
const agent = new WebhookClient({ request, response });
const {parameters}=request.body.queryResult;
const course =parameters['course'];
// ... do the database logic here ...
// eg get the title of available text books for the particular course
// I will assume
const {id} = await models.Course.find(({name})=>name ===course)
const books = await model.Book.filter(({courseId})=>courseId===id)
const booksTitleArray = books.map(({title})=>title)
let titleList = booksTitle.Array.toString();
titleList.replace(',', ' , ') // put space btn comas
titleList.replace(/\,(?=[^,]*$)/, "and")
let intentMap = new Map();
const recommendBooks courses=>{
agent.add(`For ${course}, We use the following books: ${titleList}`);
};
intentMap.set('course.recommended.books',recommendBooks);
agent.handleRequest(intentMap);
next();
})
server.applyMiddleware({ app, path });
app.listen(port,()=>{
console.log( `Apollo Server Running on http://localhost:${port}`)
})
I feel like writing an article on this because I tried looking for help almost everywhere in vain. Incase I get the time to do so, I will provide it in the comments.😏😉🤔🤭
Guys, we should not forget the ngrok magic if we are testing from localhost 😁

Using writeFragment to Update a Field Belonging to an Object?

I'm trying to get my first writeFragment working.
Here's the object shape:
resolutions {
_id
name
completed
goals {
_id
name
completed
}
}
I've just run a mutation on the client that successfully adds a new goal, and now I'm trying to get the client page to auto-update and show the new goal that was just added.
I've got readFragment working. It reads in the Resolution successfully. I'm reading in the Resolution, rather than the goals, because as a field belonging to resolution, the goals don't have an id of their own.
Here's my update function, showing readFragment and writeFragment:
<Mutation
mutation={CREATE_GOAL}
update={(cache, { data: { createGoal } }) => {
let resId = 'Resolution:' + resolutionId;
const theRes = cache.readFragment({
id: resId,
fragment: GET_FRAGMENT_GOAL,
});
theRes.goals = theRes.goals.concat([createGoal]); //<== THIS WORKS
cache.writeFragment({
id: resId,
fragment: SET_FRAGMENT_GOAL,
data: { __typename: 'Resolution', goals: theRes.goals },
});
}}
>
...and here's the gql for the fragments:
const GET_FRAGMENT_GOAL = gql`
fragment targetRes on resolutions {
name
completed
goals {
_id
name
completed
}
}
`;
const SET_FRAGMENT_GOAL = gql`
fragment targetGoal on resolutions {
__typename
goals
}
`;
Here's a console error I'm getting:
You are using the simple (heuristic) fragment matcher, but your queries contain union or interface types.
Apollo Client will not be able to able to accurately map fragments.To make this error go away, use the IntrospectionFragmentMatcher as described in the docs: http://dev.apollodata.com/react/initialization.html#fragment-matcher
I read up on IntrospectionFragmentMatcher and it looks like mega-overkill for my situation. It appears I'm doing something else wrong. Here's the other error I'm getting at the same time:
Uncaught (in promise) TypeError: Cannot read property 'data' of undefined
What's wrong with my call to writeFragment?
After quite a few hours of study, I learned a lot about fragments!
I got it working. Here are the updated fragment and query definitions:
import gql from "graphql-tag";
let resolutionQueryFragments = {
goalParts: gql`
fragment goalParts on Goal {
_id
name
completed
}
`,
};
resolutionQueryFragments.resolutionGoals = gql`
fragment resolutionGoals on Resolution {
goals{
_id
name
completed
}
}
`;
const GET_RESOLUTIONS = gql`
query Resolutions {
resolutions {
_id
name
completed
...resolutionGoals
}
user {
_id
}
}
${resolutionQueryFragments.resolutionGoals}
`;
const CREATE_RESOLUTION = gql`
mutation createResolution($name: String!) {
createResolution(name: $name) {
__typename
_id
name
...resolutionGoals
completed
}
}
${resolutionQueryFragments.resolutionGoals}
`;
const GET_RESOLUTIONS_FOR_MUTATION_COMPONENT = gql`
query Resolutions {
resolutions {
_id
name
completed
...resolutionGoals
}
}
${resolutionQueryFragments.resolutionGoals}
`;
const CREATE_GOAL = gql`
mutation createGoal($name: String!, $resolutionId: String!) {
createGoal(name: $name, resolutionId: $resolutionId) {
...goalParts
}
}
${resolutionQueryFragments.goalParts}
`;
export {resolutionQueryFragments, GET_RESOLUTIONS, GET_RESOLUTIONS_FOR_MUTATION_COMPONENT, CREATE_RESOLUTION, CREATE_GOAL}
...and here's the updated Mutation component:
import React, {Component} from "react";
import gql from "graphql-tag";
import {graphql} from "react-apollo";
import {Mutation} from "react-apollo";
import {withApollo} from "react-apollo";
import {resolutionQueryFragments, CREATE_GOAL} from '../../imports/api/resolutions/queries';
const GoalForm = ({resolutionId, client}) => {
let input;
return (
<Mutation
mutation={CREATE_GOAL}
update={(cache, {data: {createGoal}}) => {
let resId = 'Resolution:' + resolutionId;
let currentRes = cache.data.data[resId];
let theGoals = cache.readFragment({
id: resId,
fragment: resolutionQueryFragments.resolutionGoals
});
theGoals = theGoals.goals.concat([createGoal]);
cache.writeFragment({
id: resId,
fragment: resolutionQueryFragments.resolutionGoals,
data: {goals: theGoals}
});
}}
>
{(createGoal, {data}) => (
<div>
<form
onSubmit={e => {
e.preventDefault();
createGoal({
variables: {
name: input.value,
resolutionId: resolutionId
}
});
input.value = "";
}}
>
<input
ref={node => {
input = node;
}}
/>
<button type="submit">Submit</button>
</form>
</div>
)}
</Mutation>
)
;
};
export default withApollo(GoalForm);

Resources