본문 바로가기
개발이야기/Etc.

Apollo 튜토리얼 - 데이터 소스에 연결하기

by hyung12 2021. 2. 8.
반응형

Photo by Shot by Cerqueira on Unsplash

 

튜토리얼 보러가기

 

데이터 소스는 스키마 필드를 채우는 데 사용하는 데이터를 보유하는 모든 데이터베이스, 서비스 또는 API이다

GraphQL API는 모든 데이터 소스 조합과 상호 작용할 수 있다

 

 

 


 

 

 

REST API 연결하기

[ src/datasources/launch.js 파일에 아래 코드 붙여 넣기 ]

const { RESTDataSource } = require('apollo-datasource-rest');

class LaunchAPI extends RESTDataSource {
  constructor() {
    super();
    this.baseURL = 'https://api.spacexdata.com/v2/';
  }
}

module.exports = LaunchAPI;

 

 

 

데이터 가져오기 방법 작성하기

1. getAllLaunches 방법
모든 SpaceX 실행 목록을 가져 오는 방법이 필요하므로 LaunchAPI 클래스 내부에 getAllLaunches 메서드를 추가한다

 

[ src/datasources/launch.js 파일에 아래 코드 붙여 넣기 ]

// class LaunchAPI... {

  async getAllLaunches() {
    const response = await this.get('launches');
    return Array.isArray(response)
      ? response.map(launch => this.launchReducer(launch))
      : [];
  }

 

launchReducerREST API의 시작 데이터를 위의 모양으로 변환 하는 메서드를 작성한다.

 

[ src/datasources/launch.js 파일에 LaunchAPI 클래스 내에 아래 코드를  붙여 넣기 ]

// class LaunchAPI... {

  launchReducer(launch) {
    return {
      id: launch.flight_number || 0,
      cursor: `${launch.launch_date_unix}`,
      site: launch.launch_site && launch.launch_site.site_name,
      mission: {
        name: launch.mission_name,
        missionPatchSmall: launch.links.mission_patch_small,
        missionPatchLarge: launch.links.mission_patch,
      },
      rocket: {
        id: launch.rocket.rocket_id,
        name: launch.rocket.rocket_name,
        type: launch.rocket.rocket_type,
      },
    };
  }

 

 

2. getLaunchById 방법

스키마는 ID로 개별 실행 가져 오기를 지원한다

 

[ src/datasources/launch.js 파일에 LaunchAPI 클래스 내에 아래 코드 붙여 넣기 ]

// class LaunchAPI... {

  async getLaunchById({ launchId }) {
    const response = await this.get('launches', { flight_number: launchId });
    return this.launchReducer(response[0]);
  }

  getLaunchesByIds({ launchIds }) {
    return Promise.all(
      launchIds.map(launchId => this.getLaunchById({ launchId })),
    );
  }

 

LaunchAPI 클래스는 완료! 🎉

 

 

 


 

 

 

SpaceX API는 시작 데이터를 가져 오기위한 읽기 전용 데이터 소스이다

사용자 ID 및 좌석 예약과 같은 애플리케이션 데이터를 저장할 수 있는 쓰기 가능한 데이터 소스가 필요하므로

SQLite 데이터베이스에 연결하고 ORM에 Sequelize를 사용한다

 

 

 

사용자 지정 데이터 소스 빌드하기

Apollo는 현재 DataSourceSQL 데이터베이스에 대한 표준 DataSourceSQL 하위 클래스를 제공하지 않는다

따라서 일반 DataSource 클래스 를 확장하여 SQLite 데이터베이스에 대한 사용자 지정 데이터 소스를 만들었다

 

[ src/datasources/user.js에 등록되어 있는 코드 가져옴( 붙여넣기 노노 ❌) ]

const { DataSource } = require('apollo-datasource');
const isEmail = require('isemail');

class UserAPI extends DataSource {
  constructor({ store }) {
    super();
    this.store = store;
  }

  /**
   * This is a function that gets called by ApolloServer when being setup.
   * This function gets called with the datasource config including things
   * like caches and context. We'll assign this.context to the request context
   * here, so we can know about the user making requests
   */
  initialize(config) {
    this.context = config.context;
  }

