简体   繁体   中英

AWS Graphql lambda query

I am not using AWS AppSync for this app . I have created Graphql schema, I have made my own resolvers. For each create, query, I have made each Lambda functions. I used DynamoDB Single table concept and it's Global secondary indexes.

It was ok for me, to create an Book item. In DynamoDB, the table looks like this: 在此处输入图像描述 .

I am having issue with the return Graphql queries. After getting the Items from DynamoDB table, I have to use Map function then return the Items based on Graphql type . I feel like this is not efficient way to do that. Idk the best way query data. Also I am getting null both author and authors query.

This is my gitlab-branch .

This is my Graphql Schema

 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;

This is I created the Book Item

 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;

This is how query the All Book

 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;

This my Author query and Image of the console result

在此处输入图像描述

 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;

Edit: current resolver map

// resolver map - src/resolvers/index.ts
const resolvers = {
  Query: {
    books,
    authors,
    author,
    book,
  },
  Mutation: {
    createBook,
  },
};

TL;DR You are missing some resolvers. Your query resolvers are trying to do the job of the missing resolvers. Your resolvers must return data in the right shape.

In other words, your problems are with configuring Apollo Server's resolvers. Nothing Lambda-specific, as far as I can tell.

Write and register the missing resolvers.

GraphQL doesn't know how to "resolve" an author's books, for instance. Add a Author {books(parent)} entry to Apollo Server's resolver map . The corresponding resolver function should return a list of book objects (ie [Books] ), as your schema requires. Apollo's docs have a similar example you can adapt.

Here's a refactored author query, commented with the resolvers that will be called:

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

Apollo Server uses the resolver map during query execution to decide what resolvers to call for a given query field. It's not a coincidence that the map looks like your schema. Resolver functions are called with parent, arg, context and info arguments, which give your functions the context to fetch the right records from the data source.

// resolver map - passed to the Apollo Server constructor
const resolvers = {
  Query: {
    books,
    authors,
    author,
    book,
  },

  Author: {
    books(parent) { getAuthorBooks(parent); }, // parent is the author - resolver should return a list of books
  },

  Book: {
    authors(parent) { getBookAuthors(parent); }, // parent is the book - resolver should return a list of authors
  },
};

Your query resolvers are trying to do too much work.

It's not the author query resolver's job to resolve all the child fields. Apollo Server will call multiple resolvers multiple times during query execution :

You can think of each field in a GraphQL query as a function or method of the previous type which returns the next type. In fact, this is exactly how GraphQL works. Each field on each type is backed by a function called the resolver which is provided by the GraphQL server developer. When a field is executed, the corresponding resolver is called to produce the next value

Apollo Server calls this the resolver chain . The books(parent) resolver will be invoked with Author as its parent argument. You can use the author id to look up her books.

Your resolver return values must be consistent with the schema.

Make sure your resolvers are returning data in the shape required by the schema. Your author resolver is apparently returning a map {Items: [author-record]} , but your schema says it needs to be a list.

(If I were you, I would change the author query signature from author(PK: String, SK: String): [Author] to something more caller-friendly like author(id: ID): Author . Return an Object, not a List. Hide the DynamoDB implementation details in the resolver function. Apollo Server has a ID scalar type that is serialised as a String .)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM