GraphQL 3 - Mutation
1. API 다운로드
링크에서 Poke API를 찾아 Poke API의 Repo로 이동한 후 자신의 깃 저장소로 Pork 한다.
깃 저장소를 내 로컬로 클론한 후 Visual Code로 연다.
npm install
package.json이 있는 위치에서 npm install로 필요한 Dependency를 설치한다.
"scripts": {
...
"start": "node --experimental-json-modules src/index.js",
},
"type": "module"
package.json에서 scripts의 start 항목을 위와 같이 변경하고 type 항목을 추가하고 값을 "module" 로 준다. 이를 지정하지 않으면 자동으로 CommonJS로 실행되어 ES6 문법(import 등)을 사용할 수 없다.
Poke API 프로젝트에서 index.js의 위치가 변경되었는지 원본에는 dist라는 폴더 내의 index.js를 실행하는 것으로 되어있으나 그러한 폴더가 존재하지 않으므로 src 폴더 내의 index.js로 변경한다.
--experimental-json-modules 옵션은 json 모듈을 import 하여 index.js를 실행시키기 위한 플래그이다. 이와 더불어 .js가 아닌 모듈을 프로젝트에서 import 하기 위해서는 type을 module로 지정해주어야 한다. 그렇지 않으면 기본 값으로 commonjs를 사용하여 'Cannot use import statement outside a module' 오류가 발생한다.
//import QueryType from './type/QueryType'; //Old
import QueryType from './type/QueryType.js'; //New
프로젝트 내의 파일들 중 프로젝트 내의 js 파일을 import하는 문장들이 있다. 이 때 .js가 붙어있지 않은 곳에 .js 붙이는 수정이 필요하다. 그렇지 않으면 'Cannot find module' 오류가 발생한다.
모든 작업이 끝났다면 npm start로 서버를 실행한 후 브라우저로 localhost:5000에 접속해보자. 브라우저에 GraphiQL이 나온다면 성공한 것이다!
2. Mutation 작성
- 포켓몬 추가기능
type Mutation {
addPokemon(name: String!, classification: String, types: [String]): Pokemon
}
나만의 포켓몬을 데이터베이스에 추가하는 기능을 만들어보자. 먼저 schma.graphql 파일에 새로운 기능에 대한 명세를 적어준다. 이름은 addPokemon이고, 입력으로 name(필수), classification, types를 받으며 새로 생성된 포켓몬 객체를 리턴하는 기능임을 적어준다. 이 때 타입을 Mutation으로 적어준다.
export async function addPokemon({ name, classification, types }) {
const newPokemon = {
"id": pokemons.length + 1,
"name": name,
"classification": classification,
"types": types
}
pokemons.push(newPokemon);
return newPokemon;
}
Pokemon.js 에는 resolver 역할을 하는 함수들이 모여있다. 따라서 이 곳에 실제 데이터 처리 함수를 작성한다.
import {
GraphQLObjectType,
GraphQLInt,
GraphQLString,
GraphQLList,
GraphQLNonNull,
GraphQLInputObjectType
} from 'graphql';
//...
const inputPokemonType = new GraphQLInputObjectType({
name: 'PokemonInput',
fields: {
name: { type: GraphQLString },
classification: { type: GraphQLString },
types: { type: new GraphQLList(GraphQLString) }
}
});
export const MutationType = new GraphQLObjectType({
name: 'Mutation',
fields: {
addPokemon: {
type: PokemonType,
args: {
input: { type: inputPokemonType }
},
resolve: async (_, args) => await addPokemon(args.input)
}
}
});
QueryType.js는 쿼리 타입들을 javascript로 정의해놓은 곳이다. 상단의 graphql에서 GraphQLObjecType, GraphQLLInt 등의 클래스를 가져오는 코드에 GraphQLInputObjectType을 추가한다. GraphQLInputObjectType은 쿼리에 Input이 여러개 있을 때 이들을 모아 하나의 객체로 만들어 재사용할 수 있게 만들어주는 클래스이다. 바로 아래의 inputPokemonType 객체가 바로 그것이다. 그 후 QueryType을 정의해준 것처럼 MutationType을 새롭게 정의한다.
이 때 주의할 점은 원래 QueryType만 schema.js에서 사용하므로 export default 를 사용하였지만 이제 MutationType 모듈 또한 schema.js에 추가되어야 하므로 두 모듈이 export 되고, schema.js에서 import 할 수 있도록 추가적인 처리가 필요하다.
import { GraphQLSchema } from 'graphql';
import { QueryType, MutationType } from './type/QueryType.js';
const schema = new GraphQLSchema({
query: QueryType,
mutation: MutationType
});
export default schema;
마지막으로 schema.js에서 mutation을 추가한 후 서버를 재실행 시켜보자.
GraphiQL에서 위와 같이 쿼리를 날리면 우리가 정의했던대로 새로운 포켓몬 객체가 리턴된다. 또한 pokemons 필드를 이용하여 전체 포켓몬 리스트를 불러왔을 때 내가 추가한 "newPokemon"이 존재하므로 addPokemon 기능을 무사히 구현하였다고 할 수 있다. 삭제, 갱신의 기능도 위와 같은 방식으로 추가해나갈 수 있다.