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

### RestAPI

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

#### 書き方の例

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

```go
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の例です。

```go
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)

```protobuf
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)

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

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