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.