├── .gitignore ├── Gopkg.lock ├── Gopkg.toml ├── Makefile ├── README.md ├── handler ├── main.go ├── schema │ ├── main.go │ └── user.go └── services │ └── dynamodb.go └── serverless.yml /.gitignore: -------------------------------------------------------------------------------- 1 | # Serverless directories 2 | .serverless 3 | 4 | # golang output binary directory 5 | bin 6 | 7 | # golang vendor (dependencies) directory 8 | vendor -------------------------------------------------------------------------------- /Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | name = "github.com/aws/aws-lambda-go" 6 | packages = [ 7 | "events", 8 | "lambda", 9 | "lambda/messages", 10 | "lambdacontext" 11 | ] 12 | revision = "6e2e37798efbb1dfd8e9c6681702e683a6046517" 13 | version = "v1.0.1" 14 | 15 | [[projects]] 16 | name = "github.com/aws/aws-sdk-go" 17 | packages = [ 18 | "aws", 19 | "aws/awserr", 20 | "aws/awsutil", 21 | "aws/client", 22 | "aws/client/metadata", 23 | "aws/corehandlers", 24 | "aws/credentials", 25 | "aws/credentials/ec2rolecreds", 26 | "aws/credentials/endpointcreds", 27 | "aws/credentials/stscreds", 28 | "aws/defaults", 29 | "aws/ec2metadata", 30 | "aws/endpoints", 31 | "aws/request", 32 | "aws/session", 33 | "aws/signer/v4", 34 | "internal/shareddefaults", 35 | "private/protocol", 36 | "private/protocol/json/jsonutil", 37 | "private/protocol/jsonrpc", 38 | "private/protocol/query", 39 | "private/protocol/query/queryutil", 40 | "private/protocol/rest", 41 | "private/protocol/xml/xmlutil", 42 | "service/dynamodb", 43 | "service/dynamodb/dynamodbattribute", 44 | "service/sts" 45 | ] 46 | revision = "1b176c5c6b57adb03bb982c21930e708ebca5a77" 47 | version = "v1.12.70" 48 | 49 | [[projects]] 50 | name = "github.com/go-ini/ini" 51 | packages = ["."] 52 | revision = "32e4c1e6bc4e7d0d8451aa6b75200d19e37a536a" 53 | version = "v1.32.0" 54 | 55 | [[projects]] 56 | name = "github.com/graphql-go/graphql" 57 | packages = [ 58 | ".", 59 | "gqlerrors", 60 | "language/ast", 61 | "language/kinds", 62 | "language/lexer", 63 | "language/location", 64 | "language/parser", 65 | "language/printer", 66 | "language/source", 67 | "language/typeInfo", 68 | "language/visitor" 69 | ] 70 | revision = "5e7dd4fc3e87341229fa03d8f79d2ab5455963fb" 71 | version = "v0.7.4" 72 | 73 | [[projects]] 74 | name = "github.com/jmespath/go-jmespath" 75 | packages = ["."] 76 | revision = "0b12d6b5" 77 | 78 | [solve-meta] 79 | analyzer-name = "dep" 80 | analyzer-version = 1 81 | inputs-digest = "6fa3c79846d3be3a599fd22beab4c27d37c6176e9127e590d1d4f4ddd178e15b" 82 | solver-name = "gps-cdcl" 83 | solver-version = 1 84 | -------------------------------------------------------------------------------- /Gopkg.toml: -------------------------------------------------------------------------------- 1 | # Gopkg.toml example 2 | # 3 | # Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md 4 | # for detailed Gopkg.toml documentation. 5 | # 6 | # required = ["github.com/user/thing/cmd/thing"] 7 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 8 | # 9 | # [[constraint]] 10 | # name = "github.com/user/project" 11 | # version = "1.0.0" 12 | # 13 | # [[constraint]] 14 | # name = "github.com/user/project2" 15 | # branch = "dev" 16 | # source = "github.com/myfork/project2" 17 | # 18 | # [[override]] 19 | # name = "github.com/x/y" 20 | # version = "2.4.0" 21 | 22 | 23 | [[constraint]] 24 | name = "github.com/aws/aws-lambda-go" 25 | version = "^1.0.1" 26 | 27 | [[constraint]] 28 | name = "github.com/aws/aws-sdk-go" 29 | version = "1.12.70" 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | dep ensure 3 | env GOOS=linux go build -ldflags="-s -w" -o bin/handler handler/main.go -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Serverless Golang + GraphQL + DynamoDB template 2 | 3 | ### Deploying 4 | ```sh 5 | make && serverless deploy 6 | ``` 7 | 8 | ### Using as Serverless framework template 9 | ```sh 10 | serverless create --template-url https://github.com/RafalWilinski/serverless-go-graphql 11 | ``` 12 | -------------------------------------------------------------------------------- /handler/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | 7 | "serverless-go-graphql/handler/schema" 8 | 9 | "github.com/aws/aws-lambda-go/events" 10 | "github.com/aws/aws-lambda-go/lambda" 11 | "github.com/graphql-go/graphql" 12 | ) 13 | 14 | type RequestBody struct { 15 | Query string `json:"query"` 16 | VariableValues map[string]interface{} `json:"variables"` 17 | OperationName string `json:"operationName"` 18 | } 19 | 20 | func executeQuery(request RequestBody, schema graphql.Schema) *graphql.Result { 21 | result := graphql.Do(graphql.Params{ 22 | Schema: schema, 23 | VariableValues: request.VariableValues, 24 | RequestString: request.Query, 25 | OperationName: request.OperationName, 26 | }) 27 | 28 | return result 29 | } 30 | 31 | // Handler of HTTP event 32 | func Handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { 33 | requestBody := RequestBody{} 34 | err := json.Unmarshal([]byte(request.Body), &requestBody) 35 | 36 | if err != nil { 37 | return events.APIGatewayProxyResponse{Body: err.Error(), StatusCode: 500}, err 38 | } 39 | 40 | graphQLResult := executeQuery(requestBody, schema.Schema) 41 | responseJSON, err := json.Marshal(graphQLResult) 42 | 43 | if err != nil { 44 | return events.APIGatewayProxyResponse{Body: err.Error(), StatusCode: 400}, err 45 | } 46 | 47 | return events.APIGatewayProxyResponse{Body: string(responseJSON[:]), StatusCode: 200}, nil 48 | } 49 | 50 | func main() { 51 | lambda.Start(Handler) 52 | } 53 | -------------------------------------------------------------------------------- /handler/schema/main.go: -------------------------------------------------------------------------------- 1 | package schema 2 | 3 | import ( 4 | "serverless-go-graphql/handler/services" 5 | 6 | "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute" 7 | "github.com/graphql-go/graphql" 8 | ) 9 | 10 | var rootQuery = graphql.NewObject(graphql.ObjectConfig{ 11 | Name: "RootQuery", 12 | Fields: graphql.Fields{ 13 | "getUser": &graphql.Field{ 14 | Type: graphQLUserType, 15 | Description: "Get single user", 16 | Args: graphql.FieldConfigArgument{ 17 | "id": &graphql.ArgumentConfig{ 18 | Type: graphql.String, 19 | }, 20 | }, 21 | Resolve: func(params graphql.ResolveParams) (interface{}, error) { 22 | user := User{} 23 | id, _ := params.Args["id"].(string) 24 | result, err := services.GetUser(id) 25 | 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | err = dynamodbattribute.UnmarshalMap(result.Item, &user) 31 | return user, err 32 | }, 33 | }, 34 | }, 35 | }) 36 | 37 | var rootMutation = graphql.NewObject(graphql.ObjectConfig{ 38 | Name: "RootMutation", 39 | Fields: graphql.Fields{ 40 | "createUser": &graphql.Field{ 41 | Type: graphql.String, 42 | Resolve: func(params graphql.ResolveParams) (interface{}, error) { 43 | return "hello", nil 44 | }, 45 | }, 46 | }, 47 | }) 48 | 49 | // Schema - GraphQL root schema 50 | var Schema, _ = graphql.NewSchema(graphql.SchemaConfig{ 51 | Query: rootQuery, 52 | Mutation: rootMutation, 53 | }) 54 | -------------------------------------------------------------------------------- /handler/schema/user.go: -------------------------------------------------------------------------------- 1 | package schema 2 | 3 | import "github.com/graphql-go/graphql" 4 | 5 | type User struct { 6 | id string `json:"id"` 7 | login string `json:"login"` 8 | password string `json:"password"` 9 | } 10 | 11 | var graphQLUserType = graphql.NewObject(graphql.ObjectConfig{ 12 | Name: "User", 13 | Fields: graphql.Fields{ 14 | "id": &graphql.Field{ 15 | Type: graphql.String, 16 | }, 17 | "login": &graphql.Field{ 18 | Type: graphql.String, 19 | }, 20 | "password": &graphql.Field{ 21 | Type: graphql.String, 22 | }, 23 | }, 24 | }) 25 | -------------------------------------------------------------------------------- /handler/services/dynamodb.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "github.com/aws/aws-sdk-go/aws" 5 | "github.com/aws/aws-sdk-go/aws/session" 6 | "github.com/aws/aws-sdk-go/service/dynamodb" 7 | ) 8 | 9 | func createSession() *dynamodb.DynamoDB { 10 | sess, _ := session.NewSession(&aws.Config{ 11 | Region: aws.String("us-east-1")}, 12 | ) 13 | 14 | return dynamodb.New(sess) 15 | } 16 | 17 | func GetUser(id string) (*dynamodb.GetItemOutput, error) { 18 | svc := createSession() 19 | 20 | return svc.GetItem(&dynamodb.GetItemInput{ 21 | TableName: aws.String("go-graphql-users"), 22 | Key: map[string]*dynamodb.AttributeValue{ 23 | "id": { 24 | S: aws.String(id), 25 | }, 26 | }, 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /serverless.yml: -------------------------------------------------------------------------------- 1 | service: serverless-go-graphql 2 | 3 | provider: 4 | name: aws 5 | region: us-east-1 6 | runtime: go1.x 7 | iamRoleStatements: 8 | - Effect: "Allow" 9 | Action: 10 | - "dynamodb:*" 11 | Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/go-graphql-users" 12 | - Effect: "Allow" 13 | Action: 14 | - "dynamodb:*" 15 | Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/go-graphql-users" 16 | 17 | package: 18 | exclude: 19 | - ./** 20 | include: 21 | - ./bin/** 22 | 23 | functions: 24 | handler: 25 | handler: bin/handler 26 | events: 27 | - http: 28 | path: /graphql 29 | method: POST 30 | 31 | resources: 32 | Resources: 33 | UsersTable: 34 | Type: AWS::DynamoDB::Table 35 | Properties: 36 | AttributeDefinitions: 37 | - AttributeName: id 38 | AttributeType: S 39 | KeySchema: 40 | - AttributeName: id 41 | KeyType: HASH 42 | ProvisionedThroughput: 43 | ReadCapacityUnits: 1 44 | WriteCapacityUnits: 1 45 | --------------------------------------------------------------------------------