どのように、我々はGraphql、反応、ゴラン、Kory KratosとKubernetesを使っている学生プロジェクトプラットホームを構築しました、パート1



考え
私は大学で大学を卒業しました.他の学生に限られた接触で、しかし、サイドプロジェクトのためのアイデアの多くは、私はしばしば、そのようなプロジェクトのアイデアを共有し、既存の学生プロジェクトをチェックアウトするプラットフォームをよく夢見ていた(実際には本当にクールなものを誰もが知っている多くのそれらの多くがあることが判明).
以下のようになります.

あなたはour current prototypeをチェックアウトしたい場合.

建築
スケーラビリティと簡単な配置のために、そして、我々がそうすることができるので、我々はKubernetesクラスタですべてのコードを展開することに決めました.開発のためにはほとんど資源を必要としません.したがって、私たちはちょうど3 - A -月VMを公共のIPで借りて、それにk3sをインストールしました.
ほとんどのデータをGolang-Applicationによって提供されるGraphSQL APIを使用して交換します.私たちは、スキーマの最初のアプローチを使用します.すなわち、APIができることに対する真理の源は、graphqlスキーマです.このスキーマから、typesafeクライアントとサーバーコードの両方を生成します.
認証はory kratos .
UIは、反応とアポロクライアントで構築されています.
データベースとして、クラスタ内のPostgreSQLインスタンスを使用します.

