GraphQL - minnie0531/fastapi-template GitHub Wiki

graphQL

Refer to graphene-python tutorial

SqlAlchemy์—์„œ ์„ ์–ธํ•œ table์„ ์‚ฌ์šฉํ•˜์—ฌ graphQL์„ ๊ตฌํ˜„ ํ•จ. - graphene_sqlalchemy๋ฅผ ์„ค์น˜ ํ•ด์•ผํ•จ.

์„ธ์…˜ ์—ฐ๊ฒฐ

SqlAlchemyย ์—์„œ ์‚ฌ์šฉํ•œ ๋‚ด์šฉ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅ. Model query ๋ฅผ ์ด์šฉํ•˜์—ฌ query ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ๋ถ€๋ถ„์— ๋Œ€ํ•œ ์„ ์–ธ์ด ํ•„์š”ํ•จ.

import cx_Oracle
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker

dsn = cx_Oracle.makedsn(host="{HOST}", port=${PORT}, sid=${SID})
engine = create_engine("oracle+cx_oracle://${user_id}:${user_pw}@%s" % str(dsn))
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

# Below Parsts should be delcared in main or entry point!!
Base.metadata.create_all(bind=engine)
**#This part is for graphQL**
**Base.query = scoped_session(SessionLocal).query_property()** 

Schema

SqlAlchemy์˜ ORM ๋ชจ๋ธ์„ ์ด์šฉํ•˜์—ฌ representative model์„ ๋งŒ๋“ค์–ด ์คŒ (๋น„์ง€๋‹ˆ์Šค ๋ชจ๋ธ).

Relay๋Š” React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ ํ”„๋ ˆ์ž„์›Œํฌ. ๊ฐ ์ปดํฌ๋„ŒํŠธ๋งˆ๋‹ค ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์„ ์–ธํ•˜๊ณ , ์ปดํฌ๋„ŒํŠธ์˜ ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ๋”ฐ๋ผ์„œ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌ ๋ฐ ์กฐํ•ฉํ•˜์—ฌ ๋‹จ์ผ GraphQL ์ฟผ๋ฆฌ๋กœ ๋งŒ๋“ค์–ด ์คŒ.

(์—ฌ๋Ÿฌ๊ฐœ์˜ query์š”์ฒญ ์‹œ ์›ํ•˜๋Š” outputํ˜•ํƒœ๋กœ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•œ framework)

Node๋Š” ์—ฐ๊ฒฐ๋œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์˜๋ฏธํ•จ. ์ฆ‰ ๋ฆด๋ ˆ์ด๋กœ ์ „๋‹ฌ ํ•  ๋•Œ ๋‹ค์Œ ์ฃผ์ž๋ฅผ ์˜๋ฏธํ•จ.

from app.model.item import Item
from graphene import relay, String
from graphene_sqlalchemy import SQLAlchemyConnectionField, SQLAlchemyObjectType

class UserGqlModel(SQLAlchemyObjectType):
    class Meta:
        model = User
        interfaces = (relay.Node,)

class ItemGqlModel(SQLAlchemyObjectType):
    class Meta:
        model = Item
        interfaces = (relay.Node,)

Query

SELECT or GET

db์— ๋Œ€ํ•œ ์ „์ฒด ์กฐํšŒ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด all_users or all_items๋ฅผ ํ†ตํ•ด ์ ‘๊ทผ ๊ฐ€๋Šฅ

๋งŒ์•ฝ ์กฑ๊ฑด์ด ์žˆ๋Š” ์กฐํšŒ์˜ ๊ฒฝ์šฐ resolve๋ฅผ ์ด์šฉํ•ด์„œ ๊ตฌํ˜„ ํ•ด์•ผ ํ•จ.

import graphene
from app.model.user import User
from app.model.item import Item
from graphene import relay, String
from graphene_sqlalchemy import SQLAlchemyConnectionField, SQLAlchemyObjectType
from app.graph_ql.gql_model import UserGqlModel, ItemGqlModel
import logging
import os

logger = logging.getLogger(os.environ.get("PROJECT"))

class Query(graphene.ObjectType):
    node = relay.Node.Field()

    user = SQLAlchemyConnectionField(UserGqlModel.connection, name=String())
    all_users = SQLAlchemyConnectionField(UserGqlModel.connection)
    all_items = SQLAlchemyConnectionField(ItemGqlModel.connection)

    def resolve_user(self, info, **kwargs):
        name = kwargs.get("name")
        logger.info("resolve_user")
        logger.info("name : %s" % name)

        if name is not None:
            return UserGqlModel.get_query(info).filter_by(name=name)

