Apollo-link-rest: apollo-link-rest๋กœ response.headers์— ์•ก์„ธ์Šคํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

์— ๋งŒ๋“  2020๋…„ 11์›” 11์ผ  ยท  6์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: apollographql/apollo-link-rest

์•ˆ๋…•ํ•˜์„ธ์š”,

๋‚˜๋จธ์ง€ API์— ์•ก์„ธ์Šคํ•˜๊ธฐ ์œ„ํ•ด apollo-link-rest๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์ง€๋งŒ useQuery () ๋‹น response.headers ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ํ—ค๋”์— first, prev, next, last ์ •๋ณด๋ฅผ ๋„ฃ๋Š” ํŽ˜์ด์ง€ ๋งค๊น€์ด์žˆ๋Š” ๋‚˜๋จธ์ง€ API์ž…๋‹ˆ๋‹ค. ์–ด๋–ป๊ฒŒ ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

์—ฌ๊ธฐ์— ์˜ˆ์ œ๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค : https://codesandbox.io/embed/how-to-access-response-0mqx7?file=/src/Client.ts

๊ฐœ๋ฐœ ๋„๊ตฌ Network ํƒญ์œผ๋กœ ์ด๋™ํ•˜์—ฌ ํ—ค๋”๊ฐ€ ์žˆ๋Š” ์‘๋‹ต ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งํฌ ์— ํŽ˜์ด์ง€ ๋งค๊น€ ์ •๋ณด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ Headers ...์— ์•ก์„ธ์Šคํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.

Hier๋Š” ๋‚ด ์ฃผ์š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. response.headers.link ๋ฅผ ์–ป๋Š” ๋ฐฉ๋ฒ•์„ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.

  const queryTodos = gql`
      query Todos($page: number, $limit: number) {
        todos(_page: $page, _limit: $limit)
          @rest(type: "[Todo]", path: "/todos/?{args}") {
            userId
            id
            title
            completed
        }
     }
   `;

