• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

ember-graphql/ember-apollo-client:

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称(OpenSource Name):

ember-graphql/ember-apollo-client

开源软件地址(OpenSource Url):

https://github.com/ember-graphql/ember-apollo-client

开源编程语言(OpenSource Language):

JavaScript 91.6%

开源软件介绍(OpenSource Introduction):

ember-apollo-client

Use @apollo/client and GraphQL from your Ember app.

Download count all time npm version Build Status Ember Observer Score

This addon is battle tested: it has been used to build several large apps. As such, we've solved real-world problems such as reliable testing and preventing resource leaks by unsubscribing from watch queries.

Installation

ember install ember-apollo-client

This should also automatically install ember-fetch and graphql.

Install the Apollo Client Developer tools for Chrome for a great GraphQL developer experience!

Compatibility

  • Apollo Client v3.0 or above
  • Ember.js v3.24 or above
  • Ember CLI v2.13 or above
  • Node.js v12 or above
  • FastBoot 1.0+

For compatibility with Ember versions below 3.4, use version 1.x. For compatibility with Apollo Client v1 or v2, use version 1.x or 2.x of this addon.

Configuration

Runtime configuration

In your app's config/environment.js, configure the URL for the GraphQL API.

let ENV = {
  ...
  apollo: {
    apiURL: 'https://test.example/graphql',
    // Optionally, set the credentials property of the Fetch Request interface
    // to control when a cookie is sent:
    // requestCredentials: 'same-origin', // other choices: 'include', 'omit'
  },
  ...
}

Additional configuration of the ApolloClient can be done by extending the Apollo service and overriding the clientOptions property. See the Apollo Service API for more info.

Build time configuration

In your app's ember-cli-build.js, you can set build time options for broccoli-graphql-filter to keep or remove file extensions in .graphql files.

module.exports = function(defaults) {
  let app = new EmberApp(defaults, {
    emberApolloClient: {
      keepGraphqlFileExtension: false
    }
  });

  return app.toTree();
};

keepGraphqlFileExtension = true, defaults to true – If false, creates files called my-query.js instead of my-query.graphql.js, so that you can import them as ./my-query instead of ./my-query.graphql.

Example:

import myQuery from 'my-app/queries/my-query.graphql';

Dependencies

This addon uses ember-auto-import to import dependencies.

This addon does not exposes any dependencies directly to your application, so if you desire any additional graphql or apollo dependencies, install them with npm/yarn and import as desired.

Here are some useful packages:

Make sure to use ember-auto-import in your application to import these additional packages.

Peer Dependencies

This addon has a peer dependency of:

Usage

Fetching data

GraphQL queries should be placed in external files, which are automatically made available for import:

app/gql/queries/human.graphql

query human($id: String!) {
  human(id: $id) {
    name
  }
}

You can also use the graphql-tag package to write your queries within your JS file:

import gql from "graphql-tag";

const query = gql`
  query human($id: String!) {
    human(id: $id) {
      name
    }
  }
`;

Note: Inline queries like the one above are compiled at runtime. This is both slower than external files (which are precompiled) and involves shipping extra dependencies in your vendor.js. For the time being, we recommend using external files for your queries.

If you are looking for an opportunity to contribute, enabling precompilation of inline queries would be a fantastic feature to work on.

Within your routes, you can query for data using the queryManager computed macro and watchQuery:

app/routes/some-route.js

import Route from "@ember/routing/route";
import { queryManager } from "ember-apollo-client";
import query from "my-app/gql/queries/human.graphql";

export default Route.extend({
  apollo: queryManager(),

  model(params) {
    let variables = { id: params.id };
    return this.apollo.watchQuery({ query, variables }, "human");
  }
});

This performs a watchQuery on the ApolloClient. The resulting object is a POJO.

If a subsequent query (such as a mutation) happens to fetch the same data while this query's subscription is still active, the object will immediately receive the latest attributes (just like ember-data).

Please note that when using watchQuery, you must unsubscribe when you're done with the query data. You should only have to worry about this if you're using the Apollo service directly. If you use the queryManager computed macro in your routes, or in your data-loading components or class that extend Ember.Object, all active watch queries are tracked and unsubscribed when the route is exited or the component and Ember.Object is destroyed.

You can instead use query if you just want a single query with a POJO response and no watch updates.