  /**
   * User can be called with an argument that includes email, but it doesn't
   * have to be. If the user is already on the context, it will use that user
   * instead
   */
  async findOrCreateUser({ email: emailArg } = {}) {
    const email =
      this.context && this.context.user ? this.context.user.email : emailArg;
    if (!email || !isEmail.validate(email)) return null;

    const users = await this.store.users.findOrCreate({ where: { email } });
    return users && users[0] ? users[0] : null;
  }

  async bookTrips({ launchIds }) {
    const userId = this.context.user.id;
    if (!userId) return;

    let results = [];

    // for each launch id, try to book the trip and add it to the results array
    // if successful
    for (const launchId of launchIds) {
      const res = await this.bookTrip({ launchId });
      if (res) results.push(res);
    }

    return results;
  }

  async bookTrip({ launchId }) {
    const userId = this.context.user.id;
    const res = await this.store.trips.findOrCreate({
      where: { userId, launchId },
    });
    return res && res.length ? res[0].get() : false;
  }

  async cancelTrip({ launchId }) {
    const userId = this.context.user.id;
    return !!this.store.trips.destroy({ where: { userId, launchId } });
  }

  async getLaunchIdsByUser() {
    const userId = this.context.user.id;
    const found = await this.store.trips.findAll({
      where: { userId },
    });
    return found && found.length
      ? found.map(l => l.dataValues.launchId).filter(l => !!l)
      : [];
  }

  async isBookedOnLaunch({ launchId }) {
    if (!this.context || !this.context.user) return false;
    const userId = this.context.user.id;
    const found = await this.store.trips.findAll({
      where: { userId, launchId },
    });
    return found && found.length > 0;
  }
}

module.exports = UserAPI;

[ src/datasources/user.js DataSource 하위 클래스 설명 ]

  • initialize: 서브 클래스에 모든 구성 옵션을 전달하려면 이 메서드를 구현하며 UserAPI 클래스는 nitialize를 사용하여 API의 컨텍스트에 액세스한다

  • this.context: 그래프 API의 컨텍스트는 GraphQL 요청의 모든 리졸버 에서 공유되는 객체로 컨텍스트가 사용자 정보를 저장하고 공유하는 데 유용하다

  • 캐싱: RESTDataSource 클래스가 내장 캐시를 제공하지만 일반 DataSource 클래스는 제공하지 않는다

 

[ 데이터 베이스에서 데이터를 가져오고 업데이트 하는 데 사용하는 몇 가지 방법 ]

  • findOrCreateUser({ email }): 데이터 베이스에 주어진 email로 사용자를 찾거나 만든다

  • bookTrips({ launchIds }): launchIds 배열에 있는 개체를 가져와 로그인 한 사용자를 위해 예약한다

  • cancelTrip({ launchId }): launchIds가 있는 개체를 가져와 로그인 한 사용자의 실행을 취소한다

  • getLaunchIdsByUser(): 로그인 한 사용자에 대해 예약 된 모든 여행을 반환한다

  • isBookedOnLaunch({ launchId }): 로그인 한 사용자가 특정 출발에 여행을 예약했는지 여부를 확인한다

 

 

Apollo Server에 데이터 소스 추가하기

dataSources 옵션을 ApolloServer 생성자에 전달한다

이 옵션은 새로 인스턴스화 된 데이터 소스를 포함하는 개체를 반환하는 함수이다

 

[ src/index.js 파일의 코드를 아래 코드로 전체 바꿔주기 ]

const { ApolloServer } = require('apollo-server');
const typeDefs = require('./schema');

const { createStore } = require('./utils');


const LaunchAPI = require('./datasources/launch');
const UserAPI = require('./datasources/user');


const store = createStore();

const server = new ApolloServer({
  typeDefs,

  dataSources: () => ({
    launchAPI: new LaunchAPI(),
    userAPI: new UserAPI({ store })
  })
});

server.listen().then(() => {
  console.log(`
    Server is running!
    Listening on port 4000
    Explore at https://studio.apollographql.com/dev
  `);
});

 

데이터 소스를 Apollo Server에 연결 완료! 🎉

 

반응형