Apollo Graphql fetchMore, updateQuery does not update state - graphql

I'm currently trying to implement pagination on my posts.
Using Apollo graphql here is my useQuery
const { data: postsData, fetchMore } = useQuery(POSTS_BY_USER_DRAFT, {
fetchPolicy: 'network-only',
variables: {
user: user.id,
start: 0,
limit: limit
},
onCompleted: () => {
setTotal(postsData[model].meta.pagination.total)
}})
and here is my onClick handler for fetching more posts
const loadMorePosts = async () => {
const nextStart = start + limit
setStart(nextStart);
await fetchMore({
variables: {
user: user.id,
offset: nextStart,
limit: limit,
},
updateQuery: (prevResult, { fetchMoreResult }) => {
if (!fetchMoreResult) {
return prevResult
}
const prevData = prevResult[model].data
const moreData = fetchMoreResult[model].data
fetchMoreResult[model].data = [...prevData, ...moreData]
// fetchMoreResult[model].data = [...moreData]
return fetchMoreResult
},
})}
My queries are successful as I do get correctly the data, however postsData does not get updated
[NOTICED]: If I switch fetchMoreResult[model].data = [...prevData, ...moreData] for
fetchMoreResult[model].data = [...moreData] my postsData does get updated.
I have tried return { ...fetchMoreResult } and multiple ways of returning data fearing an immutability/comparaison issue but it does not seem to do the job.

I'm not sure why, but setting a fetchPolicy for Apollo will do the job
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache({
typePolicies: {
Publication: {
merge: true,
},
Post: {
merge: true,
},
},
}),
defaultOptions: defaultOptions,
})

Related

Displaying the password on a filtered request

Strapi Version: 4.4.5
Operating System: linux
Database: sqlite
Node Version: 16.17.0
NPM Version:
Yarn Version: 1.22.19
Hello,
I'm just trying to get all the information from my "Channel" table, namely the product_id and the "users" concerned in the channel. I simply overload my find method like this:
module.exports = createCoreController("api::channel.channel", ({ strapi }) => ({
async find(ctx) {
const { user } = ctx.state:
const entity = await strapi.service("api::channel.channel").find({
filters: {
users: {
id: {
$in: user.id,
},
},
},
populate: ["users"]
});
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
return this.transformResponse(sanitizedEntity);
},
}));
And for some reason, I get all the user information and especially the hash of the passwords.
So I try to do a select on my populate like this, but it doesn't work :
module.exports = createCoreController("api::channel.channel", ({ strapi }) => ({
async find(ctx) {
const { user } = ctx.state;
const entity = await strapi.service("api::channel.channel").find({
filters: {
users: {
id: {
$in: user.id,
},
},
},
populate: {
users: {
select: ["id"]
}
}
});
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
return this.transformResponse(sanitizedEntity);
},
}));
Does anyone have a solution to my problem?
It is not select, you would use fields
Strapi Population
const qs = require('qs');
const query = qs.stringify({
fields: ['title', 'body'],
}, {
encodeValuesOnly: true, // prettify URL
});
So in your case, fields in combination with populate.
populate: {
users: {
fields: ["id"]
}
}

Using Nextjs (getServerSideprops) with elasticsearch from lambda node causes an error

I use nextjs with elastic search cloud, also use amplify and lambda created from amplify rest api.
This is my function:
export async function getServerSideProps(context) {
let esItems;
try {
const { query } = context.query;
const apiName = 'APINAME';
const path = '/searchlist';
const myInit = {
response: true,
queryStringParameters: { query: query }
};
const response = await API.get(apiName, path, myInit);
esItems = response;
} catch (err) {
console.log(err)
}
return {
props: {
allProducts: esItems ? esItems.data.items : [],
}
};
}
I return 50 products from elastic and get this error:
502 ERROR
The request could not be satisfied.
The Lambda function returned invalid JSON: The JSON output is not parsable. We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
This is lambda function:
app.get('/searchlist', async (req, res) => {
const { query } = req.query;
const client = new Client({
node: "https://elastic-cloud....",
auth: {
username: process.env.USERNAME,
password: process.env.PASSWORD
}
});
const searchQuery = {
query: {
multi_match: {
query: query,
type: "phrase",
fields: [
'manufacturerTypeDescription^8',
'manufacturerName^6',
'ean^4',
'_id^2',
]
}
}
}
const typeSearch = {
index: "product",
size: 50,
body: searchQuery
}
const r = await client.search(typeSearch);
const hits = r.body.hits;
const items = hits.hits.map((hit) => ({
_id: hit._id,
...hit._source,
}))
res.json({
success: 'get call succeed!',
items
});
});

