orderBy: the results from update cache query - react-apollo

Working on a react apollo graphcool project
I've got my mutation update working, however I would like to filter the results, the results only filter on page refresh?
Looking at cache.writeQuery() the docs say get the query and concat to that so i guess thats why its not filtering. Is there anyway to query after?
Here the code from my CreatePost component
import React from 'react';
import gql from "graphql-tag";
import { Mutation } from "react-apollo";
const GET_POSTS = gql`
{
allPosts(orderBy: createdAt_DESC) {
id
title
content
}
}
`;
const CREATE_POST = gql`
mutation createPost($title: String!, $content: String!){
createPost(title: $title, content: $content){
id
title
content
}
}
`;
const udpateCache = (cache, { data: { createPost } }) => {
const { allPosts } = cache.readQuery({ query: GET_POSTS });
cache.writeQuery({
query: GET_POSTS,
data: { allPosts: allPosts.concat([createPost]) }
})
}
const CreatePost = () => {
//vars for the input refs
let titleInput
let contentInput
return (
<Mutation
mutation={CREATE_POST}
update={udpateCache}
>
{createPost => ( //##form and onSubmit ##// ) }
</Mutation>
)
}
export default CreatePost

When you do your writeQuery you also need to pass in any variables used, to make sure you receive the same information from the cache.
const udpateCache = (cache, { data: { createPost } }) => {
const { allPosts } = cache.readQuery({ query: GET_POSTS });
cache.writeQuery({
query: GET_POSTS,
data: { allPosts: allPosts.concat([createPost]) },
variables: { orderBy: /* input */ }
})
}

Related

Dynamic routing using graphQL in a Next.js app

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.

why does my graphql return an empty array?

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}

How to integrate Multiple clients using graphql hooks in react hooks

I wanna integrate multiple clients in my react-hooks application. I'm using graphql-hooks via Apollo client we have a module to integrate multiple clients
Following is the link Apollo graphql multiple client
`https://www.npmjs.com/package/#titelmedia/react-apollo-multiple-clients`
Following I'm using for graphql hooks
https://www.npmjs.com/package/graphql-hooks
How do we achieve the same for graphql-hooks?
My requirement is depending on the selection of the radio button I need to switch between these multiple clients all that in one component using more than one client.
In my app I'm using graphql-hook we wrap the component to the client here the same component has functionality wherein depending on the select one of the radio buttons the client must be switch.I'm having one client but need to integrate multiple clients I googled but i've not found so have questioned here.
Is this possible?
Can anyone please help out there
We can use new GraphQLClient() multiple times to get multiple graphql clients
Here is a good approach and you can follow it: https://www.loudnoises.us/next-js-two-apollo-clients-two-graphql-data-sources-the-easy-way/
I have two middleware graphql servers running. One for the app one for analytics. I do it in two different ways according to my need as below...
CubeClient.jsx
import { ApolloClient } from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";
import { SchemaLink } from "apollo-link-schema";
import { makeExecutableSchema } from "graphql-tools";
const cache = new InMemoryCache();
const defaultDashboardItems = [{"vizState":"{\"query\":{\"measures\":[\"Product.count\"],\"timeDimensions\":[{\"dimension\":\"Product.createdAt\"}],\"dimensions\":[\"Producttype.name\"],\"filters\":[],\"order\":{}},\"chartType\":\"bar\",\"orderMembers\":[{\"id\":\"Product.count\",\"title\":\"Product Count\",\"order\":\"none\"},{\"id\":\"Producttype.name\",\"title\":\"Producttype Name\",\"order\":\"none\"},{\"id\":\"Product.createdAt\",\"title\":\"Product Created at\",\"order\":\"none\"}],\"pivotConfig\":{\"x\":[\"Producttype.name\"],\"y\":[\"measures\"],\"fillMissingDates\":true,\"joinDateRange\":false},\"shouldApplyHeuristicOrder\":true,\"sessionGranularity\":null}","name":"Product Types","id":"2","layout":"{\"x\":0,\"y\":0,\"w\":24,\"h\":8}"},{"vizState":"{\"query\":{\"measures\":[\"Sale.total\"],\"timeDimensions\":[{\"dimension\":\"Sale.createdAt\",\"granularity\":\"day\"}],\"dimensions\":[\"Customer.firstname\"],\"order\":{\"Sale.total\":\"desc\"}},\"chartType\":\"area\",\"orderMembers\":[{\"id\":\"Sale.total\",\"title\":\"Sale Total\",\"order\":\"desc\"},{\"id\":\"Customer.firstname\",\"title\":\"Customer Firstname\",\"order\":\"none\"},{\"id\":\"Sale.createdAt\",\"title\":\"Sale Created at\",\"order\":\"none\"}],\"pivotConfig\":{\"x\":[\"Sale.createdAt.day\"],\"y\":[\"Customer.firstname\",\"measures\"],\"fillMissingDates\":true,\"joinDateRange\":false},\"shouldApplyHeuristicOrder\":true}","name":"Sale Total","id":"3","layout":"{\"x\":0,\"y\":8,\"w\":24,\"h\":8}"}]
export function getCubeClient(location){
const getDashboardItems = () => {
// return JSON.parse(window.localStorage.getItem("dashboardItems")) || defaultDashboardItems;
return defaultDashboardItems
}
const setDashboardItems = items => {
return window.localStorage.setItem("dashboardItems", JSON.stringify(items));
}
const nextId = () => {
const currentId = parseInt(window.localStorage.getItem("dashboardIdCounter"), 10) || 1;
window.localStorage.setItem("dashboardIdCounter", currentId + 1);
return currentId.toString();
};
const toApolloItem = i => ({ ...i, __typename: "DashboardItem" });
const typeDefs = `
type DashboardItem {
id: String!
layout: String
vizState: String
name: String
}
input DashboardItemInput {
layout: String
vizState: String
name: String
}
type Query {
dashboardItems: [DashboardItem]
dashboardItem(id: String!): DashboardItem
}
type Mutation {
createDashboardItem(input: DashboardItemInput): DashboardItem
updateDashboardItem(id: String!, input: DashboardItemInput): DashboardItem
deleteDashboardItem(id: String!): DashboardItem
}
`;
const schema = makeExecutableSchema({
typeDefs,
resolvers: {
Query: {
dashboardItems() {
const dashboardItems = getDashboardItems();
return dashboardItems.map(toApolloItem);
},
dashboardItem(_, { id }) {
const dashboardItems = getDashboardItems();
return toApolloItem(dashboardItems.find(i => i.id.toString() === id));
}
},
Mutation: {
createDashboardItem: (_, { input: { ...item } }) => {
const dashboardItems = getDashboardItems();
item = { ...item, id: nextId(), layout: JSON.stringify({}) };
dashboardItems.push(item);
setDashboardItems(dashboardItems);
return toApolloItem(item);
},
updateDashboardItem: (_, { id, input: { ...item } }) => {
const dashboardItems = getDashboardItems();
item = Object.keys(item)
.filter(k => !!item[k])
.map(k => ({
[k]: item[k]
}))
.reduce((a, b) => ({ ...a, ...b }), {});
const index = dashboardItems.findIndex(i => i.id.toString() === id);
dashboardItems[index] = { ...dashboardItems[index], ...item };
setDashboardItems(dashboardItems);
return toApolloItem(dashboardItems[index]);
},
deleteDashboardItem: (_, { id }) => {
const dashboardItems = getDashboardItems();
const index = dashboardItems.findIndex(i => i.id.toString() === id);
const [removedItem] = dashboardItems.splice(index, 1);
setDashboardItems(dashboardItems);
return toApolloItem(removedItem);
}
}
}
});
return new ApolloClient({
cache,
uri: "http://localhost:4000",
link: new SchemaLink({
schema
})
});
}
Dashboard.jsx
import { getCubeClient } from './CubeClient';
import { Query } from "react-apollo";
let cubeClient = getCubeClient()
const Dashboard = () => {
const dashboardItem = item => (
<div key={item.id} data-grid={defaultLayout(item)}>
<DashboardItem key={item.id} itemId={item.id} title={item.name}>
<ChartRenderer vizState={item.vizState} />
</DashboardItem>
</div>
);
return(
<Query query={GET_DASHBOARD_ITEMS} client={cubeClient}>
{({ error, loading, data }) => {
if (error) return <div>"Error!"</div>;
if (loading) return (
<div className="alignCenter">
<Spinner color="#be97e8" size={48} sizeUnit="px" />
</div>
);
if (data) {
return (<DashboardView t={translate} dashboardItems={data && data.dashboardItems}>
{data && data.dashboardItems.map(deserializeItem).map(dashboardItem)}
</DashboardView>)
} else {
return <div></div>
}
}}
</Query>
)
};
Explore.jsx
import { useQuery } from "#apollo/react-hooks";
import { withRouter } from "react-router-dom";
import ExploreQueryBuilder from "../components/QueryBuilder/ExploreQueryBuilder";
import { GET_DASHBOARD_ITEM } from "../graphql/queries";
import TitleModal from "../components/TitleModal.js";
import { isQueryPresent } from "#cubejs-client/react";
import PageHeader from "../components/PageHeader.js";
import ExploreTitle from "../components/ExploreTitle.js";
import { PageLayout } from '#gqlapp/look-client-react';
import Helmet from 'react-helmet';
import { translate } from '#gqlapp/i18n-client-react';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faAngleRight, faTrashAlt, faPlusCircle } from '#fortawesome/free-solid-svg-icons';
import settings from '#gqlapp/config';
import PropTypes from 'prop-types';
import { getCubeClient } from './CubeClient';
let cubeClient = getCubeClient()
const Explore = withRouter(({ history, location,t }) => {
const [addingToDashboard, setAddingToDashboard] = useState(false);
const params = new URLSearchParams(location.search);
const itemId = params.get("itemId");
const { loading, error, data } = useQuery(GET_DASHBOARD_ITEM, {
client: cubeClient,
variables: {
id: itemId
},
skip: !itemId
});
Titlemodal.jsx
import React from "react";
import { Modal, Input } from "antd";
import { useMutation } from "#apollo/react-hooks";
import { GET_DASHBOARD_ITEMS } from "../graphql/queries";
import {
CREATE_DASHBOARD_ITEM,
UPDATE_DASHBOARD_ITEM
} from "../graphql/mutations";
import { getCubeClient } from '../containers/CubeClient';
let cubeClient = getCubeClient()
const TitleModal = ({
history,
itemId,
titleModalVisible,
setTitleModalVisible,
setAddingToDashboard,
finalVizState,
setTitle,
finalTitle
}) => {
const [addDashboardItem] = useMutation(CREATE_DASHBOARD_ITEM, {
client: cubeClient,
refetchQueries: [
{
query: GET_DASHBOARD_ITEMS
}
]
});
const [updateDashboardItem] = useMutation(UPDATE_DASHBOARD_ITEM, {
client: cubeClient,
refetchQueries: [
{
query: GET_DASHBOARD_ITEMS
}
]
});
queries and mutations.js
import gql from "graphql-tag";
export const GET_DASHBOARD_ITEMS = gql`
query GetDashboardItems {
dashboardItems {
id
layout
vizState
name
}
}
`;
export const GET_DASHBOARD_ITEM = gql`
query GetDashboardItem($id: String!) {
dashboardItem(id: $id) {
id
layout
vizState
name
}
}
`;
export const CREATE_DASHBOARD_ITEM = gql`
mutation CreateDashboardItem($input: DashboardItemInput) {
createDashboardItem(input: $input) {
id
layout
vizState
name
}
}
`;
export const UPDATE_DASHBOARD_ITEM = gql`
mutation UpdateDashboardItem($id: String!, $input: DashboardItemInput) {
updateDashboardItem(id: $id, input: $input) {
id
layout
vizState
name
}
}
`;
export const DELETE_DASHBOARD_ITEM = gql`
mutation DeleteDashboardItem($id: String!) {
deleteDashboardItem(id: $id) {
id
layout
vizState
name
}
}
`;

local state management for graphql vue apollo

I am trying to make local state management with vue apollo, but even after following the docs I am getting no result. There is no error in the console so I am not sure what is wrong.
Here is my setup:
// main.js file the initializing part
const client = new ApolloClient({
link: ApolloLink.from([
errorLink,
authMiddleware,
link,
]),
cache,
typeDefs,
resolvers,
connectToDevTools: true,
});
// resolvers file
import gql from 'graphql-tag';
import { todoItemsQuery } from './task.queries';
export const typeDefs = gql`
type Item {
id: ID!
text: String!
done: Boolean!
}
type Mutation {
changeItem(id: ID!): Boolean
deleteItem(id: ID!): Boolean
addItem(text: String!): Item
}
`;
export const resolvers = {
Mutation: {
checkItem: (_, { id }, { cache }) => {
const data = cache.readQuery({ query: todoItemsQuery });
console.log('data res', data);
const currentItem = data.todoItems.find(item => item.id === id);
currentItem.done = !currentItem.done;
cache.writeQuery({ query: todoItemsQuery, data });
return currentItem.done;
},
},
};
//queries file
import gql from 'graphql-tag';
export const todoItemsQuery = gql`
{
todoItems #client {
id
text
done
}
}
`;
export const checkItemMutation = gql`
mutation($id: ID!) {
checkItem(id: $id) #client
}
`;
// component where I call it
apollo: {
todoItems: {
query: todoItemsQuery
}
},
checkItem(id) {
this.$apollo
.mutate({
mutation: checkItemMutation,
variables: { id }
})
.then(({ data }) => {
console.log("CACHE", data);
});
},
I get empty todoItems, no errors.
Please let me know what am I missing, I am not grasping some concept I think, and if there is a way to use vuex with apollo then I can do that too.
Foreword: I'm not an Apollo specialist, just starting to use it.
Just in case, these thoughts may help you. If they don't, well, I'm sorry.
In main.js: Apollo documentation provides a slightly different set up when using Apollo Boost (https://www.apollographql.com/docs/link/links/state/#with-apollo-boost). Just in case, this is how I have set up my implementation so far.
import VueApollo from 'vue-apollo'
import ApolloClient from 'apollo-boost';
import { InMemoryCache } from 'apollo-cache-inmemory';
const cache = new InMemoryCache();
Vue.use(VueApollo)
const apolloClient = new ApolloClient({
//...whatever you may already have,
clientState: {
// "defaults" is your initial state - if empty, I think it might error out when your app launches but is not hydrated yet.
defaults: {
todoItems: [],
}
cache,
typeDefs: {...yourTypeDefs},
resolvers: {...yourResolvers},
},
});
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
});
In your typeDefs: I can not see a Query Type for your todoItems:
type Query {
todoItemsQuery: [Item]
}
In your component: my own implementation was not working until I added an update method to the apollo request:
apollo: {
todoItems: {
query: todoItemsQuery,
update: data => data.todoItems
}
},

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