아폴로 연맹과 게이트웨이 입문

Apollo는 지난해 여러 개의 GraphQL API를 단일 게이트웨이 API로 조합하는 과정을 간소화하기 위해 Apollo Federation라는 오픈 소스 도구를 발표했다.
Apollo Federation(Apollo Federation)은 과거에 GraphQL API를 모드 결합으로 연결한 적이 있는데, 그것이 제공하는 성명식, 크게 놀랄 필요가 없는 방법은 신선한 공기이다.사실 이 라이브러리가 발표되었을 때, 나는 막 시작했을 때 writing a book about GraphQL, 아폴로 연맹을 사용하기 위해 첫 부분을 신속하게 다시 썼다.
지난 10개월 동안 저는 이 도서관을 탐색해 왔습니다. 저는 이곳에서 일련의 블로그 글을 쓰고 학습 과정에서 배운 기교를 공유할 것이라고 생각합니다.
첫 번째 글은 Apollo Federation을 사용하여 단독 서비스에 두 개의 '연합 모드' 를 설정하고 Apollo Gateway를 사용하여GraphQL API를 조합하는 방법을 간략하게 소개할 것이다.네트워크 인터페이스 API를 시작하고 다시 불러오기 위한 npm 스크립트 설정과 두 가지 서비스를 실현하는 최선의 방법도 공유할 것입니다.
TL;박사: 찾을 수 있어요the complete code here.
첫 번째 단계는 프로젝트 디렉토리를 만드는 것입니다.
mkdir basic-apollo-federation-demo && cd basic-apollo-federation-demo
그런 다음 새 디렉토리npm init--yes 플래그 생성package.json 파일을 실행하고 질문하지 않습니다.
npm init --yes
그런 다음 필요한 모든 패키지를 설치합니다.
npm i [email protected] @apollo/[email protected] @apollo/[email protected] \
[email protected] [email protected] [email protected] [email protected] [email protected]
다음은 위 패키지의 용도 설명입니다.
  • apollo-server: 게이트웨이 API와 우리가 만든 모든 서비스에 사용되는 ApolloServer의 실례가 필요합니다.
  • @apollo/federation: 이 가방은 우리가 서비스의 모델을 조합할 수 있도록 허락할 것입니다.
  • @apollo/gateway: 이 패키지는 전송된GraphQL API 요청을 하부 서비스에 나누어 줍니다.
  • graphql: 아폴로는 이 라이브러리를 대등한 의존으로 필요로 한다.
  • esm: 이 가방은'무바베타, 무묶음의 ECMAScript 모듈 마운트기'로 노드에서 사용할 수 있습니다importexport.아무런 번거로움도 없다.
  • nodemon: 프로젝트 디렉터리에 있는 파일이 변경되면 Nodemon은 자동으로 프로그램을 다시 불러옵니다.
  • concurrently: 우리는 이 패키지를 사용하여 여러 명령을 동시에 실행할 수 있습니다.또한 와일드카드를 사용하여 단축 명령을 지원합니다.
  • wait-on: 게이트웨이 API를 시작하기 전에 서비스의 포트를 사용할 수 있도록 기다리고 확보하는 것이 좋습니다. 따라서 이 패키지를 사용할 것입니다.
  • 다음에 프로젝트를 구성하기 위해 디렉터리와 파일을 만들어야 합니다.장면 설정(이중관어 사용 불가)🙃), IMDB와 유사한 미니 API를 만들어 영화, 배우, 감독에 대한 데이터를 제공할 것입니다.Apollo Federation의 묘미는 GraphQL API를 유형별로 나누는 것이 아니라 관심사 분리를 바탕으로 분리할 수 있다는 점이다.
    실천에서, 이것은 우리가 하나의 서비스 모델에서 하나의 유형을 정의하고, 접근할 수 있으며, 심지어는 다른 모델에서 다른 필드를 사용하여 그것을 확장할 수 있다는 것을 의미한다.이 기능을 사용하면 제품 영역 논리에 따라 API를 쉽게 구분할 수 있습니다.
    우리는 단독'영화'와'인물'서비스를 통해 데이터에 대한 접근을 관리할 것이다.모든 서비스는 연방 모델이 있습니다. 우리는 이 두 모델을 네트워크 인터페이스급 API에 통합할 것입니다. 이렇게 하면 클라이언트는 하나의 API에서 데이터를 조회할 수 있고 이 두 개의 하부 서비스에 직접 관심을 가질 필요가 없습니다.
    이제 각 서비스에 대한 디렉토리를 추가합니다.
    mkdir films people
    
    또한 파일index.js을 추가하여 두 개의 서비스 및 게이트웨이 코드를 포함합니다.
    touch index.js films/index.js people/index.js
    
    마지막으로 API를 통해 질의하려면 시뮬레이션 데이터가 필요합니다.또한 data.js 파일을 추가합니다.
    touch data.js
    
    다음 코드를 추가합니다.
    export const people = [
      { id: "1", name: "Steven Spielberg" },
      { id: "2", name: "Richard Dreyfuss" },
      { id: "3", name: "Harrison Ford" },
    ];
    
    export const films = [
      {
        id: "1",
        title: "Jaws",
        actors: ["2"],
        director: "1",
      },
      {
        id: "2",
        title: "Close Encounters of the Third Kind",
        actors: ["2"],
        director: "1",
      },
      {
        id: "3",
        title: "Raiders of the Lost Ark",
        actors: ["3"],
        director: "1",
      },
    ];
    
    우리는 인원 설정 서비스부터 시작할 것이다.다음 코드를 people/index.js에 추가합니다.
    import { ApolloServer, gql } from "apollo-server";
    import { buildFederatedSchema } from "@apollo/federation";
    
    import { people } from "../data.js";
    
    const port = 4001;
    
    const typeDefs = gql`
      type Person @key(fields: "id") {
        id: ID!
        name: String
      }
    
      extend type Query {
        person(id: ID!): Person
        people: [Person]
      }
    `;
    
    위에서 우리는 API의 aPerson를 설명하는 기본적인 유형 정의를 가지고 있다.APerson 배우일 수도 있고 감독일 수도 있지만 앞으로 영화 서비스 부문에서 구분하도록 하겠습니다.@key 명령이 Person 형식 정의에 추가된 것을 볼 수 있습니다. 이 특수 명령은 Person를 하나의 실체로 만들었습니다. 이것이 바로 Apollo에게 이 유형은 다른 서비스에 인용되고 확장될 수 있다는 것을 알려 줍니다. (다른 서비스가 id 필드에 표시된 값으로 한 사람을 식별할 수 있다면.)
    이 서류에는 두 가지 주의해야 할 일이 있다.우선, 우리는 buildFederatedSchema 에서 @apollo/federation 를 가져와서, 잠시 후에 모델 연합을 준비할 수 있도록 합니다.둘째, 우리는 extend 앞에서 type Query 키워드를 사용한다. QueryMutation 유형은 게이트웨이 단계에서 기원되기 때문에 Apollo 문서에 의하면 모든 실현 서비스는 어떠한 추가 조작을 통해 이런 유형을 확장해야 한다고 한다.
    다음은 people/index.js의 유형에 대한 파서를 추가합니다.
    // ...
    
    const resolvers = {
      Person: {
        __resolveReference(object) {
          return people.find((person) => person.id === object.id);
        }
      },
      Query: {
        person(_, { id }) {
          return people.find((person) => person.id === id);
        },
        people() {
          return people;
        }
      }
    };
    
    Query의 해석기는 우리가 바라는 것이지만 Person__referenceResolver에서 재미있는 것을 만났다.이 참고 해석기는 우리가 다른 서비스에 인용되었을 때, 그 @key 필드 (즉 id 를 통해person 실체를 얻는 방법을 인터페이스에 설명하는 것입니다.
    마지막으로, Dell은 ApolloServer 하단에서 명시적으로 전달people/index.jsbuildFederatedSchema 옵션이 아닌 서버의 schema 반환 값을 사용하여 새 typeDefs를 시작합니다.
    // ...
    
    const server = new ApolloServer({
      schema: buildFederatedSchema([{ typeDefs, resolvers }]),
    });
    
    server.listen({ port }).then(({ url }) => {
      console.log(`People service ready at ${url}`);
    });
    
    이것이 바로 우리 직원 서비스에 필요한 모든 코드다.영화 서비스에 초점을 맞추기 전에resolvers에 게이트웨이 API를 설치합니다.
    import { ApolloGateway } from "@apollo/gateway";
    import { ApolloServer } from "apollo-server";
    
    const port = 4000;
    
    const gateway = new ApolloGateway({
      serviceList: [
        { name: "people", url: "http://localhost:4001" }
      ]
    });
    
    const server = new ApolloServer({
      gateway,
      subscriptions: false
    });
    
    server.listen({ port }).then(({ url }) => {
      console.log(`Server ready at ${url}`);
    });
    
    게이트웨이 단계에서 우리는 다시 index.js을 실례화했지만 이번에는 모드가 아닌 ApolloServer를 가져오고 실례화했다.ApolloGateway 구조 함수에 ApolloServer 대상 수조를 전달하는데 그 중에서 각 대상은 우리가 인터페이스로 구성하고자 하는 연방 모델을 설명한다.마지막으로, 우리는 이 ApolloGateway 에서 serviceListsubscriptions 로 설정합니다. 왜냐하면 Apollo Gateway는 구독을 지원하지 않기 때문입니다.
    현재 코드가 있으면 GraphQL API를 처음 시작할 수 있습니다.이를 위해서는 세 개의 새 스크립트 업데이트 falseApolloServer 을 만들어야 합니다.우리는 scripts 스크립트를 만들어서 package.jsonpeople 서비스를 시작하고 dev:people 스크립트를 만들어서people 서비스의 포트가 사용할 수 있기를 기다린 다음 nodemon로 게이트웨이 API를 시작합니다.마지막으로, 우리는 dev:gateway 스크립트를 만들고 nodemon 와일드카드를 사용하여 모든 dev 스크립트를 시작합니다.
    {
      ...
      "scripts": {
        "dev": "concurrently -k npm:dev:*",
        "dev:people": "nodemon -r esm ./people/index.js",
        "dev:gateway": "wait-on tcp:4001 && nodemon -r esm ./index.js"
      },
      ...
    }
    
    노드를 실행할 때 엔지니어 서비스와 게이트웨이 API 프로세스에 concurrently(또는 dev:- 플래그를 사용하여 모듈을 미리 로드합니다-r.js(esm 패키지의 요구에 따라).
    GraphQL API가 http://localhost:4000/graphql에서 사용할 수 있는지 확인하기 위해 지금 --require를 실행합니다.너도 이제 브라우저의 이 URL에서 GraphQL 놀이터를 열 수 있다.
    이어서 우리는 영화 서비스를 건설할 것이다.films 서비스의 모델은people 서비스보다 더 복잡할 것입니다. esm 형식을 추가하는 것 외에 이전에 만든 npm run dev 형식을 인용하고 확장하기 때문입니다.먼저 가져오기 및 유형 정의를 Film에 설정합니다.
    import { ApolloServer, gql } from "apollo-server";
    import { buildFederatedSchema } from "@apollo/federation";
    
    import { films } from "../data.js";
    
    const port = 4002;
    
    const typeDefs = gql`
      type Film {
        id: ID!
        title: String
        actors: [Person]
        director: Person
      }
    
      extend type Person @key(fields: "id") {
        id: ID! @external
        appearedIn: [Film]
        directed: [Film]
      }
    
      extend type Query {
        film(id: ID!): Film
        films: [Film]
      }
    `;
    
    영화 서비스에서 Person 유형을 사용하려면 다시 정의해야 하지만, 이번에는 films/index.js 키워드를 앞에 두어야 한다.우리는 그것의 키 필드 Person 를 포함해야 하지만, 이번에는 다른 서비스에서 정의된 것을 표시하기 위해dd extend 명령을 사용합니다.그 다음에 우리는 id 유형에 두 개의 새로운 필드를 추가해서 이 사람이 출연하거나 감독한 영화를 열거할 수 있다.@external 유형에서는 Person 대상에 영화에서 연기하거나 감독한 사람을 열거할 수 있지만 이번에는 이 영화와 관련된 사람이다.필름스 서비스의 Film 유형을 인용하고 확장함으로써 데이터 그래프의 두 방향에서 사람과 영화 간의 관계를 두루 훑어볼 수 있다. 설령 서로 다른 서비스에서 해당하는 유형을 정의했다 하더라도.
    다음은 영화 서비스에 추가된 모든 새로운 형식과 추가 필드 컴파일러가 필요합니다.다음 코드를 Person에 추가합니다.
    // ...
    
    const resolvers = {
      Film: {
        actors(film) {
          return film.actors.map((actor) => ({ __typename: "Person", id: actor }));
        },
        director(film) {
          return { __typename: "Person", id: film.director };
        }
      },
      Person: {
        appearedIn(person) {
          return films.filter((film) =>
            film.actors.find((actor) => actor === person.id)
          );
        },
        directed(person) {
          return films.filter((film) => film.director === person.id);
        }
      },
      Query: {
        film(_, { id }) {
          return films.find((film) => film.id === id);
        },
        films() {
          return films;
        }
      }
    };
    
    해석Personfilms/index.jsactors 필드에서 영화 서비스 부서에서 이들에 대한 유일한 정보는 그들의 유일한 ID입니다. 하지만 이것은 괜찮습니다!Apollo Federation을 사용하여 이 필드를 해석하려면, people 서비스로 전송을 요청할 때 이 대상을 식별하기 위해 대상 (또는 대상 목록) 과 키 필드/값을 포함하는 대상을 되돌려보내기만 하면 됩니다.
    또한 directors 유형은 처음에 다른 서비스에서 정의되었지만, 우리는 인원 ID와 Film ID가 일치하거나 __typename 그룹에 나타난 영화의 일치를 통해 영화 서비스가 여기에 추가한 새로운 필드를 분석해야 한다.
    추가할 마지막 코드 Person 는 이 서비스를 시작할 director 입니다. 우리가 people 서비스에서 했던 것처럼:
    // ...
    
    const server = new ApolloServer({
      schema: buildFederatedSchema([{ typeDefs, resolvers }]),
    });
    
    server.listen({ port }).then(({ url }) => {
      console.log(`Films service ready at ${url}`);
    });
    
    게이트웨이 API에 영화 서비스를 추가해야 합니다actors.
    // ...
    
    const gateway = new ApolloGateway({
      serviceList: [
        { name: "people", url: "http://localhost:4001" },
        { name: "films", url: "http://localhost:4002" } // NEW!
      ]
    });
    
    // ...
    
    마지막으로, 우리는 films/index.js에 영화 서비스를 시작하기 위해 다른 npm 스크립트를 추가하고, 인터페이스를 통해 영화 서비스의 포트를 즉시 기다리도록 요구할 것이다.
    {
      ...
      "scripts": {
        "dev": "concurrently -k npm:dev:*",
        "dev:people": "nodemon -r esm ./people/index.js",
        "dev:films": "nodemon -r esm ./films/index.js",
        "dev:gateway": "wait-on tcp:4001 tcp:4002 && nodemon -r esm ./index.js"
      },
      ...
    }
    
    GraphQL API는 현재 GraphQL 놀이공원에서 캐릭터와 영화를 예상대로 조회할 수 있도록 준비되어 있습니다.참고로 the final version of the code here를 볼 수 있습니다.
    나는 이 글이 네가 아폴로 연맹이 얼마나 친근한지 엿볼 수 있기를 바란다. 만약 네가 이전에 아폴로 서버의 경험이 조금 있었다면.인코딩 고마워요!

    좋은 웹페이지 즐겨찾기