Pagination Apollo Client 3 - Cache merges but does not render new results when paginating

I'd love to implement nested pagination within my application. I have been reading the docs and looking at several other examples but I just can't get this to work - any help is appreciated! Thanks!
React component:
I am clicking the button to run the fetchMore function provided by the useQuery hook (apollo). The network request is going through and the new products are merged into the cache... but no new products render on the page.
export const FilterableKit = () => {
const selectedKitId = useReactiveVar(selectedKitIdVar);
const [
getKitProducts,
{ data: getKitProductsData, loading: getKitProductsLoading, fetchMore },
] = useGetKitProductsLazyQuery();
useEffect(() => {
if (selectedKitId) {
getKitProducts({
variables: {
getKitsInput: {
_id: {
string: selectedKitId,
filterBy: "OBJECTID" as StringFilterByEnum,
},
},
getProductsInput: {
config: {
pagination: {
reverse: true,
limit: 3,
},
},
},
},
});
}
}, [getKitProducts, selectedKitId]);
const kitProducts = getKitProductsData?.getKits.data?.find(
(kit) => kit?._id === selectedKitId
)?.products.data;
const handleLoadMore = () => {
if (kitProducts && kitProducts?.length > 0) {
const remaining =
getKitProductsData?.getKits.data[0]?.products.stats?.remaining;
if (remaining && remaining > 0) {
const cursor =
kitProducts[kitProducts.length - 1] &&
kitProducts[kitProducts.length - 1]?.createdAt;
fetchMore({
variables: {
getProductsInput: {
config: {
pagination: {
reverse: true,
createdAt: cursor,
},
},
},
},
});
}
}
};
return (
<CContainer>
<KitItemCards products={kitProducts} loading={getKitProductsLoading} />
<CContainer className="d-flex justify-content-center my-3">
<CButton color="primary" className="w-100" onClick={handleLoadMore}>
Load More
</CButton>
</CContainer>
</CContainer>
);
};
Type Policies: I define the "Kit" typePolicy to merge products into the correct field.
export const cache: InMemoryCache = new InMemoryCache({
typePolicies: {
Kit: {
fields: {
products: {
keyArgs: false,
merge(existing = [] as Product[], incoming: GetProductsResponse) {
if (!incoming) return existing;
if (!existing) return incoming;
const { data: products, ...rest } = incoming;
let result: any = rest;
result = [...existing, ...(products ?? [])];
return result;
},
},
},
},
});
Thanks for any pointers in the right direction! Let me know if there is something else you'd like to see.

Running Subscriptions on GraphiQL using express-graphql, graphql, graphql-subscriptions and graphql-subscriptions-ws

I'm fairly new to GraphQL and currently familiarizing myself by making a quiz application using React on the front-end.
At the moment, I'm busy with my back-end. After successfully setting up queries and mutations, I am finding it difficult to get subscriptions working. When using GraphiQL, I am getting null as an output instead of "Your subscription data will appear here..."
Queries and the mutation for adding the quiz works.
The entry point of my server, app.js:
const express = require("express");
const mongoose = require("mongoose");
const { graphqlHTTP } = require("express-graphql");
const schema = require("./graphql/schema");
const cors = require("cors");
const port = 4000;
//Subscriptions
const { createServer } = require("http");
const { SubscriptionServer } = require("subscriptions-transport-ws");
const { execute, subscribe } = require("graphql");
const subscriptionsEndpoint = `ws://localhost:${port}/subscriptions`;
const app = express();
app.use(cors());
mongoose.connect("mongodb://localhost/quizify", {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true,
useFindAndModify: false
});
mongoose.connection.once("open", () => console.log("connected to database"));
app.use("/graphql", graphqlHTTP({
schema,
graphiql: true,
subscriptionsEndpoint,
}));
const webServer = createServer(app);
webServer.listen(port, () => {
console.log(`GraphQL is now running on http://localhost:${port}`);
//Set up the WebSocket for handling GraphQL subscriptions.
new SubscriptionServer({
execute,
subscribe,
schema
}, {
server: webServer,
path: '/subscriptions',
});
});
Below is from the schema definitions, schema.js:
const { graphqlHTTP } = require("express-graphql");
const graphql = require("graphql");
const { PubSub } = require("graphql-subscriptions");
const pubsub = new PubSub();
//Import of Mongoose Schemas:
const Quiz = require("../models/quiz");
const {
GraphQLObjectType,
GraphQLList,
GraphQLSchema,
GraphQLNonNull,
GraphQLID,
GraphQLString,
GraphQLBoolean
} = graphql;
const QuizType = new GraphQLObjectType({
name: "Quiz",
fields: () => ({
id: { type: GraphQLID },
title: { type: GraphQLString },
questions: {
type: new GraphQLList(QuestionType),
resolve(parent, args) {
return Question.find({ quizId: parent.id });
}
},
creator: {
type: UserType,
resolve(parent, args) {
return User.findById(parent.creatorId);
}
}
})
});
const NEW_QUIZ_ADDED = "new_quiz_added";
const Subscription = new GraphQLObjectType({
name: "Subscription",
fields: {
quizAdded: {
type: QuizType,
subscribe: () => {
pubsub.asyncIterator(NEW_QUIZ_ADDED);
},
},
}
});
const Mutation = new GraphQLObjectType({
name: "Mutation",
fields: {
createQuiz: {
type: QuizType,
args: {
title: { type: new GraphQLNonNull(GraphQLString) },
creatorId: { type: new GraphQLNonNull(GraphQLID) }
},
resolve(parent, args) {
const newQuiz = new Quiz({ //Quiz imported from Mongoose schema.
title: args.title,
creatorId: args.creatorId,
});
pubsub.publish(NEW_QUIZ_ADDED, { quizAdded }); //NEW_QUIZ_ADDED - a constant defined above for easier referencing.
return newQuiz.save();
}
},
},
});
module.exports = new GraphQLSchema({
query: RootQuery,
mutation: Mutation,
subscription: Subscription,
});
I've looked around, however I'm not finding an method that works for this kind of site. I know it might sound like a simple problem. Any assistance would be greatly appreciated!

Apollo react: combining rest and graphql with link-state

I am trying to use REST endpoints to post data and GraphQL for query and fetch along with apollo-link-state. My rest endpoint is getting hit and application is getting created. But when I try to run the query to write to cache it's not hitting the graphql endpoint. and I keep getting the following error:
Unhandled Rejection (Error): Can't find field findApplicationByUuid({"uuid":"912dc46d-2ef8-4a77-91bc-fec421f5e4bc"}) on object (ROOT_QUERY) {
"application": {
"type": "id",
"id": "$ROOT_QUERY.application",
"generated": true
}
}.
Here are my GQL query
import gql from 'graphql-tag';
const START_APP = gql`
mutation startApp($type: String!) {
application: startApp( input: { applicationType: $type})
#rest(type: "Application", path: "v1/member/application/create", method: "POST") {
uuid: applicationUuid
}
}
`;
const GET_APP = gql`
query getAppByUuid($uuid: String!) {
application: findApplicationByUuid(uuid: $uuid) {
uuid,
type,
version,
}
}
`;
export {
START_APP,
GET_APP,
};
Here is my resolver:
import { START_APP, GET_APP } from './application'
import client from '../apolloClient';
const startApp = async (_, { type }, { cache }) => {
client.mutate({
variables: { type },
mutation: START_APP,
}).then(({ data: { application } }) => {
const { uuid } = application;
const { data } = cache.readQuery({
query: GET_APP,
variables: { uuid },
});
cache.writeQuery({
query: GET_APP,
data,
});
});
};
const resolvers = {
Mutation: {
startApp,
},
};
Here are my links:
import { resolvers, defaults } from './resolvers';
const cache = new InMemoryCache();
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.map(({ message, locations, path }) =>
console.log(`[GQL Error]: Msg: ${message}, Loc: ${locations}, Path: ${path}`));
if (networkError) console.log(`[Network error]: ${networkError}`);
});
const stateLink = withClientState({
cache,
defaults,
resolvers,
});
const restLink = new RestLink({
uri: 'http://localhost:7010/api/',
credentials: 'include',
});
const batchHttpLink = new BatchHttpLink({
uri: 'http://localhost:7010/api/graphql',
credentials: 'include',
});
const httpLink = new HttpLink({
uri: 'http://loaclhost:7010/api/graphql',
credentials: 'include',
});
const link = ApolloLink.from([
errorLink,
stateLink,
restLink,
httpLink,
]);
my client
const client = new ApolloClient({
link,
cache,
});
My react component:
// Remote Mutation
const START_APP = gql`
mutation startApp($type: String!) {
startApp(type: $type) #client {
uuid
}
}
`;
const StartApp = ({ match }) => {
const { type } = match.params;
return (
<Mutation mutation={START_APP} variables={{ type }}>
{startApp => (<button onClick={startApp}>click me</button>)}
</Mutation>
)
};
When I hit the button it calls create endpoint and creates the app and returns the uuid. But the following I want to happen is hit the graphql endpoint and query for the application using the uuid returned from the rest request, and write that data to the cache/state.

Resources