AWS Graphql lambda 查询

Kri*_*sna 7 amazon-web-services aws-lambda graphql apollo-server

我没有为此应用程序使用 AWS AppSync。我已经创建了 Graphql 模式,我已经制作了自己的解析器。对于每个创建、查询,我都创建了每个 Lambda 函数。我使用了 DynamoDB 单表概念及其全局二级索引。

创建一个 Book 项目对我来说没问题。在 DynamoDB 中,该表如下所示:在此输入图像描述

我在返回 Graphql 查询时遇到问题。从 DynamoDB 表获取后Items,我必须使用 Map 函数,然后返回Items基于 Graphql 的表type。我觉得这不是有效的方法。我不知道查询数据的最佳方式。另外,我的作者和作者查询都为空。

这是我的gitlab 分支

这是我的 Graphql 架构

import { gql } from 'apollo-server-lambda';

const typeDefs = gql`
  enum Genre {
    adventure
    drama
    scifi
  }

  enum Authors {
    AUTHOR
  }

  # Root Query - all the queries supported by the schema

  type Query {
    """
    All Authors query
    """
    authors(author: Authors): [Author]
    books(book: String): [Book]
  }

  # Root Mutation - all the mutations supported by the schema
  type Mutation {
    createBook(input: CreateBook!): Book
  }

  """
  One Author can have many books
  """
  type Author {
    id: ID!
    authorName: String
    book: [Book]!
  }

  """
  Book Schema
  """
  type Book {
    id: ID!
    name: String
    price: String
    publishingYear: String
    publisher: String
    author: [Author]
    description: String
    page: Int
    genre: [Genre]
  }

  input CreateBook {
    name: String
    price: String
    publishingYear: String
    publisher: String
    author: [CreateAuthor]
    description: String
    page: Int
    genre: [Genre]
  }

  input CreateAuthor {
    authorName: String!
  }
`;
export default typeDefs;
Run Code Online (Sandbox Code Playgroud)

这是我创建的图书项目

import AWS from 'aws-sdk';
import { v4 } from 'uuid';
import { CreateBook } from '../../generated/schema';

async function createBook(_: unknown, { input }: { input: CreateBook }) {
  const dynamoDb = new AWS.DynamoDB.DocumentClient();
  const id = v4();

  const authorsName = 
    input.author &&
    input.author.map(function (item) {
      return item['authorName'];
    });

  const params = {
    TableName: process.env.ITEM_TABLE ? process.env.ITEM_TABLE : '',
    Item: {
      PK: `AUTHOR`,
      SK: `AUTHORS#${id}`,
      GSI1PK: `BOOKS`,
      GSI1SK: `BOOK#${input.name}`,
      name: input.name,
      author: authorsName,
      price: input.price,
      publishingYear: input.publishingYear,
      publisher: input.publisher,
      page: input.page,
      description: input.description,
      genre: input.genre,
    },
  };

  await dynamoDb.put(params).promise();

  return {
    ...input,
    id,
  };
}

export default createBook;
Run Code Online (Sandbox Code Playgroud)

这是查询全书的方法

import AWS from 'aws-sdk';

async function books(_: unknown, input: { book: string }) {
  const dynamoDb = new AWS.DynamoDB.DocumentClient();

  const params = {
    TableName: process.env.ITEM_TABLE ? process.env.ITEM_TABLE : '',
    IndexName: 'GSI1',
    KeyConditionExpression: 'GSI1PK = :hkey',
    ExpressionAttributeValues: {
      ':hkey': `${input.book}`,
    },
  };

  const { Items } = await dynamoDb.query(params).promise();

  const allBooks =  // NEED TO MAP THE FUNcTION THEN RETURN THE DATA BASED ON GRAPHQL //QUERIES.
    Items &&
    Items.map((i) => {
      const genre = i.genre.filter((i) => i);
      return {
        name: i.name,
        author: i.author,
        genre,
      };
    });

  return allBooks;
}

export default books;
Run Code Online (Sandbox Code Playgroud)

这是我的作者查询和控制台结果的图像

在此输入图像描述

import AWS from 'aws-sdk';
import { Author, Authors } from '../../generated/schema';

async function authors(
  _: unknown,
  input: { author: Authors }
): Promise<Author> {
  const dynamoDb = new AWS.DynamoDB.DocumentClient();

  const params = {
    TableName: process.env.ITEM_TABLE ? process.env.ITEM_TABLE : '',
    KeyConditionExpression: 'PK = :hkey',
    ExpressionAttributeValues: {
      ':hkey': `${input.author}`,
    },
  };

  const { Items } = await dynamoDb.query(params).promise();

  console.log({ Items }); // I can see the data but don't know how to returns the data like this below type without using map function

  // type Author {
  //   id: ID!
  //   authorName: String
  //   book: [Book]!
  // }

  return Items; // return null in Graphql play ground. 
}

export default authors;
Run Code Online (Sandbox Code Playgroud)

编辑:当前解析器映射

// resolver map - src/resolvers/index.ts
const resolvers = {
  Query: {
    books,
    authors,
    author,
    book,
  },
  Mutation: {
    createBook,
  },
};
Run Code Online (Sandbox Code Playgroud)

fed*_*nev 2

TL;DR您缺少一些解析器。您的查询解析器正在尝试完成缺失解析器的工作。您的解析器必须以正确的形式返回数据。

换句话说,您的问题在于配置 Apollo Server 的解析器。据我所知,没有任何特定于 Lambda 的内容。

编写并注册缺少的解析器。

例如,GraphQL 不知道如何“解析”作者的书籍。Author {books(parent)}向 Apollo Server 的解析器映射添加一个条目。相应的解析器函数应根据您的模式要求返回书籍对象的列表(即[Books])。Apollo 的文档有一个类似的示例,您可以改编。

这是一个重构的author查询,用将要调用的解析器进行了注释:

query author(id: '1') {     # Query { author } resolver
  authorName
  books {                   # Author { books(parent) } resolver
    name
    authors {               # Book { author(parent) } resolver
      id
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Apollo Server 在查询执行期间使用解析器映射来决定为给定查询字段调用哪些解析器。地图看起来像您的架构并非巧合。使用parent、arg、context 和info参数调用解析器函数,这些参数为您的函数提供从数据源获取正确记录的上下文。

query author(id: '1') {     # Query { author } resolver
  authorName
  books {                   # Author { books(parent) } resolver
    name
    authors {               # Book { author(parent) } resolver
      id
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

您的查询解析器正在尝试做太多工作。

解析所有子字段不是作者查询解析器的工作。Apollo Server在查询执行期间会多次调用多个解析器:

您可以将 GraphQL 查询中的每个字段视为前一个类型的函数或方法,它返回下一个类型。事实上,这正是 GraphQL 的工作原理。每种类型的每个字段都由 GraphQL 服务器开发人员提供的名为解析器的函数支持。当一个字段被执行时,相应的解析器被调用以产生下一个值

Apollo 服务器将此称为解析器链。解析器将作为其参数books(parent)被调用。您可以使用作者 ID 来查找她的书籍。Authorparent

您的解析器返回值必须与架构一致。

确保您的解析器返回架构所需形状的数据。您的author解析器显然返回了一个 map {Items: [author-record]},但您的架构表明它需要是一个列表。

(如果我是您,我会将作者查询签名更改为author(PK: String, SK: String): [Author]更适合调用者的签名,例如author(id: ID): Author。返回一个对象,而不是列表。在解析器函数中隐藏 DynamoDB 实现详细信息。Apollo Server 有一个ID标量类型,该类型被序列化为一个字符串。)