arrow-left

All pages
gitbookPowered by GitBook
1 of 1

Loading...

GraphQL

hashtag
B. GraphQL

GraphQL API λ§΅ν•‘μ—λŠ” call, publish, subscribe, map 컀λ„₯ν„°λ₯Ό 이용 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

TypeDefs

GraphQL ν”„λ‘œν† μ½œμ—μ„œ typeDefs 속성에 μ„œλΉ„μŠ€μ— ν•„μš”ν•œ μ •μ˜(scalarλ₯Ό μ œμ™Έν•œ νƒ€μž…, μΈν„°νŽ˜μ΄μŠ€, μ—΄κ±°ν˜• λ“± λͺ¨λ“  ν˜•νƒœ)을 μΆ”κ°€ν•˜κ±°λ‚˜ κΈ°μ‘΄ νƒ€μž…(API Gatewayμ—μ„œ μ œκ³΅ν•˜λŠ” κΈ°λ³Έ νƒ€μž…κ³Ό λΆ„μ‚° μ„œλΉ„μŠ€μ—μ„œ μ œκ³΅ν•œ νƒ€μž…λ“€)을 ν™•μž₯ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Resolvers

μ΄ν•˜ 리쑸버에 각 νƒ€μž…λ“€μ˜ ν•„λ“œλ₯Ό call, publish, subscribe, map 컀λ„₯터에 λ§΅ν•‘ν•©λ‹ˆλ‹€.

리쑸버가 ν• λ‹Ήλ˜μ§€ μ•Šμ€ ν•„λ“œλ“€μ€ source κ°μ²΄μ—μ„œ λ™μΌν•œ μ΄λ¦„μ˜ μ†μ„±μœΌλ‘œλΆ€ν„° μ£Όμž…λ©λ‹ˆλ‹€.

Call

GraphQL API의 Query 및 Mutation νƒ€μž…μ˜ ν•„λ“œλ“€μ—λŠ” publish 및 call λ˜λŠ” map 컀λ„₯ν„°λ₯Ό 이용 ν•  수 μžˆμŠ΅λ‹ˆλ‹€. params λ§΅ν•‘μ—λŠ” @source, @args, @context, @infoλ₯Ό 이용 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Map

GraphQL ν”„λ‘œν† μ½œμ—μ„œ map 컀λ„₯ν„° (Inline JavaScript Function String)λŠ” κ°„λž΅ν•˜κ²Œ field: { map: <FN_STRING> } λŒ€μ‹ μ— field: <FN_STRING> λ°©μ‹μœΌλ‘œ μž‘μ„± ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μœ„μ²˜λŸΌ Union, Interface κ΅¬ν˜„ νƒ€μž…μ„ ν•΄μ„ν•˜κΈ° μœ„ν•œ 특수 ν•„λ“œμ—λ„ Inline JavaScript Function Stringλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

Batched Call (DataLoader)

μœ„μ²˜λŸΌ 인증 정보λ₯Ό ν¬ν•¨ν•œ @contextλ‚˜ GraphQL ν•„λ“œ 인자인 @argsλ₯Ό ν™œμš©ν•΄ λ™μΌν•œ μ•‘μ…˜μ„ μ„œλ‘œ λ‹€λ₯Έ λ°©μ‹μœΌλ‘œ λ§΅ν•‘ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

λ˜ν•œ call λ©”μ†Œλ“œλŠ” GraphQL μš”μ²­μ—μ„œ λ°œμƒν•˜κΈ° μ‰¬μš΄ N+1 쿼리λ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄ μš”μ²­μ„ 배치둜 처리 ν•  수 μžˆλ„λ‘ μ„€κ³„λ˜μ—ˆμŠ΅λ‹ˆλ‹€. (ref. )

ν•œ μ»¨ν…μŠ€νŠΈμ—μ„œ μ—¬λŸ¬λ²ˆ ν˜ΈμΆœλ˜λŠ” μ•‘μ…˜μ— 배칭을 μ§€μ›ν•˜λ©΄ 응닡 속도λ₯Ό 획기적으둜 λ†’νž 수 μžˆμŠ΅λ‹ˆλ‹€. 배칭을 ν™œμ„±ν™”ν•˜κΈ° μœ„ν•΄μ„œλŠ” call 컀λ„₯ν„°μ˜ batchedParams ν•„λ“œμ— 배치 μ²˜λ¦¬κ°€ κ°€λŠ₯ν•œ ν•„λ“œμ˜ 이름을 μž‘μ„±ν•˜κ³ , μ—°κ²°λœ μ„œλΉ„μŠ€ μ•‘μ…˜μ΄ λ°°μ—΄λ‘œ λ“€μ–΄μ˜€λŠ” 인자 λ¬ΆμŒμ„ 처리 ν•  수 μžˆλ„λ‘ ν•©λ‹ˆλ‹€.

