1
1

Go言語(Golang)における、RestAPI、GraphQL、gRPCについて

Last updated at Posted at 2024-08-31

RestAPI

特徴

  1. エンドポイントベース: 各リソースに対して個別のエンドポイントが存在します(例: /users, /posts)。
  2. HTTPメソッドの使用: リソースの操作はHTTPメソッド(GET, POST, PUT, DELETE)を用いて行います。
  3. ステートレス: 各リクエストは独立しており、サーバーはクライアントの状態を保持しません。
  4. キャッシュ可能: HTTPのキャッシュ機能を活用できます。

書き方の例

以下は、Golangを使用したシンプルなRestAPIの例です。

package main

import (
    "encoding/json"
    "net/http"
    "strconv"

    "github.com/gorilla/mux"
)

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

var users = []User{
    {ID: 1, Name: "Alice"},
    {ID: 2, Name: "Bob"},
}

func getUsers(w http.ResponseWriter, r *http.Request) {
    json.NewEncoder(w).Encode(users)
}

func getUser(w http.ResponseWriter, r *http.Request) {
    params := mux.Vars(r)
    id, _ := strconv.Atoi(params["id"])
    for _, user := range users {
        if user.ID == id {
            json.NewEncoder(w).Encode(user)
            return
        }
    }
    http.Error(w, "User not found", http.StatusNotFound)
}

func createUser(w http.ResponseWriter, r *http.Request) {
    var newUser User
    json.NewDecoder(r.Body).Decode(&newUser)
    users = append(users, newUser)
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(newUser)
}

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/users", getUsers).Methods("GET")
    r.HandleFunc("/users/{id}", getUser).Methods("GET")
    r.HandleFunc("/users", createUser).Methods("POST")

    http.ListenAndServe(":3000", r)
}

GraphQL

特徴

  1. 単一エンドポイント: 全てのリクエストは単一のエンドポイントを通じて行われます(例: /graphql)。
  2. 柔軟なクエリ: クライアントは必要なデータを正確に指定して取得できます。
  3. 型システム: スキーマに基づき、データの型が明確に定義されます。
  4. リアルタイム更新: サブスクリプションを使用してリアルタイムのデータ更新が可能です。

書き方の例

以下は、GolangとGraphQLを使用したシンプルなGraphQL APIの例です。

package main

import (
    "github.com/graphql-go/graphql"
    "github.com/graphql-go/handler"
    "net/http"
)

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

var users = []User{
    {ID: 1, Name: "Alice"},
    {ID: 2, Name: "Bob"},
}

var userType = graphql.NewObject(graphql.ObjectConfig{
    Name: "User",
    Fields: graphql.Fields{
        "id": &graphql.Field{
            Type: graphql.Int,
        },
        "name": &graphql.Field{
            Type: graphql.String,
        },
    },
})

var queryType = graphql.NewObject(graphql.ObjectConfig{
    Name: "Query",
    Fields: graphql.Fields{
        "users": &graphql.Field{
            Type: graphql.NewList(userType),
            Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                return users, nil
            },
        },
        "user": &graphql.Field{
            Type: userType,
            Args: graphql.FieldConfigArgument{
                "id": &graphql.ArgumentConfig{
                    Type: graphql.Int,
                },
            },
            Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                id, ok := p.Args["id"].(int)
                if ok {
                    for _, user := range users {
                        if user.ID == id {
                            return user, nil
                        }
                    }
                }
                return nil, nil
            },
        },
    },
})

var mutationType = graphql.NewObject(graphql.ObjectConfig{
    Name: "Mutation",
    Fields: graphql.Fields{
        "addUser": &graphql.Field{
            Type: userType,
            Args: graphql.FieldConfigArgument{
                "name": &graphql.ArgumentConfig{
                    Type: graphql.String,
                },
            },
            Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                name, _ := p.Args["name"].(string)
                newUser := User{ID: len(users) + 1, Name: name}
                users = append(users, newUser)
                return newUser, nil
            },
        },
    },
})

func main() {
    schema, _ := graphql.NewSchema(graphql.SchemaConfig{
        Query:    queryType,
        Mutation: mutationType,
    })

    h := handler.New(&handler.Config{
        Schema: &schema,
        Pretty: true,
    })

    http.Handle("/graphql", h)
    http.ListenAndServe(":3000", nil)
}

gRPC

特徴

  1. プロトコルバッファ: データのシリアライズ形式としてプロトコルバッファ(Protocol Buffers)を使用します。
  2. 効率的な通信: バイナリ形式での通信により高速で低容量のデータ転送が可能です。
  3. 双方向ストリーミング: クライアントとサーバー間で双方向のストリーミング通信が可能です。
  4. 多言語対応: gRPCは様々なプログラミング言語をサポートしています。

書き方の例

以下は、Golangを使用したシンプルなgRPCサーバーとクライアントの例です。

プロトコルバッファ定義 (user.proto)
syntax = "proto3";

package user;

service UserService {
    rpc GetUser (GetUserRequest) returns (User);
    rpc CreateUser (CreateUserRequest) returns (User);
}

message User {
    int32 id = 1;
    string name = 2;
}

message GetUserRequest {
    int32 id = 1;
}

message CreateUserRequest {
    string name = 1;
}
サーバー実装 (server.go)
package main

import (
    "context"
    "log"
    "net"

    "google.golang.org/grpc"
    pb "path/to/your/proto/package"
)

type server struct {
    pb.UnimplementedUserServiceServer
    users []pb.User
}

func (s *server) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.User, error) {
    for _, user := range s.users {
        if user.Id == req.Id {
            return &user, nil
        }
    }
    return nil, nil
}

func (s *server) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*pb.User, error) {
    newUser := pb.User{Id: int32(len(s.users) + 1), Name: req.Name}
    s.users = append(s.users, newUser)
    return &newUser, nil
}

func main() {
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    grpcServer := grpc.NewServer()
    pb.RegisterUserServiceServer(grpcServer, &server{})
    if err := grpcServer.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}
クライアント実装 (client.go)
package main

import (
    "context"
    "log"
    "time"

    "google.golang.org/grpc"
    pb "path/to/your/proto/package"
)

func main() {
    conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()

    client := pb.NewUserServiceClient(conn)

    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    defer cancel()

    // Create a new user
    newUser, err := client.CreateUser(ctx, &pb.CreateUserRequest{Name: "Charlie"})
    if err != nil {
        log.Fatalf("could not create user: %v", err)
    }
    log.Printf("Created user: %v", newUser)

    // Get user
    user, err := client.GetUser(ctx, &pb.GetUserRequest{Id: newUser.Id})
    if err != nil {
        log.Fatalf("could not get user: %v", err)
    }
 


1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1