본문 바로가기
graphql

[Nexus] Source Types으로 parent 타입 수정하기

by 박연호의 개발 블로그 2022. 4. 9.


Source란 ?

 

Nexus를 사용해서 schema를 만들 때, sourceTypes 옵션을 추가할 수 있습니다. sourceTypes는 source의 type을 내가 원하는 타입으로 매핑해주는 역할을 합니다. 여기서 source는 resolver의 첫번째 인자를 의미합니다.

 

클라이언트가 아래와 같이 질의한다고 해봅시다.

query{
  user{           - resolver (1)
    email         - resolver (2)
    address       - resolver (3)
    posts{        - resolver (4)
      content     - resolver (5)
    }
  }
}

우리가 질의하는 모든 field에는 resolver가 존재합니다. 질의는 resolver에서 처리되며 결과값을 클라이언트에서 받아보는 것이죠. 

 

resolver는 4개의 인자를 받습니다. 각각의 인자의 의미를 확인하고 싶으면 apollo server 문서를 보시면 됩니다.

 

여기서 우리가 다룰 부분은 첫번째 인자, source 입니다. source는 바로 그 전 resolver에서 반환한 value를 의미합니다. 보통 parent로 많이 표현하는데, 여기서는 source로 포현하였습니다.

resolve: (source, args, ctx, info) => {

 

클라이언트에서 user query를 질의하면 (1)번 resolver가 수행되며, 내부로직은 아래와 같습니다. user query에서 return 되는 값은 user type의 field인 email, address, posts의 resolver(2,3,4)의 첫번째 인자로 들어가게 됩니다. 이것이 우리가 이번에 다룰 source 입니다.

const Query = queryType({
  definition(t) {
    t.field("user", {
      type: "User",
      resolve: (parent, args, ctx) => {
        const user = {
          email: "email@naver.com",
          address: "address1",
          age: 23,
          posts: [
            {
              content: "content1",
            },
            {
              content: "content2",
            },
          ],
        };

        return user;
      },
    });
  },
});

즉, 정리하면 resolve의 첫번째 인자는 이전 resolver에서 반환한 값이 됩니다. graphql 공식문서apollo server에서는 resolver의 첫번째 인자를 다음과 같이 설명하고 있습니다.

 

- The return value of the resolver for this field's parent

- The previous object

 

Nexus는 type safe by default 합니다. 우리가 code first하게 작성한 모든 코드에 타입을 만들어 줍니다. 물론 source에 대한 타입도 있습니다. 아래의 코드는 위의 코드는 Nexus에 의해 생성된 artifacts 이며 source에 대한 타입은 NexusGenObjects['User']입니다. 

export interface NexusGenObjects {
  Post: { // root type
    content?: string | null; // String
  }
  Query: {};
  User: { // root type
    address?: string | null; // String
    age?: number | null; // Int
    email?: string | null; // ID
    name?: string | null; // String
    posts?: Array<NexusGenRootTypes['Post'] | null> | null; // [Post]
  }
}

Source 타입과 원하는 type으로 매핑

보통 source의 타입과 값이 같다면 client의 질의는 default resolver에 의해 무난하게 처리 됩니다. 하지만 다음과 같은 경우를 생각해 봅시다.

- 클라이언트 질의
query{
   user{
     name
     age
    }
  }
  
 - user resolver에서 반환하는 값(name, age의 source값)
 {
    userName : "userName",
    userAge : 20
  }

 

클라이언트에서 질의하는 field는 name, age이지만 실제로 받은 source값은 userName, userAge입니다. 이 경우 source값을 name, age에 매핑해줘야 합니다. 그러기 위해서는 현재 source type을 userName, userAge에 맞게 수정해줘야 하는데, 그 역할을 하는 것이  sourceTypes입니다. 

 

즉 source의 type을 NexusGenObjects['User']가 아닌, 우리가 정의한 type으로 매핑해주는 것입니다.

./type.ts
const User = objectType({
  name: "User",
  definition(t) {
    t.string("name", {
      resolve: (source) => source.userName,
    });
    t.int("age", {
      resolve: (source) => source.userAge,
    });
  },
});

const Query = queryType({
  definition(t) {
    t.field("user", {
      type: "User",
      resolve: (parent, args, ctx) => {
        const user = {
          userName: "userName",
          userAge: 23,
        };
        return user;
      },
    });
  },
});

./schema.ts
  sourceTypes: {
    modules: [
      {
        module: path.join(__dirname, "db.ts"),
        alias: "DB",
      },
    ],
    mapping: {
      User: "DB.User",
    },
    skipTypes: ["Query"],
  },
  

./db.ts
  export interface User {
  userName: string;
  userAge: number;
}

 

source type은 interface, class, type alias or enum으로 정의할 수 있습니다.

 

기본적으로 resolver의 source type은 Nexus가 자동으로 User type으로 매칭해 줍니다. 그렇기 때문에 우리는 별다른 설정을 해주지 않아도 됩니다. 

 

'graphql' 카테고리의 다른 글

[Nexus] NexusGenObjects와 NexusGenFieldTypes의 차이  (0) 2023.03.24
[GraphQL] custom scalar type 사용하기  (0) 2022.10.09
[GraphQL] null propagation  (0) 2022.10.01
[GraphQL] GraphQL와 N+1  (4) 2021.09.26