Apollo GraphQl CRUD in project

Jun 15th 2020 by jyoon

apollo GraphQl CRUD 에 대해서

react-redux를 사용할때는 connect를 이용 mapDispatchToProps를 통해서 redux로 갔다가 다시 return받은걸 mapStateToProps 로 받아서 presentation으로 보내는 작업이 있었는데

apollo GraphQl 해당 컴포넌트에 필요한 query가 있어서 해당 컴포넌트를 사용하면 추가적으로 호출/후처리를 해주지 않아도 되는 편리한 점이 있다.생산성이 높아질것 같다. (컴포넌트형 개발이 아니라면 api를 필요한곳에서 계속 호출/ 후처리를 불편함이 있을 것이다.)

Create: ADD_COMMENT에 대해서

  • mutation에서 사용될 query객체는 어떤 모습일까?

    • mutation은 create, update, delete에서 사용되기 때문에 variables 설정이 필요하기 때문에 아래 코드와 같이 variables를 받는 과정이 있습니다.
  • useMutation함수를 통해서 어떻게 server로 request할까?

    • useMutation 호출시 반환되는 배열 첫번째 요소를 호출해야 request합니다.
    • 조회 할 때 쓰이는 "useQuery"는 호출시 바로 수행 됩니다.
  • useMutation함수 설정은 프로젝트에서 어떻게 해줬는가?

    • 두개의 인자를 넣었으며 아래와 같이 설정했습니다.
    • 첫번째 인자: 수행할 sql
    • 두번째 인자: 수행 sql에서 필요한 조건
  • useMutation함수로 반환 받은 첫번째 객체 설명

    • 이 객체는 promise 객체라서 await를 사용하게 되면 await 키워드를 사용한 promise객체가 settles and return 할때까지 async를 사용한 function은 기다리게 된다.
    • 이 프로젝트에서는 아래 코드 주석 POINT3에서 addCommentMutation를 통해 server request를 하고 있고 그 아래 "setSelfComments" 이 함수는 response를 받은 후에 실행 될것입니다.

      • 이때 기다린다고 해서 cpu의 resource가 낭비되는것이 아니라 javascript engine은 다른 script를 execute를 하거나, 이벤트 처리를 하는 등의 수행을 합니다.
    • Aync, Await는 다음을 참고하자

  • Aync, Await에 대한 간략한 추가 설명

    • 이때 기다린다고 해서 cpu의 resource가 낭비되는것이 아니라 javascript engine은 다른 script를 execute를 하거나, 이벤트 처리를 하는 등의 수행을 합니다.
//PostQuery.js
import { gql } from "apollo-boost"

//POINT1
export const ADD_COMMENT = gql`
  mutation addComment($postId: String!, $text: String!) {
    addComment(postId: $postId, text: $text) {
      id
      text
      user {
        username
      }
    }
  }
`

//PostContainer.js
import { useMutation } from "react-apollo-hooks"
import { TOGGLE_LIKE, ADD_COMMENT } from "./PostQueries"

const PostContainer = ({
  id,
  user,
  files,
  likeCount,
  isLiked,
  comments,
  caption,
  location,
  createdAt,
}) => {

  //POINT2
  const [addCommentMutation] = useMutation(ADD_COMMENT, {
    variables: { postId: id, text: comment.value },
  })
  const onKeyPress = async event => {
    const { which } = event
    if (which === 13) {
      event.preventDefault()
      try {
        //POINT3
        const {
          data: { addComment },
        } = await addCommentMutation()
        // console.log("### addComment: ", addComment);
        setSelfComments([...selfComments, addComment])
        comment.setValue("")
      } catch {
        toast.error("Can't send comment")
      }
    }
  }

  return (
    <PostPresenter
      user={user}
      files={files}
      isLiked={isLikedS}
      likeCount={likeCountS}
      comments={comments}
      caption={caption}
      location={location}
      createdAt={createdAt}
      newComment={comment}
      setIsLiked={setIsLiked}
      setLikeCount={setLikeCount}
      currentItem={currentItem}
      toggleLike={toggleLike}
      onKeyPress={onKeyPress}
      selfComments={selfComments}
    />
  )
}