If you need to access the Apollo Client ObservableQuery, such as for pagination, you can retrieve it from a watchQuery result using getObservable:

import Route from "@ember/routing/route";
import { getObservable, queryManager} from "ember-apollo-client";

export default Route.extend({
  apollo: queryManager(),

  model() {
    let result = this.apollo.watchQuery(...);
    let observable = getObservable(result);
    observable.fetchMore(...) // utilize the ObservableQuery
    ...
  }
});

See the detailed query manager docs for more details on usage, or the Apollo service API if you need to use the service directly.

GraphQL Subscriptions

GQL Subscriptions allow a client to subscribe to specific queries they are interested in tracking. The syntax for doing this is similar to query / watchQuery, but there are a few main differences:

  • you must define a subscription (versus a query or mutation)
  • because subscriptions are async by nature, you have to listen for these events and act accordingly.
  • subscriptions require websockets, so must configure your link accordingly

Creating your subscription

app/gql/subscriptions/new-human.graphql

subscription {
  newHuman() {
    name
  }
}

Subscribing from inside a route

app/routes/some-route.js

import Route from '@ember/routing/route';
import { queryManager } from 'ember-apollo-client';
import query from 'app/gql/subscriptions/new-human';
import { addListener, removeListener } from '@ember/object/events';

const handleEvent = event => alert(`${event.name} was just born!`);

export default Route.extend({
  apollo: queryManager(),

  model() {
    return this.get('apollo').subscribe({ query }, 'human');
  },

  setupController(controller, model) {
    addListener(model, 'event', handleEvent);
  },

  resetController(controller, isExiting, transition) {
    if (isExiting) {
      removeListener(controller.model, 'event', handleEvent);
    }
  }
});

The big advantage of using the queryManager is that when you navigate away from this route, all subscriptions created will be terminated. That said, if you want to manually unsubscribe (or are not using the queryManager) subscription.unsubscribe() will do the trick.

Enabling Websockets

While this library should work w/ any back-end implementation, here's an example with Authenticated Phoenix + Absinthe:

my-app/services/apollo.js

import ApolloService from 'ember-apollo-client/services/apollo';
import { inject as service } from '@ember/service';
import { Socket } from 'phoenix';
import { createAbsintheSocketLink } from '@absinthe/socket-apollo-link';
import AbsintheSocket from '@absinthe/socket';

class OverriddenApollo extends ApolloService {
  @service
  session;

  link() {
    const socket = new Socket("ws://socket-url", {
      params: { token: this.get('session.token') },
    });
    const absintheSocket = AbsintheSocket.create(socket);

    return createAbsintheSocketLink(absintheSocket);
  }
}

Note: This will switch all gql communication to use websockets versus http. If you want to conditionally use websockets for only subscriptions (a common pattern) this is where Apollo Link Composition comes in. Specifically, the split function is what we're after (note we are using apollo-utilities, a helpful npm package):

my-app/services/apollo.js

import ApolloService from 'ember-apollo-client/services/apollo';
import { inject as service } from '@ember/service';
import { Socket } from 'phoenix';
import { split } from '@apollo/client';
import { getMainDefinition } from '@apollo/client/utilities';
import { createAbsintheSocketLink } from '@absinthe/socket-apollo-link';
import AbsintheSocket from '@absinthe/socket';

class OverriddenApollo extends ApolloService {
  @service
  session;

  link() {
    let httpLink = super.link();

    const socket = new Socket("ws://socket-url", {
      params: { token: this.get('session.token') },
    });
    const socketLink = createAbsintheSocketLink(AbsintheSocket.create(socket));

    return split(
      ({ query }) => {
        const { kind, operation } = getMainDefinition(query);

        return kind === 'OperationDefinition' && operation === 'subscription';
      },
      socketLink,
      httpLink
    );
  }
}

Note: You will need to add the following dependencies to your project:

yarn add -D @apollo/client
yarn add -D @absinthe/socket
yarn add -D @absinthe/socket-apollo-link

Mutations and Fragments

You can perform a mutation using the mutate method. You can also use GraphQL fragments in your queries. This is especially useful if you want to ensure that you refetch the same attributes in a subsequent query or mutation involving the same model(s).

The following example shows both mutations and fragments in action:

app/gql/fragments/review-fragment.graphql

