- 追記
- noteにまとめました
- https://note.com/tagty/n/ncbfe96a3be23
-
ApolloClient
とはなにか?- どのように使うのか?
- どのような要素で構成されるのか?
-
ApolloClient
はどのように使われるのか?
-
ApolloClient
は以下のように使うuri
やcache
などをオプションに渡して使う
-
ApolloClient
は以下ような要素で構成されるLink
Cache
LocalState
QueryManager
-
ApolloClient
はクエリをを実行するのに使われるgql
を使って生成したASTをquery
に渡すquery
はQueryManager
を利用する
-
https://www.apollographql.com/docs/react/
- GraphQLを利用
- localとremoteのデータの管理
- アプリケーションのデータをfetch、cache、変更
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
uri: 'https://48p1r2roz4.sse.codesandbox.io',
cache: new InMemoryCache()
});
- ApolloClientのinitialize
- 引数にuriとcache
-
uri
- GraphQLサーバーのuri
- https://48p1r2roz4.sse.codesandbox.io
-
cache
- InMemoryCache
-
src/core/ApolloClient.ts
-
GraphQL Document(QueryとMutation)をGraphQLサーバーに送信
-
GraphQLサーバーからのレスポンスをストアにcache
-
uri
- Apollo Clientが接続するGraphQLのエンドポイント
-
cache
- ストアに利用されるcache
export class ApolloClient<TCacheShape> implements DataProxy {
constructor(options: ApolloClientOptions<TCacheShape>) {
const {
uri,
cache,
// ...
} = options;
}
// ...
if (!link) {
link = uri
? new HttpLink({ uri, credentials, headers })
: ApolloLink.empty();
}
// ...
this.link = link;
this.cache = cache;
// ...
this.localState = new LocalState({
cache,
client: this,
//...
});
this.queryManager = new QueryManager({
cache: this.cache,
link: this.link,
// ...
localState: this.localState,
// ...
});
}
src/core/ApolloClient.ts
export class ApolloClient<TCacheShape> implements DataProxy {
constructor(options: ApolloClientOptions<TCacheShape>) {
const {
uri,
// ...
} = options;
}
// ...
if (!link) {
link = uri
? new HttpLink({ uri, credentials, headers })
: ApolloLink.empty();
}
// ...
});
}
src/link/http/HttpLink.ts
export class HttpLink extends ApolloLink {
public requester: RequestHandler;
constructor(public options: HttpOptions = {}) {
super(createHttpLink(options).request);
}
}
src/link/core/ApolloLink.ts
export class ApolloLink {
// ...
constructor(request?: RequestHandler) {
if (request) this.request = request;
}
// ...
}
- ApolloクライアントとGraphQLサーバー間のデータフローをカスタマイズ
- クライアントのネットワークのふるまいをLinkオブジェクトのチェーンとして定義可能
- Headerの変更やログの記録など
-
例
-
1.デバッグのためのログの記録
-
2.認証のためのHTTPヘッダーを追加
-
3.終端のリンク
- HTTP経由でGraphQLサーバーなどにリクエスト
-
4.レスポンスは各リンクに逆の順序で受け渡される
- データがcacheされる前に、レスポンスの変更や他のアクションの実行も可能
-
-
デフォルトでは、ApolloLinkのHttpLinkを使用
- HTTP経由でGraphQLの操作をサーバーにリクエスト
- 多くの場合はHttpLinkで対応可能
-
拡張や置換をするには
- カスタムリンクを定義
- ApolloClientのconstructorで実行順序を指定
src/core/ApolloClient.ts
export class ApolloClient<TCacheShape> implements DataProxy {
constructor(options: ApolloClientOptions<TCacheShape>) {
const {
uri,
// ...
} = options;
}
// ...
if (!link) {
link = uri
? new HttpLink({ uri, credentials, headers })
: ApolloLink.empty();
}
// ...
}
src/link/http/HttpLink.ts
export class HttpLink extends ApolloLink {
public requester: RequestHandler;
constructor(public options: HttpOptions = {}) {
super(createHttpLink(options).request);
}
}
src/link/core/ApolloLink.ts
export class ApolloLink {
// ...
constructor(request?: RequestHandler) {
if (request) this.request = request;
}
// ...
}
src/link/http/createHttpLink.ts
export const createHttpLink = (linkOptions: HttpOptions = {}) => {
let {
uri = '/graphql',
// ...
} = linkOptions;
}
src/link/http/__tests__/HttpLink.ts
it('supports using a GET request', done => {
const variables = { params: 'stub' };
const extensions = { myExtension: 'foo' };
const link = createHttpLink({
uri: '/data',
fetchOptions: { method: 'GET' },
includeExtensions: true,
includeUnusedVariables: true,
});
execute(link, { query: sampleQuery, variables, extensions }).subscribe({
next: makeCallback(done, () => {
const [uri, options] = fetchMock.lastCall()!;
const { method, body } = options!;
expect(body).toBeUndefined();
expect(method).toBe('GET');
expect(uri).toBe(
'/data?query=query%20SampleQuery%20%7B%0A%20%20stub%20%7B%0A%20%20%20%20id%0A%20%20%7D%0A%7D%0A&operationName=SampleQuery&variables=%7B%22params%22%3A%22stub%22%7D&extensions=%7B%22myExtension%22%3A%22foo%22%7D',
);
}),
error: error => done.fail(error),
});
});
src/core/ApolloClient.ts
export class ApolloClient<TCacheShape> implements DataProxy {
constructor(options: ApolloClientOptions<TCacheShape>) {
const {
// ...
cache,
// ...
} = options;
}
// ...
this.localState = new LocalState({
cache,
client: this,
//...
});
// ...
}
- https://www.apollographql.com/docs/react/local-state/local-state-management/
-
ApolloClient
- GraphQLを利用
- GraphQLサーバーと通信
- stateの管理が可能
-
localに独立している場合、remoteのサーバーが不要
-
remoteからfetchされたstateとlocalのstateの両方の管理が可能
- 単一のAPIで管理が可能
-
-
localのstateは、任意の方法でストアが可能
localStorage
やApollo Client cache
-
特定のフィールドにクエリを実行するとき
- localのデータをfetch、上書きするロジックを指定
-
localのフィールドとremoteからfetchされたフィールドの両方を一つのQueryに含めることが可能
src/core/ApolloClient.ts
export class ApolloClient<TCacheShape> implements DataProxy {
constructor(options: ApolloClientOptions<TCacheShape>) {
const {
// ...
cache,
// ...
} = options;
}
// ...
this.cache = cache;
// ...
this.localState = new LocalState({
cache,
client: this,
//...
});
// ...
}
src/core/LocalState.ts
export class LocalState<TCacheShape> {
constructor({
cache,
client,
// ...
}: LocalStateOptions<TCacheShape>) {
this.cache = cache;
if (client) {
this.client = client;
}
}
}
src/core/ApolloClient.ts
export class ApolloClient<TCacheShape> implements DataProxy {
constructor(options: ApolloClientOptions<TCacheShape>) {
const {
uri,
cache,
// ...
} = options;
}
// ...
if (!link) {
link = uri
? new HttpLink({ uri, credentials, headers })
: ApolloLink.empty();
}
// ...
this.link = link;
this.cache = cache;
// ...
this.localState = new LocalState({
cache,
client: this,
//...
});
this.queryManager = new QueryManager({
cache: this.cache,
link: this.link,
// ...
localState: this.localState,
// ...
});
}
src/core/QueryManager.ts
export class QueryManager<TStore> {
constructor({
cache,
link,
// ...
localState,
// ...
}: {
cache: ApolloCache<TStore>;
link: ApolloLink;
// ...
localState?: LocalState<TStore>;
// ...
}) {
this.cache = cache;
this.link = link;
// ...
this.localState = localState || new LocalState({ cache });
// ...
}
}
import { gql } from '@apollo/client';
// const client = ...
const query = gql`
query GetRates {
rates(currency: "USD") {
currency
}
}
`;
client.query({ query: query }).then((result) => console.log(result));
gql
client.query()
- https://github.com/apollographql/graphql-tag
- literal
- GraphQLクエリの文字列を標準のGraphQLのASTに解析
import gql from 'graphql-tag';
const query = gql`
{
user(id: 5) {
firstName
lastName
}
}
`
- 生成されるsyntax tree
{
"kind": "Document",
"definitions": [
{
"kind": "OperationDefinition",
"operation": "query",
"name": null,
"variableDefinitions": null,
"directives": [],
"selectionSet": {
"kind": "SelectionSet",
"selections": [
{
"kind": "Field",
"alias": null,
"name": {
"kind": "Name",
"value": "user",
...
}
}
]
}
}
]
}
import { gql } from "@apollo/client";
const query = gql`
query GetRates {
rates(currency: "USD") {
currency
}
}
`;
console.log(query);
import { gql } from '@apollo/client';
// const client = ...
const query = gql`
query GetRates {
rates(currency: "USD") {
currency
}
}
`;
client.query({ query: query }).then((result) => console.log(result));
src/core/ApolloClient.ts
public query<T = any, TVariables = OperationVariables>(
options: QueryOptions<TVariables, T>,
): Promise<ApolloQueryResult<T>> {
// ...
return this.queryManager.query<T, TVariables>(options);
}
export class ApolloClient<TCacheShape> implements DataProxy {
//...
this.queryManager = new QueryManager({
cache: this.cache,
link: this.link,
// ...
localState: this.localState,
// ...
});
}
src/core/QueryManager.ts
public query<TData, TVars = OperationVariables>(
options: QueryOptions<TVars, TData>,
): Promise<ApolloQueryResult<TData>> {
// ...
const queryId = this.generateQueryId();
return this.fetchQuery<TData, TVars>(
queryId,
options,
).finally(() => this.stopQuery(queryId));
}
- 単一のQueryを実行
- Promiseをreturn
-
ApolloClient
は以下のように使うuri
やcache
などをオプションに渡して使う
-
ApolloClient
は以下ような要素で構成されるLink
Cache
LocalState
QueryManager
-
ApolloClient
はクエリをを実行するのに使われるgql
を使って生成したASTをquery
に渡すquery
はQueryManager
を利用する