Crie um back-end graphql no Golang

Hoje vamos desenvolver uma aplicação no Golang + GraphQL.

Costumamos usar o GraphQL em nossos projetos e sabemos muito sobre ele, juntamente com várias linguagens de programação: Javascript, Ruby, e agora nossas mãos chegaram ao ponto de experimentar o grupo Golang GraphQL.

Muito foi dito sobre as vantagens da linguagem de consulta GraphQL na Internet, muitos a elogiam por sua simplicidade, flexibilidade e conveniência, tanto quando usadas no servidor quanto no cliente.

Para um desenvolvimento conveniente usando o GraphQL, o GraphiQL ou o GraphQL Playground geralmente é configurado - uma interface para enviar solicitações e visualizar a documentação da API.
Para implantar um Playground localmente, basta usar esta solução de código aberto - Golang HTTP.Handler para graphl-go.

graphql playground golang

Parece um playground de corrida.

Vamos continuar escrevendo um aplicativo pequeno, um exemplo do qual descobriremos como trabalhar com o GraphQL e o Go. O código completo do aplicativo pode ser encontrado no link no Github .

Antes de tudo, iniciaremos o servidor e o playground.

func main() {
  schema, err := graphql.NewSchema(defineSchema()) //      
  if err != nil {
     log.Panic("Error when creating the graphQL schema", err)
  }

  h := handler.New(&handler.Config{
     Schema:     &schema,
     Pretty:     true,
     GraphiQL:   false,
     Playground: true,
  }) //      FormatErrorFn -    

  http.Handle("/graphql", h) //      playground    
  err = http.ListenAndServe(":8080", nil)
  if err != nil {
     log.Panic("Error when starting the http server", err)
  }
}

Nenhum backend do graphql pode ficar sem uma descrição do esquema, agora vamos analisar sua descrição.

var User = graphql.NewObject(
  graphql.ObjectConfig{
     Name: "User",
     Fields: graphql.Fields{
        "_id": &graphql.Field{
           Type: ObjectID,
        },
        "firstName": &graphql.Field{
           Type: graphql.String,
        },
        "lastName": &graphql.Field{
           Type: graphql.String,
        },
        "email": &graphql.Field{
           Type: graphql.String,
        },
     },
  },
)

var UserInput = graphql.NewInputObject(
  graphql.InputObjectConfig{
     Name: "UserInput",
     Fields: graphql.InputObjectConfigFieldMap{
        "firstName": &graphql.InputObjectFieldConfig{
           Type: graphql.String,
        },
        "lastName": &graphql.InputObjectFieldConfig{
           Type: graphql.Int,
        },
        "email": &graphql.InputObjectFieldConfig{
           Type: graphql.String,
        },
     },
  },
)

func defineSchema() graphql.SchemaConfig {
  return graphql.SchemaConfig{
     Query: graphql.NewObject(graphql.ObjectConfig{
        Name: "Query",
        Fields: graphql.Fields{
           "users": &graphql.Field{
              Name:    "users",
              Type:    graphql.NewList(User),
              Resolve: usersResolver,
           },
        },
     }),
     Mutation: graphql.NewObject(graphql.ObjectConfig{
        Name: "Mutation",
        Fields: graphql.Fields{
           "addUser": &graphql.Field{
              Name:    "addUser",
              Type:    User,
              Resolve: addUserResolver,
              Args: graphql.FieldConfigArgument{
                 "input": &graphql.ArgumentConfig{
                    Type: UserInput,
                 },
              },
           },
        },
     })}
}

Acima, descrevemos o tipo de usuário com os campos _id, firstName, lastName e email, que usaremos como o tipo de resposta às solicitações. O campo _id é do tipo ObjectID, que é um tipo personalizado, pois era necessário serializar o tipo de ObjectID nativo MongoDB, que é uma estrutura desse tipo.

type InsertOneResult struct {
  InsertedID interface{}
}

Para descrever os parâmetros de entrada na mutação para adicionar um usuário, o tipo UserInput foi criado contendo 3 campos opcionais que descrevem nosso usuário. O campo _id está ausente nesse tipo, pois será gerado no resolvedor.

Para conectar-se ao mongodb, este projeto usa o driver golang mongodb.

func usersCollection() *mongo.Collection { // ,   users    
  ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
  client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://mongo:27017"))
  if err != nil {
     log.Panic("Error when creating mongodb connection client", err)
  }
  collection := client.Database("testing").Collection("users")
  err = client.Connect(ctx)
  if err != nil {
     log.Panic("Error when connecting to mongodb", err)
  }

  return collection
}


//      
func usersResolver(_ graphql.ResolveParams) (interface{}, error) {
  ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
  collection := usersCollection()
  result, err := collection.Find(ctx, bson.D{})
  if err != nil {
     log.Print("Error when finding user", err)
     return nil, err
  }

  defer result.Close(ctx)

  var r []bson.M
  err = result.All(ctx, &r)
  if err != nil {
     log.Print("Error when reading users from cursor", err)
  }

  return r, nil
}


//     
func addUserResolver(p graphql.ResolveParams) (interface{}, error) {
  ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
  collection := usersCollection()
  //         ,   _id
  id, err := collection.InsertOne(ctx, p.Args["input"])
  if err != nil {
     log.Print("Error when inserting user", err)
     return nil, err
  }

  var result bson.M
  //       _id
  err = collection.FindOne(ctx, bson.M{"_id": id.InsertedID}).Decode(&result)
  if err != nil {
     log.Print("Error when finding the inserted user by its id", err)
     return nil, err
  }

  return result, nil
}

Aqui, processamos a solicitação de aquisição e mutação de usuários, usando o mongodb como um local para armazenar dados do usuário. Em vez deste banco de dados, você pode usar qualquer outro sem problemas, tanto não relacionais quanto relacionais, porque o GraphQL não nos limita na escolha de um banco de dados.

Eu usei docker compor para agrupar golang e mongodb. Para isso, descrevi um pequeno arquivo de configurações. Tudo está pronto. Um par de tecnologias golang mongo nos permite armazenar dados do usuário de uma forma conveniente para posterior retorno das consultas do GraphQL.

version: '3'
services:
graphql:
image: golang
volumes:
- .:/go/src
command: /bin/bash -c "cd src && go run *.go"
ports:
- 8080:8080
mongo:
image: mongo





Obviamente, esse aplicativo acabou sendo bastante simples e compacto. E isso pode ser tomado como base para o seu próximo projeto e complementado com novos tipos, solicitações e mutações e outras funcionalidades. Desta vez, não consideramos exemplos de implementação de alguns recursos mais interessantes do GraphQL, por exemplo, assinatura. Talvez eu continue com este exemplo no futuro, então deixe comentários se algo não foi suficiente para você neste artigo e, na próxima parte, consideraremos isso definitivamente.

All Articles