fragment ReviewFragment on Human {
  stars
  commentary
}

app/gql/mutations/create-review.graphql

#import ReviewFragment from 'my-app/gql/fragments/review-fragment.graphql'

mutation createReview($ep: Episode!, $review: ReviewInput!) {
  createReview(episode: $ep, review: $review) {
    review {
      ...ReviewFragment
    }
  }
}

app/routes/my-route.js

import Route from "@ember/routing/route";
import { inject as service } from "@ember/service";
import mutation from "my-app/gql/mutations/create-review.graphql";

export default Route.extend({
  apollo: service(),

  actions: {
    createReview(ep, review) {
      let variables = { ep, review };
      return this.get("apollo").mutate({ mutation, variables }, "review");
    }
  }
});

Query manager API

  • watchQuery(options, resultKey): This calls the ApolloClient.watchQuery method. It returns a promise that resolves with a POJO. That object will be updated whenever the watchQuery subscription resolves with new data. As before, the resultKey can be used to resolve beneath the root.

    The query manager will automatically unsubscribe from this object.

  • subscribe(options, resultKey): This calls the ApolloClient.subscribe method. It returns a promise that resolves with an EmberApolloSubscription. You can use this object in a few ways to keep track of your subscription:

    • emberApolloSubscription.lastEvent; // return the most recently received event data
    //import { addListener, removeListener } from '@ember/object/events';
    
    const result = await this.apollo.subscribe(
      {
        subscription: mySubscription,
      }
    );
    
    const handleEvent = event => {
      console.log('event received', event)
    };
    
    // Add listener to new data
    addListener(result, 'event', handleEvent);
    
    // Remove the listener from new data
    removeListener(result, 'event', handleEvent);

    As before, the resultKey can be used to resolve beneath the root.

    The query manager will automatically unsubscribe from this object. If you want to manually unsubscribe, you can do so with emberApolloSubscription.apolloUnsubscribe();

  • query(options, resultKey): This calls the ApolloClient.query method. It returns a promise that resolves with the raw POJO data that the query returns. If you provide a resultKey, the resolved data is grabbed from that key in the result.

  • mutate(options, resultKey): This calls the ApolloClient.mutate method. It returns a promise that resolves with the raw POJO data that the mutation returns. As with the query methods, the resultKey can be used to resolve beneath the root.

Apollo service API

You should not need to use the Apollo service directly for most regular usage, instead utilizing the queryManager computed macro. However, you will probably need to customize options on the apollo service, and might need to query it directly for some use cases (such as loading data from a service rather than a route or component).

The apollo service has the following public API:

  • clientOptions: This function should return the options hash that will be passed to the ApolloClient constructor. You can override this function to configure the client this service uses:

    class OverriddenApolloService extends ApolloService {
      clientOptions() {
        return {
          link: this.link(),
          cache: this.cache(),
        };
      }
    }
  • link: This function provides a list of middlewares and afterwares to the Apollo Link the interface for fetching and modifying control flow of GraphQL requests. To create your middlewares/afterwares:

      link() {
        let httpLink = super.link()
    
        // Middleware
        let authMiddleware = setContext(async(request, context) => {
          if (!token) {
            token = await localStorage.getItem('token') || null;
          }
    
          Object.assign(context.headers, {
            headers: {
              authorization: token
            }
          });
    
          return context;
        });
    
        // Afterware
        const resetToken = onError(({ networkError }) => {
          if (networkError && networkError.statusCode === 401) {
            // remove cached token on 401 from the server
            token = undefined;
          }
        });
    
        const authFlowLink = authMiddleware.concat(resetToken);
    
        return authFlowLink.concat(httpLink);
      }

    Example with ember-simple-auth:

    import ApolloService from 'ember-apollo-client/services/apollo';
    import { inject as service } from '@ember/service';
    import { setContext } from '@apollo/client/link/context';
    import { Promise } from 'rsvp';
    
    class OverriddenApollo extends ApolloService {
      @service
      session;
    
      link() {
        let httpLink = super.link();
    
        let authLink = setContext((request, context) => {
          return this._runAuthorize(request, context< 

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
microsoftgraph/graphql-demo: [ARCHIVED] GraphQL for Microsoft Graph发布时间:2022-06-13
下一篇:
brianschardt/node_graphql_apollo_template发布时间:2022-06-13
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap