Nodejs

GraphQL API

마손리 2022. 12. 29. 06:35

GraphQL의 사전적 정의와 서버가동은 이전 포스트에 있습니다.

(이전포스트: https://mason-lee.tistory.com/19)

 

GraphQL을 사용하기 위해서는 기본적으로 typeDefs와 resolvers를 작성해 주어야됩니다.

 

기본사용

typeDefs

typeDefs에서는 입력값(인자)와 출력값의 타입을 정해 놓습니다(Type Definition). 

import { gql } from "apollo-server";

const typeDefs = gql`
  type Movie {
    title: String!
    genres: String
    description: String
  }
  type Query {
    allMovies: [Movie]!
  }
  type Mutation {
    addMovie(title: String!, genres: String, description: String): Movie!
    removeMovie(title: String!): Boolean!
  }
`;

위의 typeDefs를 보면 출력값으로 객체 Movie의 타입을 정해 주고 뒤이어 Query와 Mutation으로 사용될 resolvers를 정의해줍니다.

 

Query의 경우 HTTP 메소드에서 GET에 해당되며 Muation의 경우 POST PUT PATCH DELETE에 해당됩니다.

 

위의 객체들을 살펴보면 타입스크립트와 굉장히 비슷하지만 다른점들이 눈에 띕니다.

첫째로 타입의 첫번째 알파벳은 대문자이며 배열의 표현방식도 Movie[] 가아닌 [Movie]로 작성 되어있습니다. 또한 GraphQL의 경우 느낌표 "!" 를 사용해주어 절대적으로 사용될 타입들을 정의해주지만 타입스크립트의 경우 기본이 절대적으로 사용될 타입들이며 물음표 "?" 를 사용해주어 비절대적인 타입들을 정의해줍니다.

 

resolvers

let movies = [];

const resolvers = {
  Query: {
    allMovies(parent, args, context, info) {
      return movies;
    },
  },
  Mutation: {
    addMovie(parent, args, context, info) {
      movies.push(args);
      return args;
    },
    removeMovie(parent, args, context, info) {
      const { title } = args;
      movies = movies.filter((movie) => movie.title !== title);
      return true;
    },
  },
};

resolvers에서는 함수를 작성해주어 typeDefs에서 정의한 값들만을 활용합니다. 위의 resolvers와 typDefs를 보시면 미리 지정해둔 입출력값들을 사용한다는 것을 알수 있습니다.

 

resolvers에서 Query와 Mutation이 받는 인자들은 총 4가지로 분류되어 있습니다.

 

  1. Parent : 상위 resolver의 리턴값입니다. 만약 현재 resolver가 최상위 resolver라면 Apollo server의 rootValue함수에서 값을 가저옵니다.
  2. Args : 클라이언트에서 직접적으로 받는 실제 인자값입니다.
  3. Context : 모든 resolver들에게 공유되는 객체입니다. Apollo서버에서 설정의 가능합니다.
  4. Info : 현재 resolver의 각종 정보가 들어 있습니다. 현재 resolver의 이름, 종류, 캐쉬정보등 많은것들이 담겨있습니다.

 

위의 resolver는 두번째 인자인 Args만 사용하였으며 다음은 parent를 사용해보겠습니다.

 

Calculated field

Calculated field를 활용하여 resolver안에 resolver를 만들고 Parent인자를 받아보겠습니다.

  type Movie {
    title: String!
    genres: String
    description: String
    totalGenreNumber: Int!
  }

우선 typeDefs에 Movie 객체에 totalGenreNumber라는 타입을 추가해주고

 

  Movie: {
    totalGenreNumber(parent, args, context, info) {
      const { genres } = parent;
      const genresNumber = genres.split(",");
      return genresNumber.length;
    },
  },

다시 해당 resolvers로 돌아와 Mutation이후 위와 같은 코드를 집어 넣습니다.

이렇게 되면 Movie 타입을 리턴하는 resolver안에 totalGenreNumber라는 resolver를 넣어 줄수 있습니다. 위의 경우 Query-allMovies가 이에 해당합니다.

 

typeDefs를 보면 Movie는 title, genres, description을 리턴해주므로 resolver에서 parent를 통해 genres에 접근해주어 함수를 작성해 주면됩니다.

 

Context

Context는 Apollo server에서 설정합니다.

 

일단 간략하게 context에서 사용될 함수를 만들어 보겠습니다.

export const userVerification = (token) => {
  if (!token) {
    throw new Error("You need a token");
  }
};

위의 함수는 token이 있을경우는 통과시키고 없을경우는 Error를 생성합니다.

 

다음 Apollo server에서 context를 추가해줍니다.

import { ApolloServer } from "apollo-server";
import typeDefs from "./test.typeDefs";
import resolvers from "./test.resolvers";
import { userVerification } from "./test.utils";

const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: async ({ req }) => {
    return {
      checkToken: await userVerification(req.headers.token),
    };
  },
});
server.listen().then(({ url }) => {
  console.log(`🚀  Server ready at ${url}`);
});

context는 클라이언트의 request에 접근할수 있습니다. 클라이언트에서 보내는 token을 받아 만들어준 함수를 연결하여 context로 각 resolver들에게 보내줄수 있습니다.

 

기존의 resolver의 Mutation-removeMovie에 만들어준 함수를 context로 쉽게 불러와 사용해 주었습니다.

  Mutation: {
    removeMovie(parent, args, context, info) {
      context.checkToken;
      const { title } = args;
      movies = movies.filter((movie) => movie.title !== title);
      return true;
    },
  },

이제 사용자에게서 token을 받아야지만 removeMovie resolver를 사용할수 있습니다.

 

'Nodejs' 카테고리의 다른 글

REST API  (0) 2022.12.26
GraphQL에서 파일 업로드  (0) 2022.12.24
GraphQl과 Apollo  (1) 2022.12.23
AWS-s3, multer, multer-s3를 이용한 express 파일업로드  (0) 2022.12.23
Express 서버에 Mysql과 session store 설치(AWS-RDS이용)  (0) 2022.12.14