I am using the Graphiql component to render a console and want to fetch the schema via an introspection query. The problem is that if the component re-renders before the first introspection query is resolved (say a modal is opened for example), a second introspection query is fired off. Given that these queries are expensive for the backend, I'd like to avoid this.
Is there a way to avoid multiple introspection queries?
The GraphiQL component accepts a schema prop:
schema: a GraphQLSchema instance or null if one is not to be used. If undefined is provided, GraphiQL will send an introspection query using the fetcher to produce a schema.
You can use getIntrospectionQuery to get the complete introspection schema, fetch the introspection result and then use it to build a schema.
const { getIntrospectionQuery, buildClientSchema } = require('graphql')
const response = await fetch('ENDPOINT_URL', {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: { query: JSON.stringify(getIntrospectionQuery()) },
})
const introspectionResult = await response.json()
const schema = buildClientSchema(introspectionResult.data)
Do this before you render the component and then just pass in the schema as a prop. If your schema isn't going to change, you can also just save the introspection result to a file and use that instead of querying the server.
Related
I have a redux slice called pendingPost where I add some field to it like car_mileage using my reducers functions and save all this inside my pendingPost slice. Then submit using the data inside the pendingPost reducer
const pendingPostReducer = createSlice({
name: 'pendingPost',
initialState,
reducers: {
...
addPropertyToPendingPost: (state, action) => {
state.savedData = { ...state.savedData, ...action.payload };
},
Also I have postsAPI where I use rtk query to get All Posts, user Posts, ...
export const postsApi = createApi({
baseQuery: fetchBaseQuery({
baseUrl: API_URL,
}),
tagTypes: ['Post'],
endpoints: (build) => ({
getPosts: build.query({
query: (body) => ({
url: `post/filter`,
method: 'POST',
body: body,
}),
providesTags: (result) =>
result
? [
...result.data.map(({ id }) => ({ type: 'Post', id })),
{ type: 'Post', id: 'LIST' },
]
: [{ type: 'Post', id: 'LIST' }],
What I want to do is combine both of these where when I create post I want to do mutation and invalidate. How can I achieve this ?
I tried to search for a way to add save some fields inside RTK query but didn't find a way to achieve that, I guess rtk query is used only for caching and queriess
Your question has two parts.
I think you have a post scenario and you want to add another post to the list and update the posts.
for the first part
I assume you store the posts inside of postPending.(if this slice reducer is for the post so postPending is not a good name for this slice you should name it postSlice and inside of it do everything about the post) and show list of post on a page based on post pending.
in this case, you should go for createAsyncThunk instead of the RTK query. because as you guessed the RTK query purpose is caching the queries.
I don't know this will help you or not but you can dispatch RTK query outside of ApiSlices like so:
dispatch(ApiSlice.endpoints.getPosts.initiate())
for the second part:
I create an example for you in here.
basically, you need to create ApiSlice using RTK query which handles get post. so you have to follow these steps:
1- create API slice
2- create GET query endpoint for fetching the list.
3- use tagTypes to tell RTK query I have these tags.
4- use providesTags for each endpoint you create to tell RTK query I have these variants of tags for this endpoint.
5- when you want to create a POST, PUT, PATCH, or DELETE request to the server you literally case a change to the available list so you need mutations instead of query.
6- in mutations, you will use invalidatesTags to tell RTK query got and find in tags I've already provide for you and remove them if they have the same identity as the tags I invalidate in invalidatesTags.
7- invalidating tags makes the RTK query find out it must re-fetch the query and update the cache.
and you do not need to store posts somewhere else. as you can see I use the query hook in 2 different components and I only make a request once.
since the RTK query knows how many subscriptions you have to the same hook and if cache data is available for that query it will return it to the hook and will not create another request. in other words RTK query Hook will play a role as your postPending slice so don't have to store the data in two places.
I was facing a problem for sometime, that was I'm unable to clear cache using RTK query.
I tried in various ways but cache data is not clear.
I used invalidatesTag in my mutation query and it called the api instantly. But in this case I want to refetch multiple api again, but not from any rtk query or mutation. I want to make the api call after some user activity like click.
How can I solve this problem?
I made a separate function where I return api.util.invalidateTags(tag) or api.util.resetApiState().
this is my code-snipet:-
` const api = createApi({.....})
export const resetRtkCache = (tag?: String[]) => {
const api =
if (tag) {
return api.util.invalidateTags(tag)
} else {
return api.util.resetApiState()
}
}`
& I called it using dispatch method from other files
`const reloadData = () => {
dispatch(resetRtkCache())
}`
but here cache data is not removed.I think dispatch funtion is not working. I don't see the api call is being sent to server in the browser network.
But in this case I want to refetch multiple api again, but not from
any rtk query or mutation. I want to make the api call after some user
activity like click. How can I solve this problem?
So if I understood correctly what you want to achieve is to fetch some api that you have in RTK only after some kind of user interaction?
Can't you just define something like this?
const { data } = useGetYourQuery({ skip: skipUntilUserInteraction })
Where skipUntilUserInteraction is a component state variable that you will set to true and update to false based on the user interaction you need? (e.g. a click of a button).
So essentially on component render that specific endpoint will be skipped but will be fetched after the interaction that you want will happen?
wow, you actually asking so many questions at once. but I think you should definitely read the documentation because it covers all the questions you have.
so trying to answer your questions one by one.
I used invalidatesTag in my mutation query and it called the api instantly.
invalidating with Tags is one of the ways to clear the cache.
you should first set the tagTypes for your API then use those tags in mutation queries and tell the RTK query which part of entities you want to clear.
I want to refetch multiple APIs again
you can customize the query inside of a mutation or query like this example and by calling one function query you can send multiple requests at once and if you want to fetch the API again after the cache removed you do not need to do anything because RTK query will do it for you.
I want to make the API call after some user activity like click
every mutation gives u a function that you can pass to onClick like below:
import { use[Mymutation]Mutation } from 'features/api';
const MyComponenet() {
const [myMutationFunc, { isLoading, ...}] = use[Mymutation]Mutation();
return <button type='button' onClick={myMutationFunc}>Click for call mutaion</button>
}
and remember if you set providesTags for your endpoint which you were defined in tagTypes by clicking on the button and firing up the myMutationFunc you will be clearing the cache with those tags.
and if you looking for an optimistic update for the cache you can find your answer in here.
async onQueryStarted({ id, ...patch }, { dispatch, queryFulfilled }) {
const patchResult = dispatch(
api.util.updateQueryData('getPost', id, (draft) => {
Object.assign(draft, patch)
})
)
try {
await queryFulfilled
} catch {
patchResult.undo()
}
}
I am trying to filter the data before displaying it in the React app
Of course, the data arrives well and is displayed correctly, but I am trying to filter it through a field in the database
When I try on Postman via Query everything is done correctly
I will attach a picture of Postman
My question in particular is the following
How do I pass a query or options in saga knowing that it will be sent in the body
I have tried some solutions, but they are not working for you, as in the attached code
this saga
function* getServicesSupport() {
try {
const response = yield call(getServicesSupportApi, {query: {categoryTickets : 2}, options: {limit: 3}});
yield put(ServicesSupportApiResponseSuccess(GET_SERVICES_SUPPORT_LIST, response.data));
} catch (error) {
yield put(ServicesSupportApiResponseError(GET_SERVICES_SUPPORT_LIST, error));
}
}
this getServicesSupportApi
export const getServicesSupportList = () => api.post(url.GET_SERVICES_SUPPORT_LIST);
as you have seen in the picture for Postman,
a query was passed as my request is of type post
I'm new to react saga I don't know how to pass a query or options
I know very well how to pass a parameter in the request,
but I do not know how to pass a query
I need to pass more than one query in other requests, filters and sort or populate ....
So it is very important for me to know how to pass query as in
Assuming your api.get supports sending the body/query params, e.g. like api.get(url.GET_SERVICES_SUPPORT_LIST, {categoryTickets: 2}) you can modify the call effect to pass down the values
const getServicesSupportList = (payload) => api.post(url.GET_SERVICES_SUPPORT_LIST, payload);
let response = yield call(getServicesSupportApi, {categoryTickets : 2});
I am using apollo v3 with a create-react app. I fire a query with useQuery and expect results to be cached, but they are not.
In my App.jsx, I have:
const client = new ApolloClient({
uri: `${api}/graphql`,
cache: new InMemoryCache()
})
I wrap my app with the ApolloProvider.
I have a provider that uses this graphql query to fetch a list of users:
const USERS_QUERY = gql`
query GetUsers {
users {
id
email
fullName
}
}
`
The query works, when I inspect the apollo tab in chrome devtools, I see nothing in the cache section.
My questions:
Why are the results not cached if I am using the useQuery from #apollo/client?
const { loading, error, data } = useQuery(USERS_QUERY)
I thought results should be cached automatically.
I also tried to add a type policy:
cache: new InMemoryCache({
typePolicies: {
Users: {
keyFields: ['id']
}
}
})
But I think I use this if I want to normalise with a different key the cache, or if I want to decide how to merge new data by myself. But first I need to have the data in my cache
As far as I know, this is because your query does not have any input arguments / variables, so the cache does not know what item to get from the cache when the query is called again. From what I understand, the cache is only used when a specific piece of data is looked for with an ID; otherwise, if it is a generic query, the data might have changed and so is not cached.
On one of my pages in my app I'm doing two api calls with graphql apollo client. One is document, the other one menu. I need menu data in one of my components so I want to use readQuery in order to not to fetch it again. What I'm doing is:
const client = useApolloClient();
try {
const testData = client.readQuery({
query: gql`
query ($result: String) {
menu(result: $result) {
text
}
}
`,
variables: {
result: „testresult”
},
});
console.log(testData);
} catch(e) {
console.log(e);
}
What graphQL is doing is looking for document root query so the error looks like this:
Invariant Violation: Can't find field menu({"lang":"en-us"}) on object
{
"document({\"lanf\":\"en-us\",\"id\":\"mySite\"})": {
"type": "id",
"generated": false,
"id": "XO5tyxAAALGzcYGG",
"typename": "Document"
}
}.
I believe that it is because menu data is not there yet.
How can I wait until it will be there?
You are right. You are getting an error because the data is not in cache yet:
The query method, on the other hand, may send a request to your server if the appropriate data is not in your cache whereas readQuery will throw an error if the data is not in your cache. readQuery will always read from the cache.
https://www.apollographql.com/docs/react/advanced/caching/#readquery
To do what you want use the a normal query with a cache-only fetchPolicy.
https://www.apollographql.com/docs/react/api/react-apollo/#optionsfetchpolicy