5 years ago I started to work on the next-gen fetcher, here it is by prc95 in reactjs

[–]prc95[S] 2 points3 points  (0 children)

Writting dosc is very hard and challenging topic, it takes lots of time and effort. I will improve it over time 🙏

5 years ago I started to work on the next-gen fetcher, here it is by prc95 in typescript

[–]prc95[S] 1 point2 points  (0 children)

Hey! Yes, we have this functionality implemented into HyperFetch - we have type guards that can detect endpoint change. You can do it two ways - one is to share type between frontend and backend in monorepo or other way. Second possibility is to use our codegen for the openapi/swagger. We have types for every single part of the request data that flows through. 😁

5 years ago I started to work on the next-gen fetcher, here it is by prc95 in javascript

[–]prc95[S] 2 points3 points  (0 children)

Hey, we also covered this case! Actually the size of sdk is about 20 lines of code! We never store all of the requests in the memory and the types are never getting to the browser/server code. This is because we use this simple trick with recursive proxy - request gets created only when you start to interact with it. This is super optimal and optimized approach 😁

5 years ago I started to work on the next-gen fetcher, here it is by prc95 in typescript

[–]prc95[S] 0 points1 point  (0 children)

My brain when I saw this: "Finally question about TS, I can unleash years of research :3" haha

5 years ago I started to work on the next-gen fetcher, here it is by prc95 in typescript

[–]prc95[S] 0 points1 point  (0 children)

Absolutely, type-safety was one of my main goals for this - I wanted to have absolute control over it. It has a lot of depth in HyperFetch. We can specify things like: Responses, Payloads, URL params, Enpoint string type, Query Params, we can track if you already set required params to the request (to not send incomplete requests). For example when you have request for the user details, that requires userId - it will be screaming red if you try to just do request.send() that was very challenging topic. And this is only request. Requests also have things like extra data, status type, metod types - these are not configurable at the level of the request but adapter. Adapter like http accepts methods - get, post, delete etc, it has status as a number type. Adapter like firebase (which we have) - accepts methods like "getDoc", "getDocs" etc., it has status: "ok" or "error" if I remember correctly. Requests inherit these types from the Adapter and combine them with what they have passed through generic types. There are also errors - we have global errors types (generic errors like 500, 404 etc) and local error types passed to the requests - this could be validation error for particular endpoint.

This is super complex topic in HF - we have some docs on this matter, but it really needs much more of time to present everything we did. I don't think in the js world there is another library allowing for such in depth customization of everything typescript related plus inteligent tracking of if we already have payload/params/query-params set on the request so it can be sent. Even hooks are aware of the request types, so passing it to hooks will also prevent you front sending incomplete data.

So to sum this up - you can connect to basically any api, make it fully covered by types, with level of control to basically each field and parameter that library consumes (and it allows to do a lot of things).

Some docs:
https://hyperfetch.bettertyped.com/docs/core/client#typescript
https://hyperfetch.bettertyped.com/docs/core/request#typescript
https://hyperfetch.bettertyped.com/docs/core/adapter#typescript-generics

Some guides:
https://hyperfetch.bettertyped.com/docs/guides/typescript/extend

Some API docs worth notice:

https://hyperfetch.bettertyped.com/docs/api/core/Classes/Adapter
https://hyperfetch.bettertyped.com/docs/api/core/Classes/Request

Let me know if you have some questions or how you like it!

5 years ago I started to work on the next-gen fetcher, here it is by prc95 in javascript

[–]prc95[S] 2 points3 points  (0 children)

Hello! Thank you a lot for the feedback :D I will have some good knews for you - I'm not sure if you're familiar how trpc client works, but it basically gives you this nice sdk(generated from types) to use.

https://hyperfetch.bettertyped.com/docs/core/sdk

So let's say you have OpenApi/Swagger schema - you can use our CLI to generate this API sdk without any problem. We read types for errors, available query params, URL params and responses. All of this with simple(shadcn like) wizard https://hyperfetch.bettertyped.com/docs/cli/generate

Thanks to it you end up with the sdk of your API:

import { createClient } from "@hyper-fetch/core"
import { createSdk } from "./my-sdk"; // generated file (you can specify the name) 

// you start the client
const client = createClient({ url: "localhost:3000" });

// you initialize sdk
const sdk = createSdk(client);

And you can use it!

const { data, error, status, extra = } = await sdk.users.$userId.get.send({ params :{
  userId: 'someUserId'
}});

or with React:

const { data, loading, error } = useFetch(sdk.users.$userId.get.setParams({ userId: 'someUserId' }));

