99designs / gqlgen

go generate based graphql server library
https://gqlgen.com
MIT License
9.88k stars 1.15k forks source link

How to use this with AWS Lambda? #466

Closed csterritt closed 5 years ago

csterritt commented 5 years ago

Hey -- trying a straight server approach with gqlgen works really well, thanks!

But I'd like to set it up as an AWS Lambda service, to replace a REST service I have set up already.

However, I can't figure out how to take a query/mutation and run it. This article shows how to do it using graphql-go, but that's a different API.

Thanks!

mathewbyrne commented 5 years ago

We don't use gqlgen with Lamda internally, so not sure what's required to get it working sorry. I would recommending asking in our gitter channel to see if anyone has tried this before.

csterritt commented 5 years ago

@mathewbyrne Thanks, I may give that a try.

sdalezman commented 5 years ago

We're doing this with heavily trafficked services - we've implemented just like any other http handler, we use the proxy adapters from the aws labs to use golang http handlers.

code is something akin to:

package main

import (
    "context"

    "github.com/aws/aws-lambda-go/lambdacontext"

    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"
    "github.com/aws/aws-xray-sdk-go/xray"
    "github.com/awslabs/aws-lambda-go-api-proxy/gorillamux"
    "github.com/gorilla/mux"
)

func init() {
    // start the mux router
    r := mux.NewRouter()
    api.Routes(r) // routes just accepts a mux router to bind routes to and that's where our gqlgen handlers live
    // initialize the mux adapter so that we can use mux with lambda
    muxAdapter = gorillamux.New(r)
}

// Handler is a wrapper around lambda and mux so that we can continue to use mux via lambda
func Handler(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
    res, err := muxAdapter.Proxy(req)
    if err != nil {
        log.Println(err)
    }
    return res, err
}

func main() {
    lambda.Start(Handler)
}
mathewbyrne commented 5 years ago

Thanks @sdalezman . I'm going to mark this issue closed, we might consider including this in a documentation recipe in the future.

csterritt commented 5 years ago

Thanks @sdalezman -- that works fine! Much appreciated.

rayhaanq commented 5 years ago

Hey there, is there a repo for this showing a fully working example? @sdalezman

sdalezman commented 5 years ago

@rayhaanq unfortunately i don't have one that I can publicly share and am pretty strapped for time these days. You can use the mux router generated above to bind to a gqlgen handler like you would if you were using mux and you can do the same thing with the playground handler. Hope that helps!

ptimson commented 4 years ago

Hey there, is there a repo for this showing a fully working example? @sdalezman

A year on but this hopefully is a more complete version for ref. It combines the generated server.go file with @sdalezman solution

package main

import (
    "context"
    "log"

    "github.com/99designs/gqlgen/graphql/handler"
    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"
    "github.com/awslabs/aws-lambda-go-api-proxy/gorillamux"
    "github.com/gorilla/mux"

    "<yourRepo>/graph"
    "<yourRepo>/graph/generated"
)

var muxAdapter *gorillamux.GorillaMuxAdapter

func init() {
    r := mux.NewRouter()

    // From server.go
    schema := generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{}})
    server := handler.NewDefaultServer(schema)
    r.Handle("/query", server)
    r.Handle("/", playground.Handler("GraphQL playground", "/query"))

    muxAdapter = gorillamux.New(r)
}

func Handler(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
    rsp, err := muxAdapter.Proxy(req)
    if err != nil {
        log.Println(err)
    }
    return rsp, err
}

func main() {
    lambda.Start(Handler)
}
leventov commented 4 years ago

@ptimson I failed to make the code like the above working (I use serverless framework). The response from the server is always 404 page not found.

What worked for me (no Playground though):

import (
    "context"
    "github.com/99designs/gqlgen/handler"
    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"
    "github.com/awslabs/aws-lambda-go-api-proxy/handlerfunc"
    "<repo>/server" // includes everything that corresponds to "generated" and "graph" in ptimson's comment
    "log"
)

var apiGatewayAdapter *handlerfunc.HandlerFuncAdapter

func init() {
    schema := server.NewExecutableSchema(server.Config{Resolvers: &server.Resolver{}})
    gqlHandler := handler.GraphQL(schema)
    apiGatewayAdapter = handlerfunc.New(gqlHandler)
}

func Handler(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
    rsp, err := apiGatewayAdapter.ProxyWithContext(ctx, req)
    if err != nil {
        log.Println(err)
    }
    return rsp, err
}
markbdaniels commented 4 years ago

@leventov I was also getting a 404 with @ptimson solution but I managed to get it working by changing my serverless config -

from: events:

to: events:

Just in case someone else runs into same issue...

nguyengoi commented 4 years ago

Hi all, Anyone use the gqlgen to generate ( types/model ) schema of AWS Appsync? I try to use it but got an error with Aws directive "failed to load schema: graph/schema.graphql:747: Undefined directive aws_subscribe.exit status 1"

Should I use the gqlgen for Aws or not? Thanks

giopetris commented 3 years ago