Retrieve: FEED_QUERY에 대해서

  • POINT1. Query 작성

    • retreieve 할 query를 gql(apollo-boost패키지)에 작성한다.(FEED_QEURY)
  • POINT2. useQeury로 서버 요청

    • POINT1에서 설정한 query를 useQuery 함수에 설정해 호출
    • const { data, loading } = useQuery(FEED_QUERY)
    • useQuery는 호출시 자동으로 query를 수행한다.(useMutation은 그렇지 않다.)
    • response 객체로 "loading, error, data" 객체를 받을 수 있습니다.
    • useQuery 공식문서
  • 예제 코드
//Feed.js 

import React from "react"
import Helmet from "react-helmet"
import styled from "styled-components"
import { gql } from "apollo-boost"
import { useQuery } from "react-apollo-hooks"
import Loader from "../Components/Loader"
import Post from "../Components/Post"

//POINT1. 
const FEED_QUERY = gql`
  {
    seeFeed {
      id
      location
      caption
      user {
        id
        avatar
        username
      }
      files {
        id
        url
      }
      likeCount
      isLiked
      comments {
        id
        text
        user {
          id
          username
        }
      }
      createdAt
    }
  }
`
...

export default () => {
  //POINT2. 
  const { data, loading } = useQuery(FEED_QUERY)

  return (
    <Wrapper className="Wrapper">
      <Helmet>
        <title>Feed | Prismagram</title>
      </Helmet>
      {loading && <Loader />}
      {!loading &&
        data &&
        data.seeFeed &&
        data.seeFeed.map(post => (
          <Post
            key={post.id}
            id={post.id}
            caption={post.caption}
            location={post.location}
            user={post.user}
            files={post.files}
            likeCount={post.likeCount}
            isLiked={post.isLiked}
            comments={post.comments}
            createdAt={post.createdAt}
          />
        ))}
    </Wrapper>
  )
}
  • Playground query 수행 결과를 확인할 수 있다.

    • Apollo Client 설정시 설정한 uri에서 확인가능(Client.js)

./img/Untitled.png

Delete: toggleLike(create 기능도 있음)

  • POINT1. Query 작성

    • mutation을 수행할 query를 gql 함수에 작성
  • POINT2. useMutation에 작성한 query, query 조건을 인자값으로 전달
  • POINT3. trigger query

    • useMutation함수 return 배열 첫번째 함수를 수행시킨다.
//PostQuery.js
import { gql } from 'apollo-boost';

//POINT1
export const TOGGLE_LIKE = gql`
  mutation toggleLike($postId: String!) {
    toggleLike(postId: $postId)
  }
`;
export const ADD_COMMENT = gql`
  mutation addComment($postId: String!, $text: String!) {
    addComment(postId: $postId, text: $text) {
      id
      text
      user {
        username
      }
    }
  }
`;

//PostContainer.js
import { useMutation } from 'react-apollo-hooks';
import { TOGGLE_LIKE, ADD_COMMENT } from './PostQueries';

const PostContainer = ({
  id,
  user,
  files,
  likeCount,
  isLiked,
  comments,
  caption,
  location,
  createdAt,
}) => {
  //POINT2
	const [toggleLikeMutation] = useMutation(TOGGLE_LIKE, {
    variables: { postId: id },
  });
  const [addCommentMutation] = useMutation(ADD_COMMENT, {
    variables: { postId: id, text: comment.value },
  });

	const onKeyPress = async (event) => {
    const { which } = event;
    if (which === 13) {
      event.preventDefault();
      try {
        const {
          data: { addComment },
        } = await addCommentMutation();
        setSelfComments([...selfComments, addComment]);
        comment.setValue('');
      } catch {
        toast.error("Can't send comment");
      }
    }
  };

	...

	const toggleLike = () => {
    //POINT3
    toggleLikeMutation();
    if (isLikedS === true) {
      setIsLiked(false);
      setLikeCount(likeCountS - 1);
    } else {
      setIsLiked(true);
      setLikeCount(likeCountS + 1);
    }
  };

  return (
    <PostPresenter
      user={user}
      files={files}
      isLiked={isLikedS}
      likeCount={likeCountS}
      comments={comments}
      caption={caption}
      location={location}
      createdAt={createdAt}
      newComment={comment}
      setIsLiked={setIsLiked}
      setLikeCount={setLikeCount}
      currentItem={currentItem}
      toggleLike={toggleLike}
      onKeyPress={onKeyPress}
      selfComments={selfComments}
    />
  );
};

REF