Post

REST API와 GraphQL API 비교

Django 프로젝트에서 REST API를 구현하고, GraphQL과 비교해봤다.

작업 브랜치: restapi

REST API 구현

  1. Recipe, Ingredient, RecipeIngredient에 대한 Serializer 생성
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
from rest_framework import serializers
from .models import Recipe, Ingredient, RecipeIngredient 


class RecipeSerializer(serializers.ModelSerializer):
    class Meta:
        model = Recipe
        fields = [
            'id',
            'user',
            'title',
            'description',
            'preparation_time',
            'cooking_time',
            'difficulty_level'
        ]


class IngredientSerializer(serializers.ModelSerializer):
    class Meta:
        model = Ingredient
        fields = [
            'id',
            'name'
        ]


class RecipeIngredientSerializer(serializers.ModelSerializer):
    recipe = RecipeSerializer
    Ingredient =IngredientSerializer

    class Meta:
        model = RecipeIngredient
        fields = [
            'recipe',
            'ingredient',
            'quantity',
            'unit'
        ]
  1. views.py에 ListCreateAPIView 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from rest_framework import generics
from .serializers import RecipeSerializer, IngredientSerializer, RecipeIngredientSerializer


class RecipeListCreate(generics.ListCreateAPIView):
    queryset = Recipe.objects.all()
    serializer_class = RecipeSerializer

class IngredientListCreate(generics.ListCreateAPIView):
    queryset = Ingredient.objects.all()
    serializer_class = IngredientSerializer

class RecipeIngredientListCreate(generics.ListCreateAPIView):
    queryset = RecipeIngredient.objects.all()
    serializer_class = RecipeIngredientSerializer

  1. urls.py 설정
1
2
3
    path('rest-api/recipe/', views.RecipeListCreate.as_view()),
    path('rest-api/ingredient/', views.IngredientListCreate.as_view()),
    path('rest-api/recipe-ingredient/', views.RecipeIngredientListCreate.as_view())

REST API와 GraphQL API 비교

환경 설정

1
2
import requests
import pandas as pd

Over fetching

모든 레시피의 id, title, difficultyLevel를 조회하는 경우를 살펴보자.

REST API

1
2
3
recipe_list_url = 'http://localhost:8000/rest-api/recipe/'

response = requests.get(recipe_list_url)
1
2
3
recipe_list = response.json()
df = pd.DataFrame(recipe_list)
df.head()
idusertitledescriptionpreparation_timecooking_timedifficulty_level
12Korean Bibimbap zzBibimbap is a signature Korean…3025Moderate
22KimchijeonKimchijeon is a savory Korean …1515Easy
32Dubu JorimDubu Jorim is a flavorful Kore…1520Easy
43계란 후라이egg fry03쉬움
52Scramble EggEgg03Easy

레시피의 user, description, preparation_time, cooking_time까지 조회되게 된다.

GraphQL

1
2
3
4
5
6
7
recipe_query = """query MyQuery {
  allRecipes {
    id
    title
    difficultyLevel
  }
}"""
1
2
3
graphql_url = 'http://localhost:8000/graphql/'  

response = requests.get(graphql_url, json={'query': recipe_query})
1
2
3
recipe_list = response.json()['data']['allRecipes']
df = pd.DataFrame(recipe_list)
df.head()
idtitledifficultyLevel
1Korean Bibimbap zzModerate
2KimchijeonEasy
3Dubu JorimEasy
4계란 후라이쉬움
5Scramble EggEasy

Under fetching

모든 레시피의 id, title, difficultyLevel와 레시피에 들어가는 재료의 정보를 조회하는 경우를 살펴보자.

REST API

1
2
3
4
5
6
recipe_list_url = 'http://localhost:8000/rest-api/recipe/'
response = requests.get(recipe_list_url)

recipe_list = response.json()
recipe_df = pd.DataFrame(recipe_list)
recipe_df.head()
idusertitledescriptionpreparation_timecooking_timedifficulty_level
12Korean Bibimbap zzBibimbap is a signature Korean…3025Moderate
22KimchijeonKimchijeon is a savory Korean …1515Easy
32Dubu JorimDubu Jorim is a flavorful Kore…1520Easy
43계란 후라이egg fry03쉬움
52Scramble EggEgg03Easy
1
2
3
4
5
6
recipe_ingredient_list_url = 'http://localhost:8000/rest-api/recipe-ingredient/'
response = requests.get(recipe_ingredient_list_url)

recipe_ingredient_list = response.json()
recipe_ingredient_df = pd.DataFrame(recipe_ingredient_list)
recipe_ingredient_df.head()
recipeingredientquantityunit
114cups
12200grams
133tablespoons
142tablespoons
151tablespoons
1
2
3
4
5
ingredient_list_url = 'http://localhost:8000/rest-api/ingredient/'
response = requests.get(ingredient_list_url)
ingredient_list = response.json()
ingredient_df = pd.DataFrame(ingredient_list)
ingredient_df.head()
idname
1Cooked rice
2Beef
3Soy sauce
4Sesame oil
5Sugar
1
2
cols = ['id', 'title', 'difficulty_level']
recipe_df = recipe_df[cols]
1
2
3
4
5
6
7
8
9
df = (
    recipe_ingredient_df
    .merge(recipe_df, left_on='recipe', right_on='id', how='left')
    .drop(columns=['id'])
    .merge(ingredient_df, left_on='ingredient', right_on='id', how='left')
    .drop(columns=['id', 'ingredient'])
    .rename(columns={'name': 'ingredient_name', 'recipe': 'id'})
)
df.head()
idquantityunittitledifficulty_levelingredient_name
14cupsKorean Bibimbap zzModerateCooked rice
1200gramsKorean Bibimbap zzModerateBeef
13tablespoonsKorean Bibimbap zzModerateSoy sauce
12tablespoonsKorean Bibimbap zzModerateSesame oil
11tablespoonsKorean Bibimbap zzModerateSugar

필요한 정보를 전부 가져오기 위해 여러 번의 요청을 보내야 한다.

GraphQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
recipe_ingredient_query = """query MyQuery {
  allRecipes {
    id
    title
    difficultyLevel
    recipeingredientSet {
      ingredient {
        name
      }
      quantity
      unit
    }
  }
}"""
1
2
response = requests.get(graphql_url, json={'query': recipe_ingredient_query})
recipe_list = response.json()['data']['allRecipes']
1
2
3
4
5
6
df = pd.json_normalize(
    recipe_list,
    record_path='recipeingredientSet',
    meta=['id', 'title', 'difficultyLevel']
)
df.head()
quantityunitingredient.nameidtitledifficultyLevel
4cupsCooked rice1Korean Bibimbap zzModerate
200gramsBeef1Korean Bibimbap zzModerate
3tablespoonsSoy sauce1Korean Bibimbap zzModerate
2tablespoonsSesame oil1Korean Bibimbap zzModerate
1tablespoonsSugar1Korean Bibimbap zzModerate

한번의 요청으로 필요한 데이터를 전부 가져온다.

This post is licensed under CC BY 4.0 by the author.