For any other beginning at golang struggling to get this working, here's a more complete version of @ptimson's solution:

server.go:

package main

import (
    "context"
    "log"
    "net/http"
    "os"
    "strings"

    "github.com/99designs/gqlgen/graphql/handler"
    "github.com/99designs/gqlgen/graphql/playground"
    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"
    "github.com/awslabs/aws-lambda-go-api-proxy/gorillamux"
    "<yourRepo>/graph"
    "<yourRepo>/graph/generated"
    "github.com/gorilla/mux"
)

var muxRouter *mux.Router

func init() {
    muxRouter = mux.NewRouter()

    schema := generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{}})
    server := handler.NewDefaultServer(schema)
    muxRouter.Handle("/query", server)
    muxRouter.Handle("/", playground.Handler("GraphQL playground", "/query"))
}

func lambdaHandler(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
    muxAdapter := gorillamux.New(muxRouter)

    rsp, err := muxAdapter.Proxy(req)
    if err != nil {
        log.Println(err)
    }
    return rsp, err
}

func main() {
    isRunningAtLambda := strings.Contains(os.Getenv("AWS_EXECUTION_ENV"), "AWS_Lambda_")

    if isRunningAtLambda {
        lambda.Start(lambdaHandler)
    } else {
        defaultPort := "7010"
        port := os.Getenv("PORT")

        if port == "" {
            port = defaultPort
        }

        log.Printf("connect to http://localhost:%s/ for GraphQL playground", port)
        log.Fatal(http.ListenAndServe(":"+port, muxRouter))
    }
}

serverless.yml:

provider:
  name: aws
  runtime: go1.x
  lambdaHashingVersion: 20201221

package:
  patterns:
    - "!./**"
    - ./bin/**

functions:
  graphql:
    handler: bin/server
    events:
      - http: ANY /{proxy+}
      - http: ANY /

Makefile:

build:
    env GOOS=linux go build -ldflags="-s -w" -o bin/server

clean:
    rm -rf ./bin

deploy: clean build
    sls deploy --verbose
3nvi commented 3 years ago

For any other beginning at golang struggling to get this working, here's a more complete version of @ptimson's solution:

server.go:

package main

import (
  "context"
  "log"
  "net/http"
  "os"
  "strings"

  "github.com/99designs/gqlgen/graphql/handler"
  "github.com/99designs/gqlgen/graphql/playground"
  "github.com/aws/aws-lambda-go/events"
  "github.com/aws/aws-lambda-go/lambda"
  "github.com/awslabs/aws-lambda-go-api-proxy/gorillamux"
  "<yourRepo>/graph"
  "<yourRepo>/graph/generated"
  "github.com/gorilla/mux"
)

var muxRouter *mux.Router

func init() {
  muxRouter = mux.NewRouter()

  schema := generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{}})
  server := handler.NewDefaultServer(schema)
  muxRouter.Handle("/query", server)
  muxRouter.Handle("/", playground.Handler("GraphQL playground", "/query"))
}

func lambdaHandler(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
  muxAdapter := gorillamux.New(muxRouter)

  rsp, err := muxAdapter.Proxy(req)
  if err != nil {
      log.Println(err)
  }
  return rsp, err
}

func main() {
  isRunningAtLambda := strings.Contains(os.Getenv("AWS_EXECUTION_ENV"), "AWS_Lambda_")

  if isRunningAtLambda {
      lambda.Start(lambdaHandler)
  } else {
      defaultPort := "7010"
      port := os.Getenv("PORT")

      if port == "" {
          port = defaultPort
      }

      log.Printf("connect to http://localhost:%s/ for GraphQL playground", port)
      log.Fatal(http.ListenAndServe(":"+port, muxRouter))
  }
}

serverless.yml:

provider:
  name: aws
  runtime: go1.x
  lambdaHashingVersion: 20201221

package:
  patterns:
    - "!./**"
    - ./bin/**

functions:
  graphql:
    handler: bin/server
    events:
      - http: ANY /{proxy+}
      - http: ANY /

Makefile:

build:
  env GOOS=linux go build -ldflags="-s -w" -o bin/server

clean:
  rm -rf ./bin

deploy: clean build
  sls deploy --verbose

Thanks for that!

tabvn commented 2 years ago

i 'm going to implement also subscription for aws apigateway socket in this repo if someone need join let's give me a hand.

https://github.com/thebaycity/aws-graphql

mihir-mehta commented 2 years ago

I am following this code snippet , but getting "Transport not supported" error

Any help would be appreciated

tabvn commented 2 years ago

@mihir-mehta it is depend on graphql subscription client. If you are using graphql-ws

you need add this to header of AWS Apigateway websocket


return events.APIGatewayProxyResponse{Headers: map[string]string{
        "Sec-WebSocket-Protocol": "graphql-transport-ws",
    }, StatusCode: http.StatusOK, Body: body}, nil

see what i implemented here. https://github.com/thebaycity/aws-graphql/blob/master/lambda/subscription/main.go

mihir-mehta commented 2 years ago

Content-Type: application/json was missing from request headers.

Thank you