μœ„μ™€ 같은 GraphQL μš”μ²­μ€ player.get μ•‘μ…˜μ„ { id: [context.user.player.id, 1, 2, 3], ...(other common params) } νŽ˜μ΄λ‘œλ“œμ™€ ν•¨κ»˜ ν•œλ²ˆλ§Œ ν˜ΈμΆœν•˜κ²Œ λ©λ‹ˆλ‹€. μ—°κ²°λœ μ•‘μ…˜μ΄ [{ ... }, { ... }, { ... }, { ... }] 묢음으둜 응닡을 μ£Όλ©΄ 각 ν•„λ“œμ— ν•΄λ‹Ήν•˜λŠ” 응닡이 ν• λ‹Ήλ©λ‹ˆλ‹€.

λ§Œμ•½ id: 3인 ν”Œλ ˆμ΄μ–΄κ°€ μ—†λŠ” 경우 배치 μš”μ²­μ„ μ²˜λ¦¬ν•˜λŠ” κ³Όμ •μ—μ„œ μ—λŸ¬λ₯Ό λ°œμƒμ‹œμΌœ μ œμ–΄ 흐름을 λ©ˆμΆ”λŠ” λŒ€μ‹ μ—, μ—λŸ¬λ₯Ό λ°œμƒν‚€μ§€μ•Šκ³  배치 응닡에 ν¬ν•¨μ‹œν‚€κ³  λ‚˜λ¨Έμ§€ μ œμ–΄ 흐름을 λ§ˆλ¬΄λ¦¬ν•©λ‹ˆλ‹€. [{ ... }, { ... }, { ... }, { message: "...", isBatchError: true, ... }] 처럼 isBatchError: true 속성을 κ°–λŠ” μ—λŸ¬ 객체λ₯Ό 응닡에 ν¬ν•¨ν•©λ‹ˆλ‹€.

Subscribe

GraphQL API의 Subscription νƒ€μž…μ˜ ν•„λ“œμ—μ„œλŠ” subscribe 컀λ„₯ν„°λ₯Ό μ‚¬μš© ν•  수 μžˆμŠ΅λ‹ˆλ‹€. params λ§΅ν•‘μ—λŠ” λ§ˆμ°¬κ°€μ§€λ‘œ @source, @args, @context, @infoλ₯Ό 이용 ν•  수 μžˆμŠ΅λ‹ˆλ‹€. @source에 이벀트 객체가 λ§΅ν•‘λ©λ‹ˆλ‹€.

@source κ°μ²΄λŠ” { event, payload }둜 κ΅¬μ„±λ©λ‹ˆλ‹€. Broker에 따라 기타 속성이 μΆ”κ°€ 될 수 μžˆμŠ΅λ‹ˆλ‹€.

subscribe 컀λ„₯ν„°μ—μ„œλŠ” μœ„μ²˜λŸΌ μˆ˜μ‹ λœ 이벀트 νŽ˜μ΄λ‘œλ“œλ₯Ό λ‹€μ‹œ map 컀λ„₯ν„°λ‘œ λ³€ν™˜ ν•  수 μžˆμŠ΅λ‹ˆλ‹€. subscribe 컀λ„₯ν„° μ•ˆμ—μ„œ map 컀λ„₯ν„°κ°€ μ‚¬μš©λ˜μ§€ μ•ŠλŠ” 경우 이벀트 객체 전체(source)λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.

    GraphQL: {
      typeDefs: `
        """Soccer Player"""
        type Player implements Node {
          id: ID!
          email: String!
          name: String!
          photoURL: String
          position: String
          """A team player belongs to"""
          team: Team
        }

        extend type Query {
          """Current Player"""
          viewer: Player
          player(id: ID!): Player
        }

        extend type Subscription {
          playerMessage: String!
          playerUpdated: Player
        }
      `,
Dataloaderarrow-up-right
      resolvers: {
        Player: {
          team: {
            call: {
              action: "team.get",
              params: {
                id: "@source.teamId",
              },
            },
          },
          position: `({ source, args, context, info }) => source.position.toUpperCase()`,
`
          // be noted that special field __isTypeOf got only three arguments
          __isTypeOf: `({ source, context, info }) => return source.someSpecialFieldForThisType != null`,

          // be noted that special field __resolveType got only three arguments
          __resolveType: `
            ({ source, context, info }) => {
              if (source.someSpecialFieldForThisType != null) {
                return "TypeA";
              } else {
                return "TypeB";
              }
            }
          `,
        },
        Query: {
          viewer: {
            call: {
              action: "player.get",
              params: {
                id: "@context.user.player.id[]",
              },
            },
          },
          player: {
            call: {
              action: "player.get",
              params: {
                id: "@args.id[]",
              },
            },
          },
        },
query {
  viewer {
    id
    email
  }
  one: player(id: 1) {
    id
    email
  }
  two: player(id: 2) {
    id
    email
  }
  three: player(id: 3) {
    id
    email
  }
}
        Subscription: {
          playerMessage: {
            subscribe: {
              events: ["player.message"],
            },
          },
          playerMessage: {
            subscribe: {
              events: ["player.message"],
              map: `({ source, args, context, info }) => source.payload.message`,
            },
          },
        },
      },
    },