How to use Redux RTK Query with Supabase - reactjs

Does anyone know how to use the Supabase Query pattern combined with RTK Query like for example https://dev.to/sruhleder/using-react-query-with-supabase-a03.

While I haven't used Supabase at all, it looks like it provides a Promise-based async request API.
In that case, you could use it with RTK Query's queryFn endpoint option, which lets you write your own arbitrary async logic and return whatever data you want.
While I haven't tested this code, a translation of the React Query + Supabase example to RTKQ might look like:
import { createApi, fakeBaseQuery } from '#reduxjs/toolkit/react';
const supabaseApi = createApi({
baseQuery: fakeBaseQuery(),
endpoints: (builder) => ({
getTodos: builder.query({
queryFn: async () => {
const {data, error} = await supabase
.from('todo')
.select('id, name')
.eq('done', false)
return data;
}
})
})
})

Related

How to use RTK-Query with Supabase

I have a project already configured with Supabase and using Redux-Toolkit. I have never used RTK-Query and I am just learning but after reading in the docs and looking for similar questions I have created a supabaseApi.js file which looks like:
import { createApi, fakeBaseQuery } from '#reduxjs/toolkit/query';
export const supabaseApi = createApi({ baseQuery: fakeBaseQuery(),
endpoints: (builder) =({
getStudents: builder.query({
queryFn: async () ={
const students = await supabase
.from('students')
.select()
return {students, error}
}
})
}) })
export const { useGetStudentsQuery } = supabaseApi;
However, when I call the useGetStudentsQuery() to get the students table I receive an error in the console telling me that "useGetStudentsQuery is not a function". I also noticed that when I begin to write the useGet function, it seems it hasn't been automatically created as I don't get the Visual Studio hints.
What is it that I am doing wrong? Thanks.
A few things:
to have the hooks, you have to import from '#reduxjs/toolkit/query/react';
the queryFn has to return either { data } or { error }, not { student }
your code does not seem to have a error variable, but tries to return it.
so:
import { createApi, fakeBaseQuery } from '#reduxjs/toolkit/query/react';
export const supabaseApi = createApi({ baseQuery: fakeBaseQuery(),
endpoints: (builder) =({
getStudents: builder.query({
queryFn: async () ={
const students = await supabase
.from('students')
.select()
return { data: students }
}
})
}) })
export const { useGetStudentsQuery } = supabaseApi;

Can't assign to object array when fetching data with RTK Query

I have come across a problem that I do not understand while trying to integrate redux toolkit and more specifically RTK Query into my project. All I want to do is fetch an object array from my backend using a query hook, pass this data into my component and alter some of the elements inside the data based on user actions.
I am copying the incoming array so as not to mutate the original.
I can replace entire objects in the array with 'blah' but if I try to alter a single value within one of the objects I get:
TypeError: Cannot assign to read only property 'title' of object '#<Object>'
This problem didn't arise when I was using fetch().
I have been stuck on this for days!!!
apiSlice
import { createApi, fetchBaseQuery } from '#reduxjs/toolkit/query/react';
export const apiSlice = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({ baseUrl: process.env.devUrl }),
tagTypes: ['categories'],
endpoints: (builder) => ({
getCategories: builder.query({
query: () => 'categories/includingJobs',
transformResponse: (response) => response.data.data,
providesTags: ['categories'],
}),
}),
});
export const { useGetCategoriesQuery} = apiSlice;
Component
import {
useGetCategoriesQuery,
} from '../features/api/apiSlice';
const Test = () => {
const {
data: categories,
isLoading,
isSuccess,
isError,
error,
} = useGetCategoriesQuery();
if (isSuccess) {
let categoriesCopy = JSON.parse(JSON.stringify(categories));
//This is the line that breaks everything. Why?
categoriesCopy[0].title = 'new title';
}
return <h1>Something rendered</h1>;
};
export default Test;

RTK query based on data from another request