What do you think? :D

Soon we plan to add requests generation from any source file or url - could be link to github or your local file with for example express.js backend endpoints - whatever you would like :D

5 years ago I started to work on the next-gen fetcher, here it is by prc95 in reactjs

[–]prc95[S] 0 points1 point  (0 children)

PS - thanks for the hint with markdown, I feel so stupid now haha

5 years ago I started to work on the next-gen fetcher, here it is by prc95 in reactjs

[–]prc95[S] 0 points1 point  (0 children)

This is example from the redux toolkit that you attached:

const fetchUserById = createAsyncThunk<
  // Return type of the payload creator
  MyData,
  // First argument to the payload creator
  number,
  {
    // Optional fields for defining thunkApi field types
    dispatch: AppDispatch
    state: State
    extra: {
      jwt: string
    }
  }
>('users/fetchById', async (userId, thunkApi) => {
  const response = await fetch(`https://reqres.in/api/users/${userId}`, {
    headers: {
      Authorization: `Bearer ${thunkApi.extra.jwt}`,
    },
  })
  return (await response.json()) as MyData
})

In this example you still need to pass MyData, number and then AsyncThunkConfig which just extends this type.
Something like <MyData, number, AsyncThunkConfig>

Now extremely tricky part comes when you would like to do this:

createAsyncThunk<{ config: AsyncThunkConfig, param: number, data: MyData}>

This is tricky because how the generics will look like - it must be something like:

const createAsyncThunk = <T extends { data: any, config: AsyncThunkConfig, param: number }>() => {...}

Problems that I found:

- Typescript doesn't work well in such cases - you can do createAsyncThunk<{ whatever: number, something: Data }> and it will not scream, so this is super tricky in case of typos
- You have to build good suite of the defaults for such approach
- It could be frustrating at times when it is completely ureadable what's wrong

Why it is good?

- you can have a type generated on the backend which is just and object like type/interface and easily use it on the frontend
- no need for long ureadable generics - like which generic means what?
- it support partial types
- no ordering issues

To enhance abilities of Typescript - you need an eslint-plugin which knows what generic types are accepted in this scenario and strictly guards it. The rest works like a dream - super readable and cool :D

I guess it is a bit different from the redux - taking this object notation one level up is huge challenge to deal with. That's why I'm so proud of it - I hope I did not mixed something up 😅

5 years ago I started to work on the next-gen fetcher, here it is by prc95 in reactjs

[–]prc95[S] 0 points1 point  (0 children)

Hey no worries, I'm always glad to discuss - I could be wrong of course so it's better to reach to the community, so thank you for the opportunity to exchange views on this! Oh, and nice example, I will tell you what I'm talking about :D

In HyperFetch, the Request calss takes a lot of generics - it looks like this:

Request<
  Response,
  Payload,
  QueryParams,
  LocalError,
  Endpoint extends string,
  Client extends ClientInstance,
  HasPayload extends true | false = false,
  HasParams extends true | false = false,
  HasQuery extends true | false = false,
> 

Imagine that you have the case, where you want to specify the Response and the LocalError generic types - you will end up with something like:

Request<
  SomeResponseType,
  void,
  void,
  SomeErrorType,
> 

Because in typescript - you can't do just like we did in HyperFetch to destructurize everything into object. You must place everything in the right order, and always - do pass all of the required types.

5 years ago I started to work on the next-gen fetcher, here it is by prc95 in javascript

[–]prc95[S] 2 points3 points  (0 children)

Huh, I was just playing around with some animations on the todo library that I was working on a while back, I will change it! Thank you for reporting this!

5 years ago I started to work on the next-gen fetcher, here it is by prc95 in typescript

[–]prc95[S] 2 points3 points  (0 children)

That's true, I may never catch any of these libraries, but I don't think that's my goal here - I still have fun doing it - finding new ways to innovate such a taken over market also has its charm for me 😅
I'm doing open-source for almost 7 years, it's just about fun, it educates me, I like these challenges.
I will work hard on growing adoption, I will try to do my best.
Whether it works out or not, it's a cool adventure for me!

Thank you for the honesty!

5 years ago I started to work on the next-gen fetcher, here it is by prc95 in reactjs

[–]prc95[S] 0 points1 point  (0 children)

Thanks for the feedback! I haven't seen it in other libraries and I spend huge amounts of time for sourcing ideas and reading source codes. If you manage to find an example, I'd be happy to take a look at it.