์•„๋ž˜์™€ ๊ฐ™์ด graphQL์„ ์š”์ฒญ ํ•˜๋ฉด "user" interface๋ฅผ ์ด์šฉํ•˜์—ฌ user table์˜ email๊ณผ join๋œ item ํ…Œ์ด๋ธ”์˜ architetureํ•ญ๋ชฉ๋งŒ ๊ฐ€์ ธ์˜ด.( ๐Ÿ˜ฎ)

query {
  user(name:"MinHeeKim") {
    edges {
      node {
        **email**
        item {
           **architecutre**
        }
      }
    }
  }
}

Mutation

INSERT, UPDATE, DELETE or POST, PUT, PATCH, DELETE

๊ฐ behavior์— ๋Œ€ํ•œ mutation์„ ์ •์˜ํ•ด์„œ ์‚ฌ์šฉํ•จ. ์•„๋ž˜๋Š” creation์— ๋Œ€ํ•ด๋‹น๋˜๋ฉฐ, update, delete์—ญ์‹œ ๋น„์Šทํ•˜๊ฒŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ.

Arguement class ๋Š” ์ž…๋ ฅ๋ฐ›์„ field์— ์ •์˜ํ•˜๊ณ , ๊ทธ ์•„๋ž˜ ์–ด๋–ค์‹์˜ return๋ฐ›์„์ง€๋ฅผ ์ •์˜ํ•จ.

mutate ํ•จ์ˆ˜์—์„œ ์‹ค์ œ DB ๋ฅผ ํ†ตํ•ด ์ˆ˜ํ–‰๋˜๋Š” ๊ธฐ๋Šฅ์„ ์ž‘์„ฑํ•˜๊ณ  ์œ„์— ์ •์˜ํ•œ return field ๋ฅผ ๋ฐ˜ํ™˜ ํ•œ๋‹ค.

Mutaion class ํ†ตํ•ด ํ•ด๋‹น class ์™€ ์—ฐ๊ฒฐ ์‹œ์ผœ์คŒ.

from graphene import String, Mutation, Int, Boolean, Field
import graphene
from app.graph_ql.gql_model import UserGqlModel
from app.model.user import User
from app.repository.sql_session import SessionLocal
import logging
import os

logger = logging.getLogger(os.environ.get("PROJECT"))
db = SessionLocal()

class CreateUser(Mutation):
    # input parameters
    class Arguments:
        name = String(required=True)
        department = String()
        email = String()
        age = Int()
        phone = String()
        address = String()
        floor = Int()

    # return fields definition
    data = Field(User)
    success = Boolean()

    @staticmethod
    def mutate(self, info, **kwars):
        user = User(**kwars)

        db.add(user)
        db.commit()
        db.refresh(user)

        logger.info("mutation")

        return CreateUser(data=user, success=True)

class Mutation(graphene.ObjectType):
    logger.info("hello")
    create_user = CreateUser.Field()

์•„๋ž˜์™€ ๊ฐ™์ด mutation์„ ์š”์ฒญํ•˜๋ฉด ์ƒˆ๋กœ์šด User ๊ฐ€ insert ๋˜๊ณ  ๊ฒฐ๊ณผ์— ๋Œ€ํ•œ result ์™€ User field์ค‘์—์„œ name, department, age, email์— ๋Œ€ํ•œ ๊ฐ’์„ ๋Œ๋ ค์คŒ.

mutation{
  createUser (
    name : "UJHan",
    department: "FrontEnd Service",
    age: 27,
    email: "[[email protected]](mailto:[email protected])",
    phone: "000-9999-8888",
    address: "Pankyo",
    floor:17
  ){
    success
    data{
        name
        department
        age
        email
  }
}

endpoint ์—ฐ๊ฒฐ

GraphQLApp์œผ๋กœquery์™€ mutation์„ ์—ฐ๊ฒฐ ์‹œ์ผœ์ค€๋‹ค.

...
# Add graphql endpoint
app.add_route(
    "/graph", GraphQLApp(schema=graphene.Schema(query=Query, mutation=Mutation))
)
...