How to invalidate routes/router in backend/webhook?
Hello, I'm making an app using T3 that uses Stripe for subscription and payment. I have made purchase and manage billing routes that all work as expected. Now, in this app there is going to be different subscription tiers and I want to allow users to upgrade/downgrade (update) their subscriptions. I've set up the webhook and tRPC routes to handle this and it's working--however, because the particular update does not send the user to an external page (like stripe portal) and back, the UI does not refresh to reflect the subscription update (once the webhook has completed its thing).
Using onSuccess() in the useMutation() doesn't work, and I suspect that's because the mutation finishes well before the webhook receives the confirmation from stripe.
I tried (foolishly) to use utils and api.useContext() to call the routes inside the webhook after the handler function had done its thing--but this, of course, only works in a react component.
TL;DR: What's a good way to trigger a invalidate on some tRPC routes from the backend such that the frontend then refreshes?
Appreciate any help with this. Cheers
4 Replies
you can't talk to the frontend from the backend without using websockets or similar
either open a socket or have some endpoint that you can ping every couple of seconds
Alright! Thanks! I'll start looking into it and post my solution here once I have something that fits my use case.
Ok, so I got an idea looking at one of the examples from Tanstack, and it involves using the refetchInterval option in useQuery. I'll post it here and add explanation of the following code in next post if it can be of help to others.
So on my billing page I have:
In my updateSub component, I have:
So the idea is that query will begin to refetch once interval value set to other than false. This will first be triggered if/when a user hits an update component.
On first render of the billing page, a copy of the sub plan will be set to state.
If/when an update component triggers refetch, on each succcesful query the incoming sub plan is checked against the existing in state. If webhook and db were successful, there should be a new sub plan, at which point it differs from the one in state and this re-renders the page and kicks off the code below: setting the interval to false (turning it off) and replacing the sub plan in the state with the new one, allowing the user to update sub plan again to another.
If the refetch has no new sub plan from the db, it continues based on the interval. It seems to be working. What I wanted was to only have the backend talk to the frontend if the user had clicked the update button, and then once the updated information had come through, shut the process down, minimizing excess calls. Does this seem like a good solution to build on? i have yet to take into account possible errors
If the refetch has no new sub plan from the db, it continues based on the interval. It seems to be working. What I wanted was to only have the backend talk to the frontend if the user had clicked the update button, and then once the updated information had come through, shut the process down, minimizing excess calls. Does this seem like a good solution to build on? i have yet to take into account possible errors
Update: the previous solution using a local react component state to store the past user data copy wasn't working as expected. I suspect that the way react batches things together was causing this. I found out that in tanstack that the onSuccess property of the query one can extract the incoming data before it is stored to the variable, and this allows me to make a comparison between the old and new to check for difference ( https://tanstack.com/query/v4/docs/react/reference/QueryCache ). This approach seems to work. See edit in posts above for the updated code. (Also moved the interval state to a context provider and added toast notifications).
QueryCache | TanStack Query Docs
The QueryCache is the storage mechanism for TanStack Query. It stores all the data, meta information and state of queries it contains.
Normally, you will not interact with the QueryCache directly and instead use the QueryClient for a specific cache.