Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

header in datasource-rest #49

Closed
devdudeio opened this issue Mar 31, 2020 · 5 comments · Fixed by #110
Closed

header in datasource-rest #49

devdudeio opened this issue Mar 31, 2020 · 5 comments · Fixed by #110

Comments

@devdudeio
Copy link

devdudeio commented Mar 31, 2020

How can I read the header via apollo-datasource-rest from a response? I have a question posted on stackoverflow but no luck so far

https://stackoverflow.com/questions/60764956/read-header-from-response-with-apollo-datasource-rest

@renatocassino
Copy link

I have the same problem :/
I "solved" with a horrible solution using override in method didReceiveResponse. I returned a Promise instead of TResult (using typescript) and in this promise, I got the content and return request and result

didReceiveResponse<TResult = any>(response: any, request: Request): Promise<any> {   
    const parent = super.didReceiveResponse.bind(this);

    return new Promise(async (resolve) => {
      const body = await parent<TResult>(response, request);

      return resolve({
        body,
        req: request,
      })
    });
  }

But there are several problem in this solution :/

Isnt possíble to override a syncronous method with an asyncronous method (this explains why I returned a Promise, instead of use an async override)
The generics for methods get, post, put, patch and delete have no sense anymore, because, in these contracts, the return is the generics.

const { req, body } = await this.get<MyType>('/uri'); // Error
// I changed to

const { req, body } = await this.get('/uri');

return body as MyType;

Too many problems, but works for me
I'm waiting for a better solution

@renatocassino
Copy link

renatocassino commented Mar 6, 2021

I refactored this and made a better solution. I created an interface like Axios response and overloaded the methods get, put, delete, patch and post.

// Interface with response, like axios
interface ResponseInterface<T = any>  {
  data: T;
  status: number;
  headers: any;
  request?: any;
}

class BaseAPIDataSource extends RESTDataSource {
  didReceiveResponse(response: any, request: Request): Promise<any> {
    // I'm getting some metrics here to prometheus
    return this.formatResponse(response, request);
  }

  async formatResponse(response: any, request: Request): Promise<ResponseInterface> {
    if (response.status >= 500) {
      await super.didReceiveResponse(response, request);
    }
    const data = await this.parseBody(response);
    return {
      data,
      status: response.status,
      headers: response.headers,
      request,
    };
  }

  // Now I'm overloading the parent methods only changing the type response
  
  protected async get<TResult = any, R = ResponseInterface<TResult>>(
    path: string,
    params?: URLSearchParamsInit,
    init?: RequestInit,
  ): Promise<R> {
    return super.get(path, params, init);
  }

  protected async put<TResult = any, R = ResponseInterface<TResult>>(
    path: string,
    body?: Body,
    init?: RequestInit,
  ): Promise<R> {
    return super.put(path, body, init);
  }

  protected async post<TResult = any, R = ResponseInterface<TResult>>(
    path: string,
    body?: Body,
    init?: RequestInit,
  ): Promise<R> {
    return super.post(path, body, init);
  }

  protected async patch<TResult = any, R = ResponseInterface<TResult>>(
    path: string,
    body?: Body,
    init?: RequestInit,
  ): Promise<R> {
    return super.patch(path, body, init);
  }

  protected async delete<TResult = any, R = ResponseInterface<TResult>>(
    path: string,
    params?: URLSearchParamsInit,
    init?: RequestInit,
  ): Promise<R> {
    return super.delete(path, params, init);
  }
}

The usage now is like Axios and the type validation works

  const { data, headers, status } = await this.get<MyCustomType>('/my/path', { page: 1 });

  console.log(data, headers, status);

@glasser
Copy link
Member

glasser commented Mar 8, 2021

Does overriding parseBody (which is protected) help here?

class MyAPI extends RESTDataSource {
  protected parseBody(response: Response): Promise<object | string> {
    return super.parseBody(response).then(result => {
      return augmentResultWithHeaders(result, response.headers);
    });
  }
}

Or is that tough because parseBody doesn't know which particular API it is implementing?

@devdudeio
Copy link
Author

I can highly recommend to switch to your own DataSource and use axios instead of using the RESTDataSource. Its giving you more flexibility in the long run.

@StarpTech
Copy link

StarpTech commented Jun 25, 2021

If you need a more flexible data source you could try https://github.com/StarpTech/apollo-datasource-http. It's also listed here https://www.apollographql.com/docs/apollo-server/data/data-sources/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants