graphql

2019-02-08

GraphQL(Graph Query Language)이란 페이스북에서 만든 쿼리 언어이다.
기존에 RESTful APIoverfetch(요청한것보다 더 많은 정보를 받음) 나 underfetch(rest하나를 완성하려고 많은 소스를 요청 예를 들어, 인스타그램의 경우
좋아요api, 사용자 api, 게시물 api등등) 의 문제점들이 있었다. 또한 ios, android 에서 필요한 정보들이 달라
각각 API를 구현하는 것이 힘들었다.

GraphQL vs RESTful

GraphQL는 전체 API를 위해서 단 하나의 Endpoint만 사용하며 사용자의 응답의 구조를 내가 원하는 대로 바꿀 수 있다.
또한 서로 다른 모양의 다양한 요청들에 대해 응답할 수 있어야 할 때, 대부분의 요청이 CRUD(Create-Read-Update-Delete) 에 해당할 때 사용한다.
RESTful API는 리소스마다 하나의 Endpoint를 가지며 주로 api를 작성할때 이미 정해놓은 구조로만 응답이 오게 된다.
또한 HTTP 와 HTTPs 에 의한 Caching 을 잘 사용하고 싶을 때, File 전송 등 단순한 Text 로 처리되지 않는 요청들이 있을 때, 요청의 구조가 정해져 있을 때
주로 사용한다.

그러나 API를 섞어서 사용자 정보등륵은 resful을 사용하고 정보수정은 graphql을 사용한다면
api의 품질을 떨어뜨릴 수 있다.
현재 프로젝트에 어떤것을 사용할지 결정하는 것이 중요하다.

GraphQL 사용법

1. 프로젝트 세팅

먼저 graphql-yoga를 설치한다. 서버는 nodemon을 사용한다.
typeDefs는 타입에 대한 정의이다. 여기서는 타입들이 있는 경로이다.
resolvers는 쿼리를 해결하는 것이다.

1
2
3
4
5
6
7
8

import {GraphQLServer } from 'graphql-yoga';
import resolvers from './graphql/resolvers';

const server = new GraphQLServer({
typeDefs: "graphql/schema.graphql",
resolvers
});

graphql/schema.graphql 파일

type을 지정해주는 부분은 어떤 타입으로 받을 건지 각각의 보내는 파라미터까지 타입을 지정해준다.
!는 필수값을 의미한다. Query는 단순히 값을 가져올때 사용하고 수정이나 삭제등 변형이 가해지는 것들은 Mutation으로 분리한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
type Movie {
id: Int!
name: String!
score: Int!
}

type Movie2 {
id: Int!
title: String!
rating: Float!
summary: String!
language: String!
medium_cover_image: String!
}

type Query {
movies: [Movie]!
movie(id: Int!): Movie
apiMoive(limit: Int, rating: Float): [Movie2]!
}


type Mutation {
addMovie(name: String!, score: Int!): Movie!
deleteMovie(id: Int!): Boolean!
}

2. db세팅

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
let movies = [
{
id: 0,
name: "Star Wars - The new one",
score: 1
},
{
id: 1,
name: "Avengers - The new one",
score: 8
},
{
id: 2,
name: "The Godfather I",
score: 99
},
{
id: 3,
name: "Logan",
score: 2
}
];

export const getMovies = () => movies;

export const getById = id => {
const filteredMovies = movies.filter(movie => movie.id === id);
return filteredMovies[0];
};

export const deleteMovie = id => {
const cleanedMovies = movies.filter(movie => movie.id !== String(id));
if (movies.length > cleanedMovies.length) {
movies = cleanedMovies;
return true;
} else {
return false;
}
};

export const addMovie = (name, score) => {
const newMovie = {
id: `${movies.length + 1}`,
name,
score
};
movies.push(newMovie);
return newMovie;
};

이런식으로 db를 세팅할 수 있으며 restful api를 감쌀 수 도 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import fetch from "node-fetch";
const API_URL = "https://yts.am/api/v2/list_movies.json";

export const getAPIMovies = (limit, rating) => {
let REQUEST_URL = API_URL;
if (limit > 0) {
REQUEST_URL += `limit=${limit}`;
}
if (rating > 0) {
REQUEST_URL += `&minimum_rating=${rating}`;
}
return fetch(REQUEST_URL)
.then(res => res.json())
.json(json=> json.data.moives)
}

3.resolvers 세팅

resolvers는 쿼리를 쿼리를 해결해 주는 역할을 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { getById, getMovies, addMovie, deleteMovie, getAPIMovies } from './db';

const resolvers = {
Query: {
movies: () => getMovies(),
movie: (_, { id }) => getById(id),
APIMovies: (_, { rating, limit }) => getAPIMovies(limit, rating)
},
Mutation: {
addMovie: (_, { name, score }) => addMovie(name, score),
deleteMovie: (_, { id }) => deleteMovie(id)
}
};

export default resolvers;

서버를 실행하면

원하는 정보만 쿼리를 날려서 사용하면 된다.

생각보다 너무 간단하고 쉽다. 또한 GraphQL에서 제공해주고 있는 툴은 현재 내가 가지고 있는 API들을 한눈에 볼 수 있으며
너무 편리하다. 기회가 되면 사용하는 프로젝트에 적용해보고 싶다.

github 저장소: https://github.com/juuuuuuuuuuuuuu/graphql.git