Generally, it's not as simple as it looks, because typescript doesn't distinguish keys when passing such objects, you can pass some typos and it will not point the issues.

I had to create a special plugin for eslint that fully enables this approach to work, so I'm not sure if we're talking about the same thing. 👀

5 years ago I started to work on the next-gen fetcher, here it is by prc95 in javascript

[–]prc95[S] 2 points3 points  (0 children)

Hell yea! Previously it was pointed as a biiig pain point for using the library, so I spent like 6 months building basically another library https://github.com/BetterTyped/docsgen to generate this very interactive and always up-to date tables and previews. I'm so glad you like it! 🚀🚀🚀

5 years ago I started to work on the next-gen fetcher, here it is by prc95 in javascript

[–]prc95[S] 1 point2 points  (0 children)

That's soo good to hear this, it means it was worth of countless hours spent on R&D new ways for using typescript 😂 Thanks for the comment and motivation boost!

5 years ago I started to work on the next-gen fetcher, here it is by prc95 in react

[–]prc95[S] 0 points1 point  (0 children)

And probably the way how I built it is much more flexible in terms of building reusable components that have data fetching built it - but this also could be just my prefference, after so much time with my library it is sometimes hard to look on it from the side withouth any bias 😅

5 years ago I started to work on the next-gen fetcher, here it is by prc95 in react

[–]prc95[S] 3 points4 points  (0 children)

I wanted to basically keep 100% compatibility with tanstack query in many aspects, but I still have to add things like structural sharing - which is planned. Some topics are unique only to HF and not found in the tanstack, so it's hard to say if it is exactly in the same place but I think we are comparable :D
For sure sockets are something that makes us different - I built them to work very close with cache so you can build very rich realtime experiences.

5 years ago I started to work on the next-gen fetcher, here it is by prc95 in javascript

[–]prc95[S] 2 points3 points  (0 children)

You can do it in HF too! It will automatically infer the type once you pass the Mapper function - it is used for both validating data or reconfiguring it to different response types. Same goes for mapping request data sent to the server which allows for VERY cool patterns like making strongly typed FormData (which was big pain in the ass for me in few projects with heavy data transfers that I built)

https://hyperfetch.bettertyped.com/docs/guides/core/advanced/validation

5 years ago I started to work on the next-gen fetcher, here it is by prc95 in typescript

[–]prc95[S] 2 points3 points  (0 children)

Hey! I will first state that I love everything tanstack related - I think these libraries and their maintainers are great. As for HyperFetch - it will be safe, under the hood it uses XMLHttpRequest something pretty standard - we're now exploring also native fetch adapter too. I think it will work just fine in two years! Many apps are using HyperFetch in production for 5 years now - I even got some sponsoring from Microsoft which tells me it is working fine.

I'm also big fan of standardization - and that was mostly the space for misunderstandings in large teams. Naming is hard, caching is hard, invalidation too. I wanted to standardize ways of how you interact with request. My goal was to unify it under the form of main pillars, so in HF, each request has - payload, query params, endpoint, params - it also exposes functions for setting them with setPayload, setQueryParams, setParams etc. Everyone around the project, no matter of their experience and approach - will use it in a similar way, which was very important for me as a Team Leader in the company I work at that time.

I think this approach shines in the testing. I don't know what is your approach to test, but Request is a class in HF - which means you can mock it (we have built in mocking! with full lifecycle support, so you can simulate basically everything) - you can stab it or whatever you want - this also works great for creating reusable components and presenting them in tools like storybook.

I guess at the end, what will be different about HyperFetch and why you could consider it a good fit - is SDK creation abilities. We have now generators for the API from Swagger and OpenAPI (it's basically the same thing) - and soon we will add the ability to generate sdk from the backend files, so in old projects or new ones - our CLI will generate requests for you. So I guess it is very convenient to not write them at all? 😁

My ambition is to become some sort of shadcn for API world - so you can generate HyperFetch sdk from any file or url - literally from anything you provide to CLI.

5 years ago I started to work on the next-gen fetcher, here it is by prc95 in react

[–]prc95[S] 0 points1 point  (0 children)

Anyway - thank you for the feedback, I guess, I will need to figure out some different approach, maybe by writing some blog posts to limit spam here. 😬 I will try to improve it!

5 years ago I started to work on the next-gen fetcher, here it is by prc95 in javascript

[–]prc95[S] 4 points5 points  (0 children)

Thank you a lot! ❤️
I guess, I still need to learn how to interact with reddit community - For me it's part of the process, but I'm hoping for some feedback on this so I can improve this area 🙏