export const Todos = () => {
  const { data, loading, error } = useQuery(queryTodos, {
    variables: { page: 1, limit: 3 },
    fetchPolicy: "no-cache"
  });

 ...

  return (<div>...</div>)

๋˜ํ•œ stackoverflow : https://stackoverflow.com/q/64791676/3279996 ์—์„œ์ด ์งˆ๋ฌธ์„ํ–ˆ์Šต๋‹ˆ๋‹ค. :๋””

enhancement enhancement๐Ÿ’ก feature help wanted ๐Ÿ›  questionโ”

๋ชจ๋“  6 ๋Œ“๊ธ€

@blatoo- ๋ถˆํ–‰ํžˆ๋„ ๊ทธ๊ฒƒ์€ ํ˜„์žฌ apollo-link-rest์— ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ GraphQL์€ ํ—ค๋”๋ฅผ ์ฒ˜๋ฆฌ ํ•  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ—ค๋”๋Š” ๊ทธ๋ž˜ํ”„์—์„œ ๊นŠ์ˆ™์ด ๋ฐ˜ํ™˜๋˜์–ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋ˆ„๊ตฐ๊ฐ€ ์ด๊ฒƒ์„ ๊ตฌํ˜„ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์‘๋‹ต์˜ "๋ณธ๋ฌธ"์— ๋น„๋ฐ€ / ์ž๋™ ํ•„๋“œ๋กœ __headers ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์ง€๋งŒ์ด ๋™์ž‘์„ ํ™œ์„ฑํ™”ํ•˜๋ ค๋ฉด ๊ธฐ๋Šฅ ํ”Œ๋ž˜๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. graphql๊ณผ ํ˜ธํ™˜๋˜๋„๋ก ํ—ค๋” ํ‚ค๋ฅผ ๋ณ€๊ฒฝํ•˜์‹ญ์‹œ์˜ค.

const queryTodos = gql`
      query Todos($page: number, $limit: number) {
        todos(_page: $page, _limit: $limit)
          @rest(type: "[Todo]", path: "/todos/?{args}") {
+             __headers {
+                         XMyHeader
+             }
            userId
            id
            title
            completed
        }
     }
   `;

@fbartho ์ด ๋‹ต๋ณ€์— ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ํ—ค๋” ์ •๋ณด์— ์ ‘๊ทผ ํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ํ›Œ๋ฅญํ•œ ๊ธฐ๋Šฅ์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‚˜๋Š” json-server๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ๊ทธ๋“ค์€ ๋˜ํ•œ ์ปค์„œ ๊ธฐ๋ฐ˜ ํŽ˜์ด์ง€ ๋งค๊น€ ์ •๋ณด๋ฅผ ์œ„ํ•ด Headers ํŽ˜์ด์ง€ ๋งค๊น€ ์ •๋ณด๋ฅผ ๋„ฃ์Šต๋‹ˆ๋‹ค.

๊ทธ๊ฑด ๊ทธ๋ ‡๊ณ , ๋‹น์‹ ์˜ ๋ฌธ์„œ์—์„œ responseTansformer ๋ฐ customFetch ์˜ต์…˜์ด ์žˆ์Œ์„ ๋ณด์•˜์Šต๋‹ˆ๋‹ค. ํŽ˜์ด์ง€ ๋งค๊น€ ์ •๋ณด๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด ๊ทธ๋“ค๊ณผ ๋ฌด์–ธ๊ฐ€๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ ??? ์‹ค์ œ๋กœ ๋งŽ์ด ์‹œ๋„ํ–ˆ์ง€๋งŒ ์—ฌ์ „ํžˆ ์„ฑ๊ณตํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค ... :

https://www.apollographql.com/docs/link/links/rest/#response -transforming

https://www.apollographql.com/docs/link/links/rest/#custom -fetch

๋ถˆํ–‰ํžˆ๋„ apollo-link-rest ๋Š” GraphQL์— ์—ฐ๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด apollo-link ์˜ API์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค.ํ•˜์ง€๋งŒ ์ด๊ฒƒ์€ JS Headers API๋ฅผ ์ง์ ‘ ๊ณต์œ ํ•˜๋Š” ํ™•์‹คํ•œ ๋ฐฉ๋ฒ•์ด ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. -๊ทธ๋Ÿฌ๋‚˜ ๋น„์ •๊ทœ ํ™” ๋œ ๋ฒ„์ „์ด ๊ฐ€๋Šฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฆ‰, responseTransformer ๋˜๋Š” customFetch ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•„์š”ํ•œ ๊ฒƒ์„ ํŒจ์น˜ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

responseTransformer ๋Š” response.headers ์†์„ฑ์ด์žˆ๋Š” JS Response ๊ฐœ์ฒด๋ฅผ๋ฐ›์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

responseTransformer: async (resp: Response) => {
    const body = await resp.json();
    const interestingHeaders = {};
    interestingHeaders.headerOne = resp.headers.get("headerOne"); // Pluck out the headers you want
    body.__headers = interestingHeaders
    return body;
}

๋ธŒ๋ผ์šฐ์ € ๋ฒ„์ „์— ๋”ฐ๋ผ headers.get() ๋Œ€์‹  headers.entries() ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋“  ํ—ค๋”๋ฅผ ํ•˜๋‚˜์˜ ๊ฐœ์ฒด๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์ง€๋งŒ ํ˜„์žฌ Safari-iOS์—์„œ๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ๋ถ„๋ช…ํžˆ ๋‹จ์ˆœํ™”๋˜๊ณ  ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๋Š” ๋‹น์‹ ์—๊ฒŒ ๋งก๊ฒจ์ ธ ์žˆ์ง€๋งŒ ์ด๊ฒƒ์ด ํšจ๊ณผ๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค!

customFetch ๋„ ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ responseTransformer ๊ฐ€ ๋” ์‰ฝ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

@fbartho ์™€์šฐ, ๋ฉ‹์ง€๋‹ค! ๋ฐฉ๊ธˆ ์ฝ”๋“œ๋ฅผ ์‹œ๋„ํ–ˆ๋Š”๋ฐ ์ž‘๋™ํ•˜๊ณ  ๋งํฌ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์ œ ๋˜ ๋‹ค๋ฅธ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ฟผ๋ฆฌ์—์„œ body.__headers.link ์— ์–ด๋–ป๊ฒŒ ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

๋‚ด ์ฟผ๋ฆฌ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

const queryTodos = gql`
  query Todos($page: number, $limit: number) {
    todos(_page: $page, _limit: $limit)
      @rest(type: "TodoPagination", path: "/todos/?{args}") {
      __headers {
        link
      }
      userId
      id
      title
      completed
    }
  }
`;

ํ•˜์ง€๋งŒ __headers ์ด null ์ธ ๊ฒƒ์€ ...
์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. Client.ts ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ–ˆ๊ณ  ์ฟผ๋ฆฌ ์ฝ”๋“œ๋Š” Todo.tsx .
https://codesandbox.io/s/how-to-access-response-0mqx7?file=/src/Todos.tsx

@fbartho ์•ˆ๋…•ํ•˜์„ธ์š”, ์ €๋Š” ์„ธ ๊ฐ€์ง€ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ์‹œ๋„ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋“œ๋””์–ด์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ์ข‹์€ ํ•ด๊ฒฐ์ฑ…์ธ์ง€ ์—ฌ์ „ํžˆ ์˜๋ฌธ์ด๋‹ค. ๋‚˜์—๊ฒŒ ๋‹น์‹ ์˜ ์˜๊ฒฌ์„ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

๋ฐฉ๋ฒ• 1 : ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.
์ฒ˜์Œ์—๋Š” __headers ์—์„œ useQuery() __headers ์ฟผ๋ฆฌ๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ์‹œ๋„ํ–ˆ์ง€๋งŒ ์‹คํŒจํ–ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๋ฐฉ๋ฒ• 2 : ํ•ด๊ฒฐ์ฑ…์„ ์˜์‹ฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์‹œ๋„ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.
responseTransformer ์—์„œ ์‘๋‹ต ๊ตฌ์กฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ  __headers ๋ฅผ __headers ํ‚ค์— ๋„ฃ์€ ๋‹ค์Œ ํ•  ์ผ ๋ชฉ๋ก์„ todo ํ‚ค์— ๋„ฃ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ข‹์€ ํ•ด๊ฒฐ์ฑ…์ด ์•„๋‹ˆ๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ์‘๋‹ต์˜ ๋ชจ๋“  ๊ตฌ์กฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ useQuery() ๋ชจ๋“  ๊ตฌ์กฐ๋ฅผ ๋‹ค์‹œ ์ž‘์„ฑํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

๋ฐฉ๋ฒ• 3 : ์„ฑ๊ณต๊ณผ ์ฃฝ์Œ์˜ ๋‹จ์ˆœํ•จ
__headers ๋ฅผ ๋ฐ˜์‘ ๋ณ€์ˆ˜์— ๋„ฃ์—ˆ์Šต๋‹ˆ๋‹ค. responseTransformer ํ•จ์ˆ˜์—์„œ __headers ์˜ ๊ฐ’์„ ๋ฐ˜์‘ ๋ณ€์ˆ˜์— ๋„ฃ๊ณ  useQuery() ์‹คํ–‰ ํ›„ ์ฟผ๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ์—ฌ์ „ํžˆ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๋‹ค๋ฅธ ์ฟผ๋ฆฌ์— ๋Œ€ํ•ด ๋‹ค๋ฅธ ๋ฐ˜์‘ ๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

@blatoo , ์ด๊ฒƒ์€ ๋‚ด ์ž์‹ ์ด ๋ฐฉ๊ธˆ ๋งŒ๋‚œ ์˜ค๋ž˜๋œ ๋ฌธ์ œ์ด๋ฉฐ new ApolloLink ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

const paginationLink = new ApolloLink((operation, forward) => {
  return forward(operation).map((response) => {
    const context = operation.getContext();

    const { headers } = context.restResponses[0] || null;
    // in my case i'm making a bunch of sub queries, so i'm using `[0]` to get the headers from the top level.

    if (headers) {
      const pagination = getPaginationFromHeaders(headers.get('link'));

      return { ...response, data: { ...response.data, pagination } };
    }
    return response;
  });
});

const client = new ApolloClient({
  cache: new InMemoryCache(),
  link: concat(paginationLink, restLink),
});
์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