ค้นหาเว็บไซต์

javascript - วิธีการกำหนดประเภทสำหรับบริบทของตัวแก้ไข Apollo?


ฉันมี subgraph ของ Apollo GraphQL แบบง่าย ๆ ซึ่งฉันต้องการกำหนดประเภทสำหรับอาร์กิวเมนต์ context ในตัวแก้ปัญหาของฉัน

ฉันสร้างตัวแก้ไขปัญหาและตั้งค่าประเภทสำหรับบริบทดังนี้:

interface Context {
  dataSources: {
    shopify: Shopify;
  }
}

const server = new ApolloServer({
  schema: buildSubgraphSchema({ 
    typeDefs,
    resolvers: {
      Query: {
        shop(_, _args, ctx: Context) {
          console.log("ctx:", ctx)
        },
      }
    }
  }),
  dataSources: () => ({
    shopify: new Shopify()
  })
});

อย่างไรก็ตาม ดูเหมือนว่าอินเทอร์เฟซ Context ที่ฉันกำหนดนั้นไม่เข้ากับประเภทที่ตัวแก้ไขของฉันคาดหวัง และฉันได้รับข้อผิดพลาดดังต่อไปนี้:

Argument of type '{ typeDefs: any; resolvers: { Query: { shop(_: any, _args: any, ctx: Context): void; }; }; }' is not assignable to parameter of type 'DocumentNode | (GraphQLSchemaModule | DocumentNode)[] | LegacySchemaModule'.
  Types of property 'resolvers' are incompatible.
    Property 'Query' is incompatible with index signature.
      Type '{ shop(_: any, _args: any, ctx: Context): void; }' is not assignable to type 'GraphQLScalarType<unknown, unknown> | { [enumValue: string]: string | number; } | { [fieldName: string]: GraphQLFieldResolver<any, unknown, any, unknown> | { requires?: string | undefined; resolve: GraphQLFieldResolver<...>; }; }'.
        Types of property 'shop' are incompatible.
          Type '(_: any, _args: any, ctx: Context) => void' is not assignable to type 'string | number | GraphQLFieldResolver<any, unknown, any, unknown> | { requires?: string | undefined; resolve: GraphQLFieldResolver<any, unknown, any, unknown>; } | undefined'.
            Type '(_: any, _args: any, ctx: Context) => void' is not assignable to type 'GraphQLFieldResolver<any, unknown, any, unknown>'.
              Types of parameters 'ctx' and 'context' are incompatible.
                Type 'unknown' is not assignable to type 'Context'.ts(2345)

ฉันจะตั้งค่าประเภทบริบทสำหรับตัวแก้ปัญหา (resolver) ของฉันได้อย่างไร เพื่อให้ฉันสามารถเข้าถึงอินเทอร์เฟซและแหล่งข้อมูลต่างๆ เป็นต้น?

คำตอบ #1

โอเค ฉันคิดว่าฉันเจอวิธีแก้ปัญหาที่ดีกว่าแล้ว

สิ่งแรกก่อน เรามาเข้าใจจริงๆ ว่า buildSubgraphSchema สามารถรับอะไรได้บ้าง:

buildSubgraphSchema(
  modulesOrSDL:
    | (GraphQLSchemaModule | DocumentNode)[] //interesting...
    | DocumentNode
    | LegacySchemaModule, //our tried and true way
): GraphQLSchema

ปัญหาคือเรากำลังพยายามส่งค่า LegacySchemaModule ({typeDefs, resolvers}) แต่เนื่องจาก <unknown> generic ที่ถูกส่งเข้าไปใน GraphQLResovlerMap ทำให้เกิดความขัดแย้งกับ Context ที่กำหนดไว้อย่างชัดเจน

type LegacySchemaModule = {
  typeDefs: DocumentNode | DocumentNode[];
  resolvers?: GraphQLResolverMap<unknown>; //note `unknown`, the root of our typing errors
};

สิ่งที่เราจำเป็นต้องใช้ในการดำเนินการตามบริบทภายใน resolver จริงๆ คือ GraphQLSchemaModule ที่กำหนดดังนี้:

export interface GraphQLSchemaModule {
  typeDefs: DocumentNode;
  resolvers?: GraphQLResolverMap<any>; //note the `any` here
}

ดังนั้นกับตัวแก้ไขที่กำหนดไว้แบบนี้:

export const resolvers: GraphQLResolverMap<Context> = {
  Query: {
    shop(_, _args, ctx) {
      console.log(ctx.datasources.shopify); // Shopify
    },
  },
};

ตอนนี้เราสามารถส่งผ่านอาเรย์ของ (GraphQLSchemaModule | DocumentNode)[] ได้แล้ว:

const server = new ApolloServer({ 
    schema: buildSubgraphSchema([ //note the array
      {
        typeDefs, //DocumentNode
        resolvers, //GraphQLSchemaModule, in this case GraphQLResolverMap<Context>
      },
    ]),
  });

นั่นใช้ได้สำหรับฉัน ไม่มีข้อผิดพลาดของ ts และเซิร์ฟเวอร์เริ่มทำงานได้ดี แต่อย่างไรก็ตาม เปิดรับข้อเสนอแนะจากชุมชนเกี่ยวกับรูปแบบที่ดีกว่าในที่นี้

ในท้ายที่สุดฉันคิดว่าเครื่องมือสร้างโค้ดถูกออกแบบมาเพื่อแก้ไขปัญหาเหล่านี้มากมาย ตอนนี้ฉันกำลังเข้าไปสำรวจเรื่องนี้: https://the-guild.dev/graphql/codegen

สนุกนะ!

คำตอบ #2

คลาส ApolloServer เป็นแบบทั่วไป คุณควรกำหนดบริบทที่กำหนดเองของคุณเป็นพารามิเตอร์ชนิด ดูเอกสารและยูนิตเทสต์เหล่านี้

interface Context {
  dataSources: {
    shopify: Shopify;
  }
}

const server = new ApolloServer<Context>({
//                             ^^^^^^^^^
  schema: buildSubgraphSchema({ 
    typeDefs,
    resolvers: {
      Query: {
        shop(_, _args, ctx: Context) {
          console.log("ctx:", ctx)
        },
      }
    }
  }),
  dataSources: () => ({
    shopify: new Shopify()
  })
});