API
まず第一に、私たちのAPI hereで遊んで、コードhereを見つけることができます
APIはgqlgenで構築されています.フォルダ構造は次のようになります.
...
├── go.mod
├── go.sum
├── gqlgen.yml # config for auto-generating go-code from gql-schema
├── graph
│   ├── generated
│   │   └── generated.go
│   ├── model # partly auto-generated, partly manually edited representations of the graphql datastructures
│   │   ├── models_gen.go
│   │   └── user.go
...
│   ├── resolvers # The code that actually handles graphql requests,  method heads are auto-generated from schema
│   │   └── user.resolvers.go
...
│   └── schema
│       └── user.graphqls
...
├── server.go # entrypoint
├── sqlc # generated database query code
│   └── users.sql.go
...
├── sqlc.yaml # config for autogenerating go-code for sql queries
├── sql-queries # queries we want go-code for
│   └── users.sql
...
└── tools.go
あなたはすぐにthis包括的なガイドに従って、このプロジェクトの構造のほとんどを初期化することができます.
今私たちのAPIの新機能を実装する喜びです!ワークフローは次のとおりです.
  • 私たちのGraphSQLスキーマに新しい機能を追加します.例えば、私たちはAPIを有効にしたいと言います.以下の内容で、adder.graphqlsというファイルを作成します
  • extend type Query{
        addNumber(a:Int!,b:Int!):Int!
    }
    
  • Codegen Comandを実行します.
  • go run github.com/99designs/gqlgen generate
    
    新しいファイルgraph/resolvers/adder.resolver.goは以下の内容で作成されます.
    package resolvers
    
    // This file will be automatically regenerated based on the schema, any resolver implementations
    // will be copied through when generating and any unknown code will be moved to the end.
    
    import (
        "context"
        "fmt"
    
        "gitlab.lrz.de/projecthub/gql-api/graph/generated"
    )
    
    func (r *queryResolver) AddNumber(ctx context.Context, a int, b int) (*int, error) {
        panic(fmt.Errorf("not implemented"))
    }
    
    // Query returns generated.QueryResolver implementation.
    func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
    
    type queryResolver struct{ *Resolver }
    
  • 私たちが今しなければならないのは、メソッドを実装することです.
  • package resolvers
    
    // This file will be automatically regenerated based on the schema, any resolver implementations
    // will be copied through when generating and any unknown code will be moved to the end.
    
    import (
        "context"
        "fmt"
    
        "gitlab.lrz.de/projecthub/gql-api/graph/generated"
    )
    
    func (r *queryResolver) AddNumber(ctx context.Context, a int, b int) (int, error) {
        return a+b,nil
    }
    
    // Query returns generated.QueryResolver implementation.
    func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
    
    type queryResolver struct{ *Resolver }
    
    ここで完全にtypesafeコードを取得する方法を参照してください!
    この小さなセットアップでは、我々のサーバーを実行し、無料でdocumentationを得ることができます!
    では、実際にデータベースクエリで有用なデータを提供する方法を見てみましょう.例えば、idを使ってプロジェクトを取得するためのAPIをください.
    # project.graphqls
    
    type Project {
      id: ID!
      name: String!
      description: String!
      languages: [String!]!
      location: Location
      participants: [User!]!
      creator: User!
      images: [Image!]!
      createdAt: Time
      # if the current user saved this project
      saved: Boolean!
      tags: [String!]!
    }
    
    extend type Query {
      getProject(id: ID!): Project
    }
    
    生成されたgo関数頭は以下のようになります:
    func (r *queryResolver) GetProject(ctx context.Context, id string) (*model.Project, error)
    
    次に、ファイルsql-queries/projects.sqlにSQLクエリを作成しました.
    -- name: GetProjectByID :one
    SELECT *
    FROM projects
    WHERE id = $1;
    
    私たちは現在、この質問のためにtypesafe囲碁コードを生成するためにsqlcを使用します.そのためには、現在のデータベーススキーマを必要とします.そのため、クラスタからデータベースを転送し、スキーマをダンプしてSQLCを起動します.
    export POSTGRES_PASSWORD=$(kubectl get secret --namespace default psql-postgresql -o jsonpath="{.data.postgresql-password}" | base64 --decode)
    kubectl port-forward --namespace default svc/psql-postgres 5432:5432 &
    sleep 2
    PGPASSWORD="$POSTGRES_PASSWORD" pg_dump --host 127.0.0.1 -U postgres -d postgres -p 5432 -s > schema.sql
    rm -Rf sqlc
    sqlc generate
    kill $(jobs -p)
    
    SQLCはsqlc -サブフォルダにクエリを出力するように設定されています.
    # sqlc.yaml
    version: "1"
    packages:
      - path: "sqlc"
        name: "sqlc"
        engine: "postgresql"
        schema: "schema.sql"
        queries: "sql-queries"
    
    では、データベースコードをリゾルバに注入することができます.
    // resolvers/resolver.go
    package resolvers
    
    import (
        "database/sql"
    
        "gitlab.lrz.de/projecthub/gql-api/sqlc"
    
    )
    
    // It serves as dependency injection for your app, add any dependencies you require here.
    
    type Resolver struct {
        queries *sqlc.Queries
    }
    
    func NewResolver(connstring string) (*Resolver, error) {
        db, err := sql.Open("postgres", connstring)
        if err != nil {
            return nil, err
        }
        queries := sqlc.New(db)
        return &Resolver{
            queries: queries,
        }, nil
    }
    
    これにより、すべてのリゾルバ関数でデータベースクエリを作成することができます.これにより、idリゾルバによってプロジェクトに適用されます.
    func (r *queryResolver) GetProject(ctx context.Context, id string) (*model.Project, error) {
        dbProject, err := r.queries.GetProjectByID(context.Background(), uuid.MustParse(id))
        if err != nil {
            return nil, err
        }
    // now just transform the db result to our gql project datatype
        return  return &Project{
            ID:          dbProject.ID.String(),
            Name:        dbProject.Name,
            Description: dbProject.Description,
            CreatorID:   dbProject.Creator.String(),
            Languages:   []string{},
        }, nil
    }
    
    ここで、DBクエリによって返されるプロジェクトの自動生成されたデータ型は非常に友好的に見えます.
    package sqlc
    type Project struct {
        ID          uuid.UUID
        Name        string
        Description string
        CreatedAt   sql.NullTime
        Creator     uuid.UUID
        Location    sql.NullString
    }
    
    ああ!
    ここでは、私たちがどのように我々のAPIをtypesafe方法で我々の反応UIに使うかについて議論します.
    コメントを自由に、詳細を求めるとチューニングをお楽しみください!