Erstellen Sie ein Graphql-Backend auf Golang

Heute werden wir eine Anwendung in Golang + GraphQL entwickeln.

Wir verwenden GraphQL oft in unseren Projekten und wir wissen viel darüber, haben es zusammen mit verschiedenen Programmiersprachen verwendet: Javascript, Ruby und jetzt haben unsere Hände den Punkt erreicht, an dem wir den Golang GraphQL-Haufen ausprobieren können.

Es wurde viel über die Vorteile der GraphQL-Abfragesprache im Internet gesagt, viele loben sie für ihre Einfachheit, Flexibilität und Bequemlichkeit, sowohl auf der Serverseite als auch auf dem Client.

Für eine bequeme Entwicklung mit GraphQL wird häufig GraphiQL oder GraphQL Playground konfiguriert - eine Schnittstelle zum Senden von Anforderungen und zum Anzeigen der API-Dokumentation.
Um einen solchen Spielplatz lokal bereitzustellen, reicht es aus, diese Open-Source-Lösung - Golang HTTP.Handler für Graphl-Go - zu verwenden.

graphql Spielplatz Golang

Es sieht aus wie ein Laufspielplatz.

Fahren wir mit dem Schreiben einer kleinen Anwendung fort. Ein Beispiel dafür zeigt, wie Sie mit GraphQL und Go arbeiten. Den vollständigen Anwendungscode finden Sie unter dem Link auf Github .

Zunächst starten wir den Server und den Spielplatz.

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)
  }
}

Keines der graphql-Backends kann ohne eine Beschreibung des Schemas auskommen. Jetzt werden wir seine Beschreibung analysieren.

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,
                 },
              },
           },
        },
     })}
}

Oben haben wir den Benutzertyp mit den Feldern _id, firstName, lastName und email beschrieben, die wir als Antworttyp auf Anforderungen verwenden werden. Das Feld _id ist vom Typ ObjectID, bei dem es sich um einen benutzerdefinierten Typ handelt, da der native ObjectID-Typ MongoDB, eine Struktur dieser Art, serialisiert werden musste.

type InsertOneResult struct {
  InsertedID interface{}
}

Um die Eingabeparameter für die Mutation zum Hinzufügen eines Benutzers zu beschreiben, wurde der UserInput-Typ erstellt, der 3 optionale Felder enthält, die unseren Benutzer beschreiben. Das Feld _id fehlt bei diesem Typ, da es im Resolver generiert wird.

Um eine Verbindung zu Mongodb herzustellen, verwendet dieses Projekt den Golang Mongodb-Treiber.

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
}

Hier haben wir die Anfrage zur Benutzerakquise und -mutation bearbeitet und dabei Mongodb als Speicherort für Benutzerdaten verwendet. Anstelle dieser Datenbank können Sie problemlos jede andere verwenden, sowohl nicht relationale als auch relationale, da GraphQL uns bei der Auswahl einer Datenbank nicht einschränkt.

Ich habe Docker Compose verwendet, um Golang und Mongodb zu bündeln. Dazu habe ich eine kleine Einstellungsdatei beschrieben. Alles ist fertig. Ein Paar Golang-Mongo-Technologien ermöglicht es uns, Benutzerdaten in einer bequemen Form zu speichern, um weitere Rückmeldungen von GraphQL-Abfragen zu erhalten.

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





Natürlich erwies sich diese Anwendung als recht einfach und kompakt. Und es kann als Grundlage für Ihr nächstes Projekt dienen und durch neue Typen, Anforderungen und Mutationen sowie andere Funktionen ergänzt werden. Dieses Mal haben wir keine Beispiele für die Implementierung einiger interessanterer GraphQL-Funktionen in Betracht gezogen, z. B. Abonnement. Vielleicht werde ich dieses Beispiel in Zukunft fortsetzen, also hinterlasse Kommentare, wenn dir in diesem Artikel etwas nicht genug war, und im nächsten Teil werden wir dies definitiv berücksichtigen.

All Articles