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

Proposal to change the API #4

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

ThisIsMissEm
Copy link

Currently the GraphQLDataSource package kind of follows RESTDataSource, but also uses ApolloLink infrastructure. It'd be great to have this basically like using ApolloClient, e.g.

class MyApi extends GraphQLDataSource {
  constructor() {
    this.link = ApolloLink.from([
      onError,
      createHttpLink({
        fetch,
        uri: "https://swapi/graphql"
      })
    ])

    super();
  }

  fetchStarships(type) {
    return this.query(gql`...`, ...);
  }
}

This would allow custom error handling, usage of batching, retries, etc.

This commit also adds some missing types and raises several questions.

Currently the GraphQLDataSource package kind of follows RESTDataSource, but also uses ApolloLink infrastructure. It'd be great to have this basically like using ApolloClient, e.g.

class MyApi extends GraphQLDataSource {
  constructor() {
    this.link = ApolloLink.from([
      onError,
      createHttpLink({
        fetch,
        uri: "https://swapi/graphql"
      })
    ])

    super();
  }

  fetchStarships(type) {
    this.query(gql`...`, ...);
  }
}

This would allow custom error handling, usage of batching, retries, etc.

This commit also adds some missing types and raises several questions.
import { DocumentNode } from 'graphql';
import fetch from 'isomorphic-fetch';
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, and I've made this change because people may want to bring their own fetch() (e.g., if you're using this in a serverless or Cloudflare-worker environment, you will already have a fetch method available.

private async execute(operation: GraphQLRequest) {
try {
return await makePromise(execute(this.link, operation));
} catch (error) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using try/catch instead of await-to-js lightens the dependencies and keeps the code still readable


private async execute(operation: GraphQLRequest) {
try {
return await makePromise(execute(this.link, operation));
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally once we go towards caching, we could do something like what Apollo Client does here: https://github.com/apollographql/apollo-client/blob/master/packages/apollo-client/src/core/QueryManager.ts#L265-L307

if (this.willSendRequest) {
this.willSendRequest(request);
await this.willSendRequest(request);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

async this.willSendRequest to be inline with the protocol/types of DataSource and RESTDataSource

}

return request;
});
}

private onErrorLink() {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be handled by the user, as each user is like to have different error reporting requirements.


protected willSendRequest?(request: any): any;

private composeLinks(): ApolloLink {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to do this every request, as the ApolloLink doesn't persist any state, so we can do it once per construction, rather than every request.

}

public async mutation(mutation: DocumentNode, options: GraphQLRequest) {
// GraphQL request requires the DocumentNode property to be named query
return this.executeSingleOperation({ ...options, query: mutation });
return this.execute({ ...options, query: mutation });
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

taking request from Apollo Client's code

type ValueOrPromise<T> = T | Promise<T>;
type RequestOptions = Record<string, any>;

export class GraphQLDataSource<TContext = any> extends DataSource {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extending DataSource for inheritance and compatibility


private resolveUri(): string {
const baseURL = this.baseURL;
protected willSendRequest?(request: RequestOptions): ValueOrPromise<void>;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these are the correct Type signatures.

const baseURL = this.baseURL;
protected willSendRequest?(request: RequestOptions): ValueOrPromise<void>;

private async execute(operation: GraphQLRequest) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably need an operation build, ala, Apollo Client, where for example we can auto-set a default operationNamehttps://github.com/apollographql/apollo-client/blob/master/packages/apollo-client/src/core/QueryManager.ts#L1255-L1284

      operationName: getOperationName(document) || undefined,

@kmills006
Copy link
Contributor

@ThisIsMissEm - Hey! Thank you for this, I apologize for the delay. I will review your proposed changes this week, at first glance I'm super stoked to see what you have done!

@hkjorgensen
Copy link

hkjorgensen commented Dec 21, 2018

@ThisIsMissEm @kmills006 can we make this happen? This is the first/only graphql datasource for ApolloServer - would love to contribute to this instead of rolling my own DataSource.

I think the proposed changes are great and would love to see them merged. Then add resolveURL() and some tests. It all depends on if this PR get's merged.

return response;
}

private resolveUri(): string {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ThisIsMissEm
Copy link
Author

ThisIsMissEm commented Dec 21, 2018 via email

@yaacovCR
Copy link

yaacovCR commented Apr 4, 2019

Novice programmer here, made a package to use datasource pattern for graphql endpoints with Apollo Client cache for stitching, would love comments/suggestions on the approach, the code, whatever, etc, etc.

Documentation: https://yaacovcr.github.io/apollo-stitcher
Working example: https://github.com/yaacovCR/nextjs-graphql-starter

I am not too familiar with TypeScript, much of this could probably be reoriented by those more in the know as pull requests to graphql-tools and/or this package.

@Enalmada
Copy link

@kmills006 These changes seem like they would really benefit the community and set the stage for more people to be able to contribute in the right direction. Given that the author isn't in a position to merge, could you finish the effort?

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

Successfully merging this pull request may close these issues.

5 participants