Я следую руководству https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-lambda-resolvers.html
И есть некоторые сомнения в использовании только switch для обработки запросов graphql.
Есть ли лучший подход для обработки более сложных запросов?





Вы можете проксировать все запросы на graphql-сервер
Сервер Apollo GraphQL обеспечивает очень хорошую настройку для развертывания сервера GraphQL в AWS Lambda.
Вам не нужно использовать одну AWS Lambda для обработки каждого запроса. Новичкам легче понять это руководство, поэтому они использовали этот подход.
Но как это реализовать в конце - решать вам. В качестве альтернативы можно было бы создать для каждого распознавателя отдельную AWS Lambda, чтобы исключить switch и следовать Принцип единой ответственности (SRP).
Да, конечная точка остается прежней. Просто AWS Lambda за резолвером отличается.
Это противоречит цели graphql: сервер GraphQL работает с одним URL / конечной точкой
@GabrielBleu лямбда не является сервером GraphQL - AppSync предоставляет реализацию GraphQL - лямбда используется преобразователем для заполнения одного поля в API. Использование разных лямбда-выражений для разных полей в API вполне разумно (они могут работать с очень разными ресурсами для разрешения данных, например, или иметь разные требования к вычислениям / памяти).
@GabrielBleu Вы должны проверить технический документ AWS AppSync.
Хорошо, вы используете несколько лямбда-выражений для преобразователей за одной конечной точкой. Следите за тем, чтобы не разделять слишком много или менее используемых лямбд, всегда будет холодный старт.
Выбор за вами, как настроить лямбда-выражение в API AppSync. Вполне разумно иметь лямбда-функцию для каждого преобразователя и иметь функцию, отвечающую за один преобразователь. В качестве альтернативы вы можете воспользоваться подходом, подобным учебному руководству, и использовать одну функцию и небольшой код маршрутизации, чтобы позаботиться о вызове правильной функции. Использование одной функции часто может дать некоторые преимущества в производительности из-за того, как работает подогрев контейнера лямбда (особенно для Java и C#, где время запуска виртуальной машины может складываться), но имеет меньшее разделение проблем.
Вот несколько подходов, которые я использовал в прошлом:
Вариант 1: JS
Этот подход использует JavaScript и должен быть знаком тем, кто раньше запускал свои собственные серверы 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. На данный момент мы должны указать 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: Перейти
Для любопытных я также успешно использовал лямбда-функции go с AppSync. Вот один подход, который мне хорошо сработал.
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. Есть много других способов сделать это, но это один метод, который хорошо сработал для меня.
Надеюсь это поможет :)
Я использовал этот подход, и он мне очень помог. apollo-server-lambda предоставляет функцию createHandler, которая соответствует подписи, необходимой для AWS Lambda.