在 Apollo 客户端中使用多个端点

问题描述 投票:0回答:4

这是我在这里发表的第一篇讨论文章。我通过

Odyssey
学会了
Apollo
+ GraphQL。目前,我正在使用 Next.js 构建自己的项目,该项目需要从 2 个 GraphQL 端点获取数据。

我的问题:如何使用 ApolloClient

多个 GraphQL 端点
获取数据?

下面是我的第一个端点的代码:

import { ApolloClient, InMemoryCache, createHttpLink } from "@apollo/client";

const client = new ApolloClient({
  ssrMode: true,
  link: createHttpLink({
    uri: "https://api.hashnode.com/",
    credentials: "same-origin",
    headers: {
      Authorization: process.env.HASHNODE_AUTH,
    },
  }),
  cache: new InMemoryCache(),
});

export default client;
javascript graphql next.js apollo apollo-client
4个回答
16
投票

你想要完成的事情有点反对阿波罗的“一张图”方法。 查看网关和联合 - https://www.apollographql.com/docs/federation/

话虽如此,一些 hacky 解决方案是可能的,但您将需要维护更复杂的结构并在每个查询中指定端点,这会破坏内置机制并可能导致优化问题。

//Declare your endpoints
const endpoint1 = new HttpLink({
    uri: 'https://api.hashnode.com/graphql',
    ...
})
const endpoint2 = new HttpLink({
    uri: 'endpoint2/graphql',
    ...
})

//pass them to apollo-client config
const client = new ApolloClient({
    link: ApolloLink.split(
        operation => operation.getContext().clientName === 'endpoint2',
        endpoint2, //if above 
        endpoint1
    )
    ...
})

//pass client name in query/mutation
useQuery(QUERY, {variables, context: {clientName: 'endpoint2'}})

这个包似乎可以满足您的要求:https://github.com/habx/apollo-multi-endpoint-link

另外,请查看此处的讨论:https://github.com/apollographql/apollo-client/issues/84


2
投票

今天也遇到同样的问题。我想让它充满活力,所以这就是我的结果:

export type DynamicLinkClientName = "aApp" | "bApp" | "graphqlApp";
type Link = RestLink | HttpLink;
type DynamicLink = { link: Link; name: DynamicLinkClientName };
const LINK_MAP: DynamicLink[] = [
  { link: aRestLink, name: "aApp" },
  { link: bAppRestLink, name: "bApp" },
  { link: graphqlAppLink, name: "graphqlApp" },
];

const isClientFromContext = (client: string) => (op: Operation) =>
  op.getContext().client === client;

const DynamicApolloLink = LINK_MAP.reduce<ApolloLink | undefined>(
  (prevLink, nextLink) => {
    // When no name is specified, fallback to defaultLink.
    if (!prevLink) {
      return ApolloLink.split(
        isClientFromContext(nextLink.name),
        nextLink.link,
        defaultLink
      );
    }
    return ApolloLink.split(
      isClientFromContext(nextLink.name),
      nextLink.link,
      prevLink
    );
  },
  undefined
) as ApolloLink;

1
投票

非常喜欢 Pete 的解决方案,它允许超过 2 个端点。

决定编写我自己的版本以进行更好的类型检查。

这是我对他的解决方案的看法:

打字稿:

const defaultClient: keyof typeof clients = "heroku";

const clients = {
  "heroku": new HttpLink({ uri: "https://endpointURLForHeroku" }),
  "lists": new HttpLink({uri: "https://endpointURLForLists" })
}

const isRequestedClient = (clientName: string) => (op: Operation) =>
  op.getContext().clientName === clientName;

const ClientResolverLink = Object.entries(clients)
  .map(([clientName, Link]) => ([clientName, ApolloLink.from([Link])] as const))
  .reduce(([_, PreviousLink], [clientName, NextLink]) => {

    const ChainedLink = ApolloLink.split(
      isRequestedClient(clientName),
      NextLink,
      PreviousLink
    )

    return [clientName, ChainedLink];
  }, ["_default", clients[defaultClient]])[1]

