blitz-js / legacy-framework

MIT License
3 stars 2 forks source link

Client-side middlewares #41

Open sirmyron opened 3 years ago

sirmyron commented 3 years ago

What do you want and why?

I want to suggest supporting client-side middlewares (or maybe interceptors is a better way to think about it) that work in a similar way that the server middlewares do and allow you to modify requests/responses during the client-side useQuery executions.

My specific scenario is that I want to add a JWT token to requests that will be used in subsequent requests on the backend inside some queries/mutations.

Possible implementation(s)

The middlewares could be executed inside the rpc file that handles the client-side requests to the queries/mutations.

Additional context

sirmyron commented 3 years ago

Gave this some more thought and I think "interceptors" may be a better name than "middleware" as it more aligns with how things work on the client side, particularly because we'd have to separate the request from the response.

Here's the proposal I have in mind for changes to the RPC call:

export const executeRpcCall = <TInput, TResult>(
  apiUrl: string,
  params: TInput,
  opts: RpcOptions = {},
) => {

    // ORIGINAL CODE, currently line 31 - 50

    // NEW CODE, before serializing params
    const {
        headers: interceptedHeaders,
        params: interceptedParams
    } = invokeRequestInterceptorChain(headers, params, opts)

    // optional safeguard
    const updatedHeaders = interceptedHeaders || headers
    const updatedParams = interceptedParams || params

    // // ORIGINAL CODE, currently line 52
    let serialized = serialize(updatedParams)

    // ORIGINAL CODE

    const promise = window
        .fetch(apiUrl, {
          method: "POST",
          headers: updatedHeaders, // NEW CODE, currently line 69
          credentials: "include",
          redirect: "follow",
          body: JSON.stringify({
            params: serialized.json,
            meta: {
              params: serialized.meta,
            },
          }),
          signal: controller.signal,
        })
        .then(async (result) => {
          clientDebug("Received request for", apiUrl)
          if (result.headers) {

           // ORIGINAL CODE, currently line 81 - 119

            throw error
          } else {

            // ORIGINAL CODE, currently line 123 - 131

            // NEW CODE, currently line 132
            const {
                data: updatedData,
                params: interceptedParams
            } = invokeResponseInterceptorChain(headers, data)

            return (updatedData || data) as TResult
          }
        }) as CancellablePromise<TResult>

  // Disable react-query request cancellation for now
  // Having too many weird bugs with it enabled
  // promise.cancel = () => controller.abort()

  return promise
}

Some additional points:

The thing left to figure out is how we get the handlers from the blitz.config.js and into the client side bundle.