I have some rtk query, that query data based on response of another request ( with axios )
const { dat } = useGetDataQuery({
keys // it comes from another request
})
export const someApi = createApi({
reducerPath: 'someApi',
baseQuery,
endpoints: (builder) => ({
getData: builder.query<
any,
{ keys: string[] }
>({
query: (arg) => {
const { keys } = arg
return {
url: '/some_endpoint',
params: {
keys,
},
}
},
}),
}),
})
And in this case data from previous request it's keys.
Is it possible make request inside query and use this data for my query, instead of doing
this request outside and pass data as params?
I wouldn't recommend it. Theoretically it is possible, using queryFn, as you have access to getState there - but if that state value would ever change, that wouldn't re-execute your query. It's really best to just put in dynamic values as argument.

How to implement multiple api call in a single query with RTK query

I'm new in RTK Query and I'm struggling with a use case I have to implement.
Scenario:
I have to merge the results from two API calls: the first API call is a private API call while the second one is a public API call. I need to merge the responses from these two APIs and write the computed result into the RTK cache so the UI can update accordingly.
Problem:
I'seeing that as soon as the await queryFullfilled is invoked, RTK Query immediately write into its cache the response from that API call and then when I make my calculation and try to update the RTK cache with apiSlice.util.updateQueryData the cache will change again. That's means that the UI will render twice, the first time using a wrong value (an array of persons) and the second time with the correct value (the JSON composed by ids and entities).
Question:
Is there a way to have just 1 write into the RTK cache so I can have just the computed value I need ? Because what is happening is that for some instances I'm having into the cache an array while I need the {ids: [...], entities: {}} JSON.
import { createEntityAdapter } from '#reduxjs/toolkit';
import axios from 'axios';
export const personsAdapter = createEntityAdapter();
const permitsInitialState = personsAdapter.getInitialState();
export const apiSlice = myServiceApi.injectEndpoints({
endpoints: (builder) => ({
getPersons: builder.query({
query: () => ({ url: '/persons', method: 'get' }),
onQueryStarted: async (_, { dispatch, queryFulfilled }) => {
try {
// Resolving the private API call
const { data: persons } = await queryFulfilled;
// Just a random public API call
const { data: todos } = await axios('https://jsonplaceholder.typicode.com/todos');
const enhancedPersons = /** Here the logic that merge the todos and the persons */
const state = personsAdapter.setAll(permitsInitialState, enhancedPermits);
dispatch(
apiSlice.util.updateQueryData('getPersons', _, (draft) => {
Object.assign(draft, state);
})
);
} catch (e) {
console.error(e);
}
},
}),
}),
});
That is one of the use cases of queryFn: Performing multiple requests with a single query
import {
createApi,
fetchBaseQuery,
FetchBaseQueryError,
} from '#reduxjs/toolkit/query'
import { Post, User } from './types'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/ ' }),
endpoints: (build) => ({
getRandomUserPosts: build.query<Post, void>({
async queryFn(_arg, _queryApi, _extraOptions, fetchWithBQ) {
// get a random user
const randomResult = await fetchWithBQ('users/random')
if (randomResult.error) throw randomResult.error
const user = randomResult.data as User
const result = await fetchWithBQ(`user/${user.id}/posts`)
return result.data
? { data: result.data as Post }
: { error: result.error as FetchBaseQueryError }
},
}),
}),
})

Redux Toolkit RTK Query call endpoints in queryFn

I'm using an endpoint with queryFn instead of query to perform many requests. Is there a way to calling endpoints that are already define instead of using fetchWithBQ ?
Here is an example.
export const api = createApi({
reducerPath: "api",
baseQuery: fetchBaseQuery({
baseUrl: "url",
}),
endpoints: (builder) => {
return {
device: builder.query<Device, string>({
query: (id) => `devices/${id}`, // repeat 1
}),
deployments: builder.query<Deployment[], string>({
queryFn: async (arg, _api, _extraOptions, fetchWithBQ) => {
// I would preferred to call the device endpoint directly.
// It will prevent to repeat the url and get cached data.
const result = await fetchWithBQ(`devices/${arg}`); // repeat 2
return ...
},
}),
};
},
});
No, at the moment that is not possible because it would add a tracking of "what depends on what else" to the whole things and that would get very complicated to manage internally.
You would usually do dependent queries just by using two useQuery hooks. And for abstraction of course you could combine those into a custom hook.
const useMyCustomCombinedQuery = (arg) => {
const result1 = useMyFirstQuery(arg)
const result2 = useMySecondQuery(result1.isSuccess ? result1.data.something : skipToken)
return {result1, result2}
}

Resources