reduxjs / redux-toolkit

The official, opinionated, batteries-included toolset for efficient Redux development
https://redux-toolkit.js.org
MIT License
10.66k stars 1.16k forks source link

Enhancement: Utility to get data on all available endpoints #2367

Closed tuzmusic closed 1 year ago

tuzmusic commented 2 years ago

It's useful to have urls for API endpoints stored as constants in an app, so that they can be reused safely. For example, I might use API.getSomeResource.url as the query property in an RTK Query endpoint, and also use API.getSomeResource.url as the url argument for mocking with, say, MirageJS, cy.intercept (in Cypress.io), or an other mocking framework.

Since the baseApi winds up being a catalog of all the endpoints used, it would be very helpful if it exposed information about those endpoints. There is indeed already baseApi.endpoints but it doesn't include this data.

Perhaps baseApi.endpoints could add the following fields: (excuse the imprecise type annotations)

type ExtendedEndpointLogic = EndpointLogic & {
  query: typeof createApi.endpoints[].query
  method: RESTMethod // "get", "post", etc.
  // and perhaps even!
  transformResponse: typeof createApi.endpoints[].transformResponse
}

(Indeed, query would be a function)

phryneas commented 2 years ago

That data is the result of calling query with different arguments, so it would be different for every cache entry of an endpoint, not only the endpoint itself. That can't be statically exposed per endpoint.

tuzmusic commented 2 years ago

That data is the result of calling query with different arguments, so it would be different for every cache entry of an endpoint, not only the endpoint itself. That can't be statically exposed per endpoint.

But the query function is always the same, and it's the function (not its return value) that would be exposed.

Before:

// file #0
export const API = {
  urls: {
    getPostsById: id => `/posts/${id}`,
  }
}

// file #1
import API from "./constants"
import baseApi from "./services/api"

baseApi.injectEndpoints({
  endpoints: build => ({
    getPostById: build.query({
      query: id => API.urls.getPostById(id), // or, query: API.urls.getPostById
    }),
  })
})

// file #2

import API from "./constants"
import mockServer from "./útil/mirage"

function mockGetPost(id) {
  mockServer.get(
    API.urls.getPostById(id),  // url
    { id, title: `post ${id}` } // response
  )
}

Note that API.urls is defined elsewhere and consumed in both places.

After:

// file #1
import baseApi from "./services/api"

baseApi.injectEndpoints({
  endpoints: build => ({
    getPostById: build.query({
      query: id => `/posts/${id}`,
    }),
  })
})

// file #2
import baseApi from "./services/api"
import mockServer from "./útil/mirage"

function mockGetPost(id) {
  mockServer.get(
    baseApi.endpoints.getPostById.query(id),  // url
    { id, title: `post ${id}` } // response
  )
}

The query function from the RTK Query API is consumed in file #1. The RTK Query API becomes the single source of truth about the URLs (or, rather, URL creators).

I suppose I should mention that my current project doesn't have an API constants file, so this functionality would prevent me having to create one 😇