Comments (7)
@toteto yeah, familiar situation š . IMO it's easier to remap your understanding of "errors" than to fight with tooling.
onError
corresponds to code/client error.
onSuccess
corresponds to the absence of the above errors.
From the client point of view 404, 500, etc. response status codes are not errors but expected scenarios. So you handle them in the same branch as 200.
If you think about it like this, then everything behaves as expected.
from ts-rest.
@ivan-kleshnin there are .query()
and .mutation()
functions exposed that will fetch directly. So you can do await client.getPost.query()
from ts-rest.
Thank you for your proposal! Pretty well thought out, but you've overlooked a major point, which is why @ts-rest/react-query exists in the first place, and which solves this entire use-case, which I'll elaborate on later.
The thing about ts-rest is that we try to make its usage as convenient as possible while being as unopinionated as much as possible. I believe we have struck this right balance.
Our core client uses fetch
which does not throw on error response codes, therefore we do not. fetch
is the standard now on the web, so we are sticking to its conventions. It's not really in our philosophy to be creating custom clients ourselves with elaborate error handling or features such as middleware, or auth or whatever since there are many different use-cases that we cannot cover all, or some, without being opinionated.
A such, we have provided functionality in order to allow everyone to customize the "client" to behave however they want it, or even swap out fetch
entirely if they want to. This is the only way we can guarantee that ts-rest works for everyone.
In your case you can just simply wrap the tsRestFetcher and do the error handling in a custom fetcher.
const client = initQueryClient(postsApi, {
baseUrl: 'http://localhost:5003',
baseHeaders: {},
api: async (args) => {
const response = await tsRestFetchApi(args);
if (!String(result.status).startsWith('2')) {
throw response;
// or wrap with your own error, whatever
}
return response;
},
});
Now for react query, @ts-rest/react-query
mainly exists as a typing wrapper rather than providing extra runtime functionality. If you opt to use just react-query
then you will need to do lots of manual typing work regardless of if you use your own custom fetcher or if we theoretically used a newly proposed ts-rest client.
const { mutate } = useMutation<
ClientInferResponses<typeof contract.addPost, SuccessfulHttpStatusCode, 'force'>,
ClientInferResponses<typeof contract.addPost, ErrorHttpStatusCode, 'ignore'> // or whatever you wrap the error with
>({
mutationFn: (newPost) => client.addPost(newPost),
onSuccess: (response) => {
},
onError: (error) => {
// or type guard here instead of using the type generics
},
});
For the error typing you will still have to either manually type useMutation
or useQuery
's second generic or type guard the error every single time. It will get very repetitive.
Anyway it seems that your problem with using @ts-rest/react-query
is something very specific and it's hard to justify building extra functionality that will take development effort to maintain just to cover a very specific use-case.
However, the types and functions to assist with building this functionality yourself is already there.
from ts-rest.
@Gabrola
Thank you for your prompt response, and I apologize for the delay in my reply. I missed the notification in my inbox.
Regarding my usage of @ts-rest/react-query
, I initially misunderstood an implementation detail: this client actually only allows 2xx responses in the onSuccess
callback. This behavior aligns with my requirements for a standalone client.
While @ts-rest/react-query
generally meets my needs, there are specific cases where I encounter challenges. For instance, in a single useMutation.mutationFn
, I sometimes need to perform multiple steps during the mutation process. Additionally, the error type in orError
is currently incorrect; it should handle any thrown errors, not just error status responses.
Putting aside my personal preferences and specific use case, I wonder if there's a way to engage the community and discuss the desired behavior of the core client.
One observation that leads me to believe that throwing errors might be more desirable is the implementation in @ts-rest/react-query
. Furthermore, many developers prefer the API of axios
over fetch
.
Iād appreciate any insights or thoughts on PR #522 whenever you have a moment.
As a temporary solution, I've created a utility function in my code that can be chained to the response promise. This utility returns the success response but throws an error response when necessary:
export function successBody<TStatus extends SuccessfulHttpStatusCode, TRes extends Res>(
response: TRes,
status?: TStatus,
): (TRes & { status: TStatus extends undefined ? SuccessfulHttpStatusCode : TStatus })['body'] {
if (status != null ? response.status === status : response.status >= 200 && response.status < 300)
return response.body;
throw new TsRestError(response);
}
const { status, body: posts } = client.getPosts({...}).then(successBody);
// status is 2xx, posts is of type Post[]
// Wrap in a try-catch block to handle errors with status codes outside the 2xx range
However, I'm not particularly fond of this approach, as I need to remember to include it in every method call.
from ts-rest.
@toteto I left you some comments on the PR. However, I still don't believe much in the utility of throwing on fetch
except to trigger an error in tanstack query. Otherwise, it's pretty much the same amount of code; whether you wrap in try/catch
, or if you just create an if
condition to check for the success status. This leads me to believe, that this change only exists to cover the use-case of wanting to use tanstack query but not wanting to use the ts-rest wrappers.
Even if you were to manually use tanstack query, all of the new types in the regular client are still useless because you still have to manually type the error object, and since the error type is the second generic on useQuery
, etc. you will still have to manually pass the success type and error type every single time you use useQuery
or useMutation
.
Regarding not using @ts-rest/react-query
, if you need to perform any steps before running a mutation, then simply just run these steps before calling mutate
.
I cannot justify including code for a feature, just to cover a very specific use-case that has not been requested by anyone else yet.
from ts-rest.
And yes, regarding the incorrectly typed error
object in @ts-reset/react-query
, I am aware of that, however it is too late to fix now without a breaking change. So we'll be fixing it in v4
from ts-rest.
@Gabrola is it possible to use a client created with initQueryClient
for direct (hook-less) fetches? I mean:
const client = initQueryClient(...)
client.getPost.useQuery() // -- does work
// then, at some place in code, I need raw `client.getPost()`
await client.getPost() // -- is not a function, any alternative syntax?
Should I create 2 clients: one for higher- and another for lower-level API handling?
Will they have a single shared cache in that case?
I use React-Query but it's often necessary to make direct calls, without useQuery
wrapper ā be it a server-side prefetchQuery()
or another necessity.
from ts-rest.
Related Issues (20)
- useQuery or useInfiniteQuery which returns a promise. HOT 2
- Custom client API causes hooks order error HOT 2
- Express middleware is not fired HOT 1
- ts-rest fastify integration code crashes server on invalid JSON body HOT 1
- Feature: allow Zod validation to run for client-only contract implementations HOT 1
- Split a router in multiple files or variables HOT 5
- Export `TsRestException` in `@ts-rest/express`
- Providing header in `extraHeaders` causes TS error of: `Type 'string' is not assignable to type 'undefined'.`
- content-type header is overwritten
- @ts-rest/nest attempting to call an object as a function HOT 2
- Using `z.void` fails to parse empty `req.body`
- Feature request: HEAD requests
- Having `void` responses causes response parsing to fail HOT 1
- ts-rest Middleware called after specific Route HOT 7
- Missing docs for `@ts-rest/serverless`
- Replace `c.noBody()` with `z.void()`/`c.type<void>` for empty request/response body HOT 1
- More detailed error context HOT 2
- Vue Query v5.x.x.x HOT 2
- Add support for multipart/form-data file array
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
š Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. ššš
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ā¤ļø Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from ts-rest.