Hello, GraphQL

REST API

GET

/users/42

POST

/users

URIMethod를 통해 자원행위를 명확히 표현

Over-Fetching

서버에서 필요이상의 데이터를 받는 것

User 이름 목록을 보여주는 화면

/users 요청

address, age 등 부가 데이터도 같이 수신

Under-Fetching

특정 Endpoint에서 충분하지 못한 데이터를 리턴

User Profile 화면

/user/1, /feed, /follower 요청

API 하나 완성을 위해 몇 번의 요청을 하게된다.

이외에도..

CRUD 속하지 않는 행위

정보가 있다면 업데이트하고 없으면 추가 하는 Upsert

POST 를 써야 할까요? PUT을 써야될까요?

애매하니까 그냥 POST를 쓰자 !

팀마다 사용하는 방식이 다름 (명확하지 않은 표준)

RESTful 하다?

그런 REST API로 괜찮은가?

REST를 구성하는 스타일

  • stateless
  • cache
  • uniform interface
    • self-descriptive messages
    • hypermedia as the engine of application state(HATEOAS)
  • layered system
  • code on demand (optional)

RESTful 한가요?

1. REST 하게 구현한다.

2. HTTP API 라고 부른다.

3. REST 하지 않지만 그냥 그렇게 부른다.

실제 다양한 요구사항을 해결하면서 

REST 하게 개발하는것은 어렵다

REST API 이외에 대안은?

GraphQL

A Query language for your API

라이브러리 ❌

API 표준 ⭕️

🚩 Goal

  • Eliminate Overfetching
  • Eliminate Underfetching
  • Be Declartive

Architecture

Client

GraphQL

Server

Query

Response

Query

Database

Response

REST API

Http Request

Http Response

DataSource 와 무관하게 사용가능

{
    user {
        name
    }
}

Flow

  1. 데이터가 어떤식으로 구성되어있는지 형식 정의
  2. 필요한 데이터를 기반으로 Query 작성
  3. 데이터를 어떻게 처리해서 보낼지 Code 작성
[  
   {  
      "id":1,
      "name":"Leanne Graham",
      "username":"Bret",
      "email":"Sincere@april.biz",
      "address":{  
         "street":"Kulas Light",
         "suite":"Apt. 556",
         "geo":{  
            "lat":"-37.3159",
            "lng":"81.1496"
         }
      },
      "phone":"1-770-736-8031 x56442"
   },
   {  
      "id":2,
      "name":"Ervin Howell",
      "username":"Antonette",
      "email":"Shanna@melissa.tv",
      "address":{  
         "street":"Victor Plains",
         "suite":"Suite 879",
         "geo":{  
            "lat":"-43.9509",
            "lng":"-34.4618"
         }
      },
      "phone":"010-692-6593 x09125"
   }
]

GET /users

application/json

response

REST API

{
    user {
        name
    }
}

Response

Basic Query

Request

[  
   {  
      "id":1,
      "name":"Leanne Graham",
      "username":"Bret",
      "email":"Sincere@april.biz",
      "address":{  
         "street":"Kulas Light",
         "suite":"Apt. 556",
         "geo":{  
            "lat":"-37.3159",
            "lng":"81.1496"
         }
      },
      "phone":"1-770-736-8031 x56442"
   },
   {  
      "id":2,
      "name":"Ervin Howell",
      "username":"Antonette",
      "email":"Shanna@melissa.tv",
      "address":{  
         "street":"Victor Plains",
         "suite":"Suite 879",
         "geo":{  
            "lat":"-43.9509",
            "lng":"-34.4618"
         }
      },
      "phone":"010-692-6593 x09125"
   }
]
[  
   {  
      "name":"Leanne Graham",
      "posts": {
        "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
        "body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut"
       }
   },
   {  
      "name":"Ervin Howell",
      "posts": {
        "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
        "body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut"
       }
   }
]
{
    user {
        name
        posts {
            id
            title
            body
        }
    }
}

Response

Basic Query - Relation

Request

Basic Query - Arguments

{
    user(id: 1) {
        name
        posts {
            id
            title
            body
        }
    }
}

Request

[  
   {  
      "name":"Leanne Graham",
      "posts": {
        "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
        "body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut"
       }
   }
]

Response

Type System

type User {
    id: String!
    name: String!
    phone: String!
}

Basic

type Starship {
  id: ID!
  name: String!
  length(unit: LengthUnit = METER): Float
}

Arguments

Schema

schema {
  query: Query
  mutation: Mutation
}

Basic

Query

Mutation

질의를 위한 인터페이스

상태변화를 위한 인터페이스

Example Query Type

type Query {
    users: [User]!
    user(id: Int!): User!
    posts: [Post]!
}

쿼리이름과 반환할 타입에 대한 정의

GraphQL Server Library(JS)

Demo Graphql-yoga

Graphql-yoga Setup

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

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

server.start();
  • typeDefs - Graphql Type & Schema 정의
  • resolvers - resolver를 정의 

Define User & Post Type

type User {
    id: Int!
    name: String!
    username: String!
    email: String!
    phone: String!
    posts: [Post!]!
}

type Post {
    id: Int!
    title: String!
    body: String!
    user: User!
}

type Query {
    users: [User]!
    user(id: Int!): User!
    posts: [Post]!
}

Resolvers

import { users, posts } from './db';

const resolvers = {
    Query: {
        users: () => users;
        user: ( _, { id }) => users.find(u => u.id === id);
        
        posts: () => posts;
    },
    Post: {
        user: ({ userId }) => users.find(u => u.id === userId)
    },
    User: {
        posts: ({ id }) => posts.filter(p => p.userId === id)
    }
};

export default resolvers;

Playground

Query, Mutation 을 확인할 수 있음 

graphql library 에서 제공 

GraphQL 이점 

  • 자동 문서화 + 테스팅 도구 Playground
  • 단일 Endpoint
  • 타입 유효성 검사
  • Overfetching, Underfetching 방지
  • 유연함 

GraphQL 고려해 볼점

  • Relation으로 Fetching 을 할때 성능 최적화 문제
  • FrontEnd 업무량 증가? 유연함? 
  • 보안 문제 

Refs

Hello, GraphQL

By y0c

Hello, GraphQL

  • 237