NestJS와 함께 relay-graphql-js 사용

16953 단어 nestjsrelaygraphql
GraphQL API를 빌드하는 동안 릴레이 서버 사양을 준수해야 하는 경우 특히 연결 사양에 따라 페이지 매김을 처리할 때 일부 상용구 코드를 피할 수 있도록 여러 도우미를 제공하므로 이것을 사용할 수 있습니다relay-graphql library.

그러나 객체 지향 프로그래밍 패러다임에 중점을 두고 있는 NestJS GraphQL 모듈과 함께 사용하면 몇 가지 문제가 발생할 수 있습니다.

진짜 문제는 주어진 엔터티 연결 반환 유형을 지정하여 NestJS가 스키마와 제대로 작동할 수 있도록 하는 방법입니다.

그래서 우리가 할 수 있는 일은 Relay의 연결 인터페이스를 구현하는 Connection 클래스를 반환하는 일반 함수를 정의하는 것입니다.

import { Type } from "@nestjs/common";
import { Field, ObjectType } from "@nestjs/graphql";
import { Connection as RelayConnection, Edge as RelayEdge, PageInfo as RelayPageInfo } from "graphql-relay";

export function Connection<GraphQLObject>(GenericClass?: Type<GraphQLObject>) {
  @ObjectType({ isAbstract: true })
  abstract class IConnection implements RelayConnection<GraphQLObject> {
    @Field(() => [Edge], { nullable: false })
    edges: Array<RelayEdge<GraphQLObject>>

    @Field(() => PageInfo, { nullable: false })
    pageInfo: PageInfo;
  }

  return IConnection
}


이 클래스IConnection는 "graphql-relay"패키지에서 가져오는 RelayConnection을 구현하고 있습니다.

우리는 edges 필드가 [Edge] 유형임을 정의하고 있습니다. 함수에서 정의해 보겠습니다.

@ObjectType({ isAbstract: true })
abstract class Edge<GraphQLObject> implements RelayEdge<GraphQLObject> {
    @Field(() => GenericClass, { nullable: false })
    node: GraphQLObject

    @Field(() => String, { nullable: false })
    cursor: string
}


그리고 pageInfo 필드가 PageInfo 를 반환하므로 이를 정의해 보겠습니다.

@ObjectType({ isAbstract: true })
class PageInfo implements RelayPageInfo {
    @Field(() => String, { nullable: true })
    startCursor: string;

    @Field(() => String, { nullable: true })
    endCursor: string;

    @Field(() => Boolean, { nullable: false })
    hasPreviousPage: boolean;

    @Field(() => Boolean, { nullable: false })
    hasNextPage: boolean;
}


이제 함수Connection가 다음과 같아야 합니다.

export function Connection<GraphQLObject>(GenericClass?: Type<GraphQLObject>) {
    @ObjectType({ isAbstract: true })
    class PageInfo implements RelayPageInfo {
        @Field(() => String, { nullable: true })
        startCursor: string;

        @Field(() => String, { nullable: true })
        endCursor: string;

        @Field(() => Boolean, { nullable: false })
        hasPreviousPage: boolean;

        @Field(() => Boolean, { nullable: false })
        hasNextPage: boolean;
    }

    @ObjectType({ isAbstract: true })
    abstract class Edge<GraphQLObject> implements RelayEdge<GraphQLObject> {
        @Field(() => GenericClass, { nullable: false })
        node: GraphQLObject

        @Field(() => String, { nullable: false })
        cursor: string
    }

    @ObjectType({ isAbstract: true })
    abstract class IConnection implements RelayConnection<GraphQLObject> {
        @Field(() => [Edge], { nullable: false })
        edges: Array<RelayEdge<GraphQLObject>>

        @Field(() => PageInfo, { nullable: false })
        pageInfo: PageInfo;
    }

    return IConnection
}


단순히 일반 클래스와 분리 클래스를 사용하지 않는 이유가 궁금할 것입니다. 불행히도 typescript에는 이 제네릭 클래스가 원하는 대로 작동하지 않는 몇 가지 제한 사항이 있습니다. (당신은 그것에 대해 조금 더 읽을 수 있습니다 here).

이제 연결 사양에 따라 페이지 매김을 구현해야 할 때마다 프로젝트의 각 엔터티에 대해 수행해야 하는 유일한 작업은 다음과 같습니다.
user.connection.ts를 만들고 다음과 같이 정의할 수 있습니다.

import { ObjectType } from "@nestjs/graphql";
import {Connection} from "../../_types/models/connection.model";
import { User } from "./user.model";

@ObjectType()
export class UserConnection extends Connection<User>(User) {
}



리졸버에서 다음과 같이 할 수 있습니다.

import { connectionFromPromisedArray } from "graphql-relay";

/**
* ... rest of your code ...
*/

@Query(() => FinancialRecordConnection)
async users(@Args({ type: () => ConnectionArguments }) args: ConnectionArguments) {
    return connectionFromPromisedArray(this.usersService?.list(), args)
}


메모:



다음과 같은 오류가 발생하면 함수Connection에서:

Return type of exported function has or is using private name 'IConnection'.ts(4060)



두 가지 옵션이 있습니다.
  • tsconfig.json 파일로 이동하여 falsedeclarations 속성으로 설정합니다.
  • Connection 를 사용하여 any 함수의 반환을 명시적으로 입력합니다.
  • 좋은 웹페이지 즐겨찾기