处理 aws lambda 的 graphql 的最佳方法?

Best approach to handle graphql for aws lambda?

我正在学习教程https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-lambda-resolvers.html

并且对仅使用 switch 来处理 graphql 查询有一些疑问。

是否有更好的方法来处理更复杂的请求?

您可以将所有查询代理到 graphql-server

Apollo GraphQL Server 提供了在 AWS Lambda 中部署 GraphQL 服务器的非常好的设置。

您不会被迫使用一个 AWS Lambda 来处理每个请求。对于本教程,新手更容易理解它,因此他们使用了这种方法。

不过最后怎么实现就看你自己了。另一种方法是为每个解析器创建一个单独的 AWS Lambda 以消除 switch 并遵循 Single Responsibility Principle (SRP).

关于如何在 AppSync 中设置 lambda 的选择权在您手中 API。每个解析器都有一个 lambda 函数并且有一个函数负责单个解析器是完全合理的。您也可以采用类似本教程的方法,使用单个函数和一些轻量级路由代码来调用正确的函数。由于 lambda 的容器预热工作原理(特别是 Java 和 C#,其中 VM 启动时间可能会增加),使用单个函数通常可以提供一些性能优势,但关注点分离较少。

以下是我过去采用的一些方法:

选项一:JS

这种方法使用 Java脚本,之前 运行 拥有自己的 GraphQL 服务器的人应该会感到熟悉。

const Resolvers = {
  Query: {
    me: (source, args, identity) => getLoggedInUser(args, identity)
  },
  Mutation: {
    login: (source, args, identity) => loginUser(args, identity)
  }
}

exports.handler = (event, context, callback) => {
    // We are going to wire up the resolver to give all this information in this format.
    const { TypeName, FieldName, Identity, Arguments, Source } = event

    const typeResolver = Resolvers[TypeName]
    if (!typeResolver) {
      return callback(new Error(`No resolvers found for type: "${TypeName}"`))
    }
    const fieldResolver = typeResolver[FieldName]
    if (!fieldResolver) {
      return callback(new Error(`No resolvers found for field: "${FieldName}" on type: "${TypeName}"`), null)
    }
    // Handle promises as necessary.
    const result = fieldResolver(Source, Arguments, Identity);
    return callback(null, result)
};

然后您可以使用 AppSync 中的标准 lambda 解析器。现在我们必须手动提供 TypeName 和 FieldName。

#**
    The value of 'payload' after the template has been evaluated
    will be passed as the event to AWS Lambda.
*#
{
    "version" : "2017-02-28",
    "operation": "Invoke",
    "payload": {
        "TypeName": "Query",
        "FieldName": "me",
        "Arguments": $util.toJson($context.arguments),
        "Identity": $util.toJson($context.identity),
        "Source": $util.toJson($context.source)
    }
}

选项 2:开始

出于好奇,我还通过 AppSync 成功地使用了 go lambda 函数。这是一种对我很有效的方法。

package main

import (
  "context"
  "fmt"

  "github.com/aws/aws-lambda-go/lambda"
  "github.com/fatih/structs"
  "github.com/mitchellh/mapstructure"
)

type GraphQLPayload struct {
  TypeName    string                 `json:"TypeName"`
  FieldName   string                 `json:"FieldName"`
  Arguments   map[string]interface{} `json:"Arguments"`
  Source      map[string]interface{} `json:"Source"`
  Identity    map[string]interface{} `json:"Identity"`
}

type ResolverFunction func(source, args, identity map[string]interface{}) (data map[string]interface{}, err error)

type TypeResolverMap = map[string]ResolverFunction

type SchemaResolverMap = map[string]TypeResolverMap

func resolverMap() SchemaResolverMap {
  return map[string]TypeResolverMap{
    "Query": map[string]ResolverFunction{
      "me": getLoggedInUser,
    },
  }
}

func Handler(ctx context.Context, event GraphQLPayload) (map[string]interface{}, error) {
  // Almost the same as the JS option.
  resolvers := resolverMap()
  typeResolver := resolvers[event.TypeName]
  if typeResolver == nil {
    return nil, fmt.Errorf("No type resolver for type " + event.TypeName)
  }
  fieldResolver := typeResolver[event.FieldName]
  if fieldResolver == nil {
    return nil, fmt.Errorf("No field resolver for field " + event.FieldName)
  }
  return fieldResolver(event.Source, event.Arguments, event.Identity)
}

func main() {
  lambda.Start(Handler)
}

/**
* Resolver Functions
 */

/**
 * Get the logged in user
 */
func getLoggedInUser(source, args, identity map[string]interface{}) (data map[string]interface{}, err error) {

  // Decode the map[string]interface{} into a struct I defined
  var typedArgs myModelPackage.GetLoggedInUserArgs
  err = mapstructure.Decode(args, &typedArgs)
  if err != nil {
    return nil, err
  }

  // ... do work
  res, err := auth.GetLoggedInUser()
  if err != nil {
    return nil, err
  }

  // Map the struct back to a map[string]interface{}
  return structs.Map(out), nil
}

// ... Add as many more as needed

然后您可以使用选项 1 中使用的相同解析器模板。还有许多其他方法可以做到这一点,但这是一种对我来说效果很好的方法。

希望对您有所帮助:)