rtk-incubator / rtk-query

Data fetching and caching addon for Redux Toolkit
https://redux-toolkit.js.org/rtk-query/overview
MIT License
626 stars 31 forks source link

Create Instance fetchBaseQuery ? #179

Closed fera765 closed 3 years ago

fera765 commented 3 years ago

Good Morning :)

I'm really enjoying this RTK-Query project, I have a doubt about declaring the application's api url.

I created an api.ts file and inside it I assembled the logic of fetchBaseQuery together with retry.

And within each createApi I call the reference of the api.ts file ....

This worked for me, but is it good practice?

api.ts

    import { fetchBaseQuery, retry } from '@rtk-incubator/rtk-query';

    // Create our baseQuery instance
    const baseQuery = fetchBaseQuery({
      baseUrl: 'http://localhost:3030',
    });

    export default retry(baseQuery, { maxRetries: 6 });

posts.ts

import apiInstace from './api';

export const postApi = createApi({
  reducerPath: 'postsApi', 
  baseQuery: apiInstace,
  entityTypes: ['Posts'],
  endpoints: build => ({
    getPosts: build.query<PostsResponse, void>({
      query: () => ({ url: 'posts' }),
      provides: result => [
        ...result.map(({ id }) => ({ type: 'Posts', id } as const)),
        { type: 'Posts', id: 'LIST' },
      ],
    }),
    addPost: build.mutation<Post, Partial<Post>>({
      query: body => ({
        url: `posts`,
        method: 'POST',
        body,
      }),
      invalidates: [{ type: 'Posts', id: 'LIST' }],
    }),
    getPost: build.query<Post, number>({
      query: id => `posts/${id}`,
      provides: (_, id) => [{ type: 'Posts', id }],
    }),
    updatePost: build.mutation<Post, Partial<Post>>({
      query(data) {
        const { id, ...body } = data;
        return {
          url: `posts/${id}`,
          method: 'PUT',
          body,
        };
      },
      invalidates: (_, { id }) => [{ type: 'Posts', id }],
    }),
    deletePost: build.mutation<{ success: boolean; id: number }, number>({
      query(id) {
        return {
          url: `posts/${id}`,
          method: 'DELETE',
        };
      },
      invalidates: (_, id) => [{ type: 'Posts', id }],
    }),
    getErrorProne: build.query<{ success: boolean }, void>({
      query: () => 'error-prone',
    }),
  }),
});

home.ts

import apiInstace from './api';

export const homeApi = createApi({
  reducerPath: 'homesApi',
  baseQuery: apiInstace,
  entityTypes: ['Home'],
  endpoints: build => ({
    getPosts: build.query<PostsResponse, void>({
      query: () => ({ url: 'home/posts' }),
      provides: result => [
        ...result.map(({ id }) => ({ type: 'Home', id } as const)),
        { type: 'Home', id: 'LIST' },
      ],
    }),
    addPost: build.mutation<Post, Partial<Post>>({
      query: body => ({
        url: `home/posts`,
        method: 'POST',
        body,
      }),
      invalidates: [{ type: 'Home', id: 'LIST' }],
    }),
    getPost: build.query<Post, number>({
      query: id => `home/posts/${id}`,
      provides: (_, id) => [{ type: 'Home', id }],
    }),
    updatePost: build.mutation<Post, Partial<Post>>({
      query(data) {
        const { id, ...body } = data;
        return {
          url: `home/posts/${id}`,
          method: 'PUT',
          body,
        };
      },
      invalidates: (_, { id }) => [{ type: 'Home', id }],
    }),
    deletePost: build.mutation<{ success: boolean; id: number }, number>({
      query(id) {
        return {
          url: `home/posts/${id}`,
          method: 'DELETE',
        };
      },
      invalidates: (_, id) => [{ type: 'Home', id }],
    }),
  }),
});
phryneas commented 3 years ago

I would recommend that you should only have one createApi for your whole application with all endpoints, when they share the same server and the same database. There is no value in splitting that up and stuff like invalidation would stop working at that point.

fera765 commented 3 years ago

I would recommend that you should only have one for your whole application with all endpoints, when they share the same server and the same database. There is no value in splitting that up and stuff like invalidation would stop working at that point.createApi

More if I have an application where there will be a lot of sessions ..

http://minhaapi:3030/posts http://minhaapi:3030/profile http://minhaapi:3030/user http://minhaapi:3030/page http://minhaapi:3030/page/posts http://minhaapi:3030/follower

it's over there..........

Let's say it was an api of a Social Network, looking at this project it would not be better to do the division of responsibility, to have something very separate and easy to maintain

If you put all this rule within a single endpoint, it could be a very large file and difficult to maintain

phryneas commented 3 years ago

No, that's exactly the point of it. You modify something in user? Might need to reload profile as a consequence of that. Write a new post? That means something in posts needs to reload, something in page, something in page/posts, maybe even profile? That stuff is interconnected. If it's the same api, the invalidation feature can do all that for you automatically. If you split it up, all those apis are separated and cannot influence each other.

And as for much code: if you have an OpenAPI API specification, you can get that generated automatically. Take a look at https://github.com/rtk-incubator/rtk-query-codegen This is the OpenAPI spec and this is the generated code. So you don't necessarily need to write that by hand.

Also, you can split up one api into multiple files if you really want to, see the docs on code splitting

phryneas commented 3 years ago

Also, notice that the 15 endpoints in that one file are not much more code than one endpoint in vanilla redux? There's really no harm putting a lot of that stuff in one file from that perspective ;)