T
TanStack7d ago
optimistic-gold

How to redirect in tanstack mutation ?

any idea ? pass router ? redirect function does not work ( handleRedirects: true )
33 Replies
conscious-sapphire
conscious-sapphire7d ago
what do you need exactly? some more context please
optimistic-gold
optimistic-goldOP7d ago
for example if api returns 403 , I want to navigate to login page , in OnError
conscious-sapphire
conscious-sapphire7d ago
so you want a generic error handling here? @Ryan Gillie i think we discussed this some time ago
optimistic-gold
optimistic-goldOP7d ago
export function createOrderMutationOptions() { return mutationOptions({ mutationKey: ['orders'], mutationFn: async (data: CreateOrder) => { const api = await getApi() const res = await api('/orders', { method: 'POST', body: data, }) return res }, onSuccess: async (_, __, ___, { client }) => { await client.invalidateQueries({ queryKey: ['orders'] }) }, onError: (error) => { if (isResponseError(error) && error.status === 400) { // redirect please } }, }) } for example and my question is what does handleRedirects: true do ? setupRouterSsrQueryIntegration({ router: router, queryClient, handleRedirects: true, })
conscious-sapphire
conscious-sapphire7d ago
it handles redirects thrown for queries
optimistic-gold
optimistic-goldOP7d ago
So , no mutations ?
No description
distinguished-blush
distinguished-blush7d ago
that entire package got rewritten, I don’t think my code is in there anymore 😅 this only handles redirects thrown from server functions using the redirect function
distinguished-blush
distinguished-blush7d ago
Server Functions | TanStack Start React Docs
What are Server Functions? Server functions let you define server-only logic that can be called from anywhere in your application loaders, components, hooks, or other server functions. They run on the...
distinguished-blush
distinguished-blush7d ago
it doesn’t handle just normal errors, you’d have to do it yourself
conscious-sapphire
conscious-sapphire7d ago
yeah i rewrote it but i thought i carried that part over
distinguished-blush
distinguished-blush7d ago
it does, I think he’s just throwing normal Errors instead of using the redirect function What does your server function look like?
optimistic-gold
optimistic-goldOP7d ago
There is no server function Separate backend Any solution ?
conscious-sapphire
conscious-sapphire7d ago
what's the target of this redirect? should it perform a client side redirect or a full page reload?
optimistic-gold
optimistic-goldOP7d ago
client side redirect , for full page reload I guess window is ok
conscious-sapphire
conscious-sapphire7d ago
you could try router.navigate({href: '/some/location/'})
optimistic-gold
optimistic-goldOP7d ago
how to access router ?
distinguished-blush
distinguished-blush6d ago
try something like
import { type AnyRouter } from "@tanstack/react-router";

// pass in router
export function createOrderMutationOptions(router: AnyRouter) {
return mutationOptions({
mutationKey: ['orders'],
mutationFn: async (data: CreateOrder) => {
const api = await getApi()
const res = await api('/orders', {
method: 'POST',
body: data,
})
return res
},
onSuccess: async (_, __, ___, { client }) => {
await client.invalidateQueries({ queryKey: ['orders'] })
},
onError: (error) => {
if (isResponseError(error) && error.status === 400) {
return router.navigate({ to: "/" }) // <-- HERE
}

// Other stuff if not navigation error
},
})
}

// usage
const router = useRouter();

const {} = useMutation(createOrderMutationOptions(router));
import { type AnyRouter } from "@tanstack/react-router";

// pass in router
export function createOrderMutationOptions(router: AnyRouter) {
return mutationOptions({
mutationKey: ['orders'],
mutationFn: async (data: CreateOrder) => {
const api = await getApi()
const res = await api('/orders', {
method: 'POST',
body: data,
})
return res
},
onSuccess: async (_, __, ___, { client }) => {
await client.invalidateQueries({ queryKey: ['orders'] })
},
onError: (error) => {
if (isResponseError(error) && error.status === 400) {
return router.navigate({ to: "/" }) // <-- HERE
}

// Other stuff if not navigation error
},
})
}

// usage
const router = useRouter();

const {} = useMutation(createOrderMutationOptions(router));
Does that work?
optimistic-gold
optimistic-goldOP6d ago
I will check that but I guess yes
like-gold
like-gold6d ago
How do you choose what the status code is when you do a "throw new Error()"?
distinguished-blush
distinguished-blush6d ago
@AMIR if you want to handle it globally you can do something like
export function getRouter() {
const queryClient = new QueryClient({
// ...other options
mutationCache: new MutationCache({
onError: (error) => {
const router = getRouterInstance();

if (isResponseError(error) && error.status === 400) {
return router.navigate({ to: "" }) // whatever
}
},
}),
queryCache: new QueryCache({
onError: (error) => {
const router = getRouterInstance();

if (isResponseError(error) && error.status === 400) {
return router.navigate({ to: "" }) // whatever
}
},
}),
});

const router = createRouter({
routeTree,
context: {
queryClient
},
// ...other options
});

setupRouterSsrQueryIntegration({ router, queryClient });

return router;
}
export function getRouter() {
const queryClient = new QueryClient({
// ...other options
mutationCache: new MutationCache({
onError: (error) => {
const router = getRouterInstance();

if (isResponseError(error) && error.status === 400) {
return router.navigate({ to: "" }) // whatever
}
},
}),
queryCache: new QueryCache({
onError: (error) => {
const router = getRouterInstance();

if (isResponseError(error) && error.status === 400) {
return router.navigate({ to: "" }) // whatever
}
},
}),
});

const router = createRouter({
routeTree,
context: {
queryClient
},
// ...other options
});

setupRouterSsrQueryIntegration({ router, queryClient });

return router;
}
It's weird because the router needs the queryClient but the queryClient needs the router so gotta do some weird typescript stuff with null! and then set it later, @Manuel Schiller correct me if I'm wrong but I think this should work The reason it has to be done this way is because it only handles redirects thrown from server function redirects, if it's from an outside source you gotta do it yourself
optimistic-gold
optimistic-goldOP6d ago
I tried globally and it works well , but I was looking for a way to don't pass router, for example throw redirect , we can get redirect error in on Error Globally , and we can check that with isRedirect function , if there is a way to handle that there , it will be great
distinguished-blush
distinguished-blush6d ago
My way globally should work
conscious-sapphire
conscious-sapphire6d ago
we have a getRouterInstance() as well
distinguished-blush
distinguished-blush6d ago
Oh, sick
optimistic-gold
optimistic-goldOP6d ago
💀 Does It return router ?
conscious-sapphire
conscious-sapphire6d ago
well.. what else could it return ? 😄
optimistic-gold
optimistic-goldOP6d ago
Good answer (:
distinguished-blush
distinguished-blush6d ago
Updated my comment above using getRouterInstance oh so you mean in your createMutationOptions you throw redirect? I don't know if that would work because of the way of mutationOptions and the cache interaction w/ callbacks
optimistic-gold
optimistic-goldOP6d ago
Yes, but if getRouterInstance() works , we don't need that
like-gold
like-gold6d ago
How do you determine the error.status?
distinguished-blush
distinguished-blush6d ago
wasn’t my code I have no idea what the isResponseError method does ¯\_(ツ)_/¯
optimistic-gold
optimistic-goldOP6d ago
It’s from up-fetch, detecting HTTP errors. Why don't we have it in tanstack router ? It's from tanstack start
conscious-sapphire
conscious-sapphire6d ago
because start instantiates the router and thus can provide access to said instance

Did you find this page helpful?