declare module "@apollo/client" {
  interface DefaultContext {
    clientName: keyof typeof clients
  }
}

JS:

const defaultClient = "heroku";

const clients = {
  "heroku": new HttpLink({ uri: "https://endpointURLForHeroku" }),
  "lists": new HttpLink({uri: "https://endpointURLForLists" })
}

const isRequestedClient = (clientName) => (op) =>
  op.getContext().clientName === clientName;

const ClientResolverLink = Object.entries(clients)
  .reduce(([_, PreviousLink], [clientName, NextLink]) => {

    const ChainedLink = ApolloLink.split(
      isRequestedClient(clientName),
      NextLink,
      PreviousLink
    )

    return [clientName, ChainedLink];
}, ["_default", clients[defaultClient]])[1]

0
投票

我将此视为一个 Apollo Links 任务,它是普通 Apollo 客户端的一部分。

具体是一个定向组成链接链

我的原型可能有点太冗长了,所以你需要担心的是:

  • 在 ApolloClient 的初始化中使用
    ApolloLink.split
    将查询定向到不同的端点。
  • 使用一些布尔运算在
    Apollo Link
    中重定向。
    • 在示例中,我使用“上下文”(您在查询选项中放入的内容)。

请注意,您不需要

这是我的原型(NextJS + TypeScript + ApolloClient):

libs/apollo/index.ts
(无论您想在哪里定义 apollo 客户端)

import { ApolloClient, ApolloLink, HttpLink, InMemoryCache } from '@apollo/client'

// Declare your endpoints
const animeEndpoint = new HttpLink({
    uri: 'https://graphql.anilist.co',
})
const countriesEndpoint = new HttpLink({
    uri: 'https://countries.trevorblades.com/',
})

// Not necessary. Just helps with type safety.
export enum Endpoint {
    anime = 'anime',
    country = 'country',
}

//pass them to apollo-client config
const client = new ApolloClient({
    // Version here is just a custom property that we can use to determine which endpoint to use
    // Truthy = animeEndpoint (second) parameter, falsy = countriesEndpoint (third) parameter
    link: ApolloLink.split((operation) => operation.getContext().version === Endpoint.anime, animeEndpoint, countriesEndpoint),
    cache: new InMemoryCache(),
})

export default client

app/page.tsx
(因为我为此使用 NextJS)

'use client'
import { useQuery } from '@apollo/client'
import GetAnimeQuery from '@/libs/gql/GetAnime' // Just a graphql query
import { Endpoint } from '@/libs/apollo'
import GetCountriesQuery from '@/libs/gql/GetCountries' // Just a graphql query

export default function Home() {
  // Anime data
    const { loading: loadingAnime, error: errorAnime, data: dataAnime } = useQuery(GetAnimeQuery, { context: { version: Endpoint.anime } })
  // Countries data
    const {
        loading: loadingCountries,
        error: errorCountries,
        data: dataCountries,
    } = useQuery(GetCountriesQuery, { context: { version: Endpoint.country } })
    console.log('Countries', dataCountries)
    console.log('Anime', dataAnime)
    return (
        <main>
            {loadingAnime && <p>Loading Anime...</p>}
            {errorAnime && <p>Error Anime :{errorAnime.message}</p>}
            {loadingCountries && <p>Loading Countries...</p>}
            {errorCountries && <p>Error Countries:{errorCountries.message}</p>}
        </main>
    )
}

附录(跳过我,除非你想要 graphql 查询)

libs/gql/GetAnime.ts

import { gql } from '@apollo/client'

const GetAnimeQuery = gql`
    query Get {
        Page(page: 1, perPage: 5) {
            pageInfo {
                total
                currentPage
                lastPage
                hasNextPage
                perPage
            }
            media {
                id
                title {
                    romaji
                }
            }
        }
    }
`

export default GetAnimeQuery

libs/gql/GetCountries.ts

import { gql } from '@apollo/client'

const GetCountriesQuery = gql`
    query GetAllCountries {
        countries {
            code
            currency
            name
        }
    }
`

export default GetCountriesQuery
© www.soinside.com 2019 - 2024. All rights reserved.