반응형

📝Apollo Server

  • GraphQL코드를 효율적으로 관리하는데 도움을 줍니다
  • GraphQL의 Query, Type 등 문서화하는데 도움을 줍니다
  • Sandbox를 제공해 직접 GraphQL 테스트도 가능합니다
  • 최적화 기능도 제공합니다

 

Graphql을 개발한 Meta에서 만든 Relay라는게 있지만 학습비용이 높고 React계열만 지원한다는 단점이 존재한다 그에 비해 Apollo는 유연하고 러닝커브가 높지 않다

 

📝리졸버

Apollo Server가 Graphql 작업 처리하는 방법입니다

요청이 들어오면 그에 따라 비즈니스 로직을 처리해 결과를 반환해주는 역할입니다

 

import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';

// Hardcoded data store
const books = [
  {
    title: 'The Awakening',
    author: 'Kate Chopin',
  },
  {
    title: 'City of Glass',
    author: 'Paul Auster',
  },
];

// Schema definition
const typeDefs = `#graphql
  type Book {
    title: String
    author: String
  }

  type Query {
    books: [Book]
  }
`;

// Resolver map
const resolvers = {
  Query: {
    books() {
      return books;
    },
  },
};

// Pass schema definition and resolvers to the
// ApolloServer constructor
const server = new ApolloServer({
  typeDefs,
  resolvers,
});

// Launch the server
const { url } = await startStandaloneServer(server);
console.log(`🚀 Server listening at: ${url}`);

위에 예시 코드의 경우 resolvers에서 books Query에 대한 리턴을 정의합니다 그 이후에 server에 스키마와 resolver를 등록합니다

 

📝리졸버 체이닝

import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';

const libraries = [
  {
    branch: 'downtown',
  },
  {
    branch: 'riverside',
  },
];

// The branch field of a book indicates which library has it in stock
const books = [
  {
    title: 'The Awakening',
    author: 'Kate Chopin',
    branch: 'riverside',
  },
  {
    title: 'City of Glass',
    author: 'Paul Auster',
    branch: 'downtown',
  },
];

// Schema definition
const typeDefs = `#graphql
  # A library has a branch and books
  type Library {
    branch: String!
    books: [Book!]
  }

  # A book has a title and author
  type Book {
    title: String!
    author: Author!
  }

  # An author has a name
  type Author {
    name: String!
  }

  # Queries can fetch a list of libraries
  type Query {
    libraries: [Library]
  }
`;

// Resolver map
const resolvers = {
  Query: {
    libraries() {
      // Return our hardcoded array of libraries
      return libraries;
    },
  },
  Library: {
    books(parent) {
      // Filter the hardcoded array of books to only include
      // books that are located at the correct branch
      return books.filter((book) => book.branch === parent.branch);
    },
  },
  Book: {
    // The parent resolver (Library.books) returns an object with the
    // author's name in the "author" field. Return a JSON object containing
    // the name, because this field expects an object.
    author(parent) {
      return {
        name: parent.author,
      };
    },
  },

  // Because Book.author returns an object with a "name" field,
  // Apollo Server's default resolver for Author.name will work.
  // We don't need to define one.
};

// Pass schema definition and resolvers to the
// ApolloServer constructor
const server = new ApolloServer({
  typeDefs,
  resolvers,
});

// Launch the server
const { url } = await startStandaloneServer(server);
console.log(`🚀 Server listening at: ${url}`);


// 호출
query GetBooksByLibrary {
  libraries {
    books {
      title
      author {
        name
      }
    }
  }
}

리졸버 체이닝이란 리졸버의 결과가 다음 리졸버의 입력으로 연결되는 패턴입니다

 

위 예시 코드에 대해 설명하면 Query Libraries에 대한 정의를 하고 해당 리턴의 타입인 [Library]의 값 받습니다 그 이후 결과값을 받아 Library 타입 안에 속하는 books의 타입인 [Book]의 값을 정의합니다

이렇게 이어받아서 작업하는 걸 리졸버 체이닝이라고합니다

 

아래는 리졸버 체이닝에 쓰이는 인자값 입니다

parent 이전 리졸버의 반환값입니다.
args  필드 에 제공된 모든 GraphQL 인수를 포함하는 객체입니다 .
예를 들어 를 실행할 때 리졸버에 전달되는 객체 query{ user(id: "4") } 입니다. argsuser { "id": "4" }
contextValue 특정 작업을 실행하는 모든 확인자 간에 공유되는 개체입니다.
이를 사용하여 인증 정보, 데이터로더 인스턴스 및 확인자 전체에서 추적할 기타 항목을 포함하여 작업별 상태를 공유합니다.

info 필드 이름, 루트에서 필드까지의 경로 등을 포함하여 작업 실행 상태 에 대한 정보가 포함되어 있습니다 .

 

 

contextValue 예시

import { GraphQLError } from 'graphql';

const resolvers = {
  Query: {
    // Example resolver
    adminExample: (parent, args, contextValue, info) => {
      if (contextValue.authScope !== ADMIN) {
        throw new GraphQLError('not admin!', {
          extensions: { code: 'UNAUTHENTICATED' },
        });
      }
    },
  },
};

interface MyContext {
  // You can optionally create a TS interface to set up types
  // for your contextValue
  authScope?: String;
}

const server = new ApolloServer<MyContext>({
  typeDefs,
  resolvers,
});

const { url } = await startStandaloneServer(server, {
  // Your async context function should async and
  // return an object
  
  context: async ({ req, res }) => ({
    authScope: getScope(req.headers.authorization),
  }),
  
});

 

contextValue 예시 2

context: async () => ({
  db: await client.connect(),
})

// Resolver
(parent, args, contextValue, info) => {
  return contextValue.db.query('SELECT * FROM table_name');
}

 

contextValue 예시 3

import { AnimalAPI } from './datasources/animals';

const resolvers = {
  Query: {
    // All of our resolvers can access our shared contextValue!
    dogs: (_, __, contextValue) => {
      return contextValue.dataSources.animalApi.getDogs();
    },
    cats: (_, __, contextValue) => {
      return contextValue.dataSources.animalApi.getCats();
    },
  },
};

interface MyContext {
  // Context typing
  dataSources: {
    animalApi: AnimalAPI;
  };
}

const server = new ApolloServer<MyContext>({
  typeDefs,
  resolvers,
});

const { url } = await startStandaloneServer(server, {
  context: async () => {
    const animalApi = new AnimalAPI();
    return {
      dataSources: {
        animalApi,
      },
    };
  },
});

contextValue통해 공유 객체에 액세스할 수 있습니다

 

 

 

 

반응형