Lambda Server in Go

Hi guys!
I have been really occupied in the last months and wasn’t able to make a lot of changes. But now I’m glad to announce I was able to build a lambda server generator (using gqlgen as inspiration).
Unfortunately I was not able to create a simple plugin for gqlgen due to some issues with validation.

Following are obviously breaking changes.
Unfortunately I can’t edit the original post but here is a short run-through of what to expect.

dgraph-lambda-go is now able to generate resolvers just based on your schema that you use for your dgraph server.

  • To install dgraph-lambda-go run the command go get github.com/schartey/dgraph-lambda-go in your project directory.
  • Then initialize the project by running go run github.com/schartey/dgraph-lambda-go init.
  • Edit the lambda.yaml file. Important: Change the path to your graphql schema. Everything else can stay as is.
  • Generate types and resolvers go run github.com/schartey/dgraph-lambda-go generate
  • Implement your lambda resolvers
  • Run your server go run server.go

The generator will generate resolvers for

  • Fields
  • Queries
  • Mutations
  • Webhooks
  • Middleware

Also you can now use a graphql and dql client of your choice by adding it to the Resolver struct.

Example:

Schema:

type User @lambdaOnMutate(add: true, update: false, delete: true) {
    id: ID!
    username: String!
    """
    @middleware(["auth"])
    """
    secret: String @lambda
}

input CreateUserInput {
    username: String!
    secret: String
}

type Query {
    """
    @middleware(["auth"])
    """
    randomUser(seed: String): User @lambda
}

type Mutation {
    """
    @middleware(["auth"])
    """
    createUser(input: CreateUserInput!): User @lambda
}

Generated Resolvers with example response:
FieldResolver

func (f *FieldResolver) User_secret(ctx context.Context, parents []string, authHeader api.AuthHeader) ([]string, error) { 
	var secrets []string
    for _, userParent := range userParents {
        secrets = append(secrets, fmt.Sprintf("Secret - %s", userParent.Id))
    }
    return secrets, nil
}

Query Resolver

func (q *QueryResolver) Query_randomUser(ctx context.Context, seed string, authHeader api.AuthHeader) (*model.User, error) { 
	nameGenerator := namegenerator.NewNameGenerator(seed)
    name := nameGenerator.Generate()

    user := &model.User{
        Id:       "0x1",
        Username: name,
    }
    return user, nil
}

Mutation Resolver

func (q *MutationResolver) Mutation_createUser(ctx context.Context, input *model.CreateUserInput, authHeader api.AuthHeader) (*model.User, error) {
    user := &User{
        Id:       "0x1",
        Username: createUserInput.Username,
    }
	return user, nil
}

Webhook Resolver

func (w *WebhookResolver) Webhook_User(ctx context.Context, event api.Event) error {
    // Send Email
	return nil
}

Middleware

func (m *MiddlewareResolver) Middleware_auth(md *api.MiddlewareData) error {
    // Check Token
    valid := true //false
    if valid {
    	md.Ctx = context.WithValue(md.Ctx, "logged_in", "true")
        return nil
    } else {
        return errors.New("Token invalid!")
    }
}

Inject custom dependencies

Typically you want to at least inject a graphql/dql client into your resolvers. To do so just add your client to the Resolver struct

// Add objects to your desire
type Resolver struct {
    Dql *dgo.Dgraph
}

and pass the client to the executor in your generated server.go file

dql := NewDqlClient()
resolver := &resolvers.Resolver{ Dql: dql}
executer := generated.NewExecuter(resolver)

Then you can access the client in your resolvers like this

func (q *QueryResolver) Query_randomUser(ctx context.Context, seed string, authHeader api.AuthHeader) (*model.User, error) {
    // Oversimplified
    user, err := s.dql.NewTxn().Do(ctx, req)
	if err != nil {
		return nil, &api.LambdaError{ Underlying: errors.New("User not found"), Status: api.NOT_FOUND}
	}
    return user, nil
}

Be aware that I did some basic testing and will also be using this solution for my own projects, but there might be some issues that I’m still missing. I will try to add automated tests and do some more manual testing.

Let me know about features you would like to see.
If you have a try, please leave some feedback, so I can improve whichever issues you may have.

2 Likes