SOULsでAPIを立ててみる

## SOULsとは
Active RecordとScaffoldやCI/CDが標準装備された、Rubyのサーバーレスフレームワークです。
GCP (Google Cloud Platform) 上で動作します。
オランダ政府より先端研究開発として認定されており、日本人の方が開発されているため[ドキュメント](https://souls.elsoul.nl/ja/)も丁寧です。

詳しくは[別で記事](https://qiita.com/sohey/items/fd5127b931bfd1f2f4ae)を書いたのでそちらをご覧ください。

## いざ、触ってみる
SOULsはGraphQL APIとWorkerが作れるので、GraphQL APIを作ってみます。


### 必要なパッケージ
ここのインストールの説明は省略します。

- Ruby v3
- Docker
- Google Cloud SDK
- PostgreSQL
- Redis
- Github CLI


### 準備
Github CLI AuthのログインとSOULs のインストールをします。

```bash
$ gh auth login

$ gem install souls

$ souls -v #インストール成功の確認
```

### アプリケーションの作成
Railsでいう`rails new`のコマンドでプロジェクトを作成できます。

```bash
$ souls new souls-app
```

次にSOULs コマンドを利用して、PostgreSQL13 の Docker コンテナを起動します。

```bash
$ cd apps/api

$ souls docker psql
```

DBの準備ができたのでデータベースの作成及び Migration を行います。

```bash
$ souls db create
$ souls db migrate
```

ここまでできてるか動作確認です。
souls s コマンドで立ち上げます。

```bash
$ souls s
```
[localhost:4000](http://localhost:4000/)にアクセスして、以下の画面が表示されたらアプリを作る準備は完了です。

![動作確認](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629156/80fedf0f-c1b3-95e2-d09e-14726c3a795c.png)


### モデルの作成
`souls db create_migration`でマイグレーションファイルを作成します。

```bash
$ souls db create_migration user
db/migrate/20210930154336_create_users.rb
Created file! : sig/api/db/migrate/create_users.rbs
Created file! : ./app/models/user.rb
Created file! : ./spec/models/user_spec.rb
Created file! : sig/api/app/models/user_model.rbs
```

作成されたファイルを元にmigrationします。

```bash
$ souls db migrate
```

### Scaffoldの実行
`souls g scaffold_all`コマンドは db/schema.rb のタイプをもとに CRUD に必要なファイルと、それに対応するテストを自動生成してくれます。

```bash
$ souls g scaffold_all
.
.
Created file! : ./app/graphql/types/edges/user_edge.rb
Created file! : ./sig/api/app/graphql/types/edges/user_edge.rbs
Created file! : ./app/graphql/types/connections/user_connection.rb
Created file! : ./sig/api/app/graphql/types/connections/user_connection.rbs
Created file! : ./app/graphql/resolvers/user_search.rb
Created file! : ./sig/api/app/graphql/resolvers/user_search.rbs
Created file! : ./spec/factories/users.rb
Created file! : ./spec/mutations/base/user_spec.rb
Created file! : ./spec/queries/user_spec.rb
Created file! : ./spec/resolvers/user_search_spec.rb
Created file! : ./spec/policies/user_policy_spec.rb
🎉  Generated SOULs CRUD Files
```
これで完成です。

### 動かしてみる

#### 登録

クエリ

```graphql
mutation {
  createUser(
    input: {
      uid: "uniq-id"
      username: "SOULs"
      email: "info@test.com"
      }
  ) {
    userEdge {
      node {
        uid
        id
        username
        email
      }
    }
  }
}
```

レスポンス

```json
{
  "data": {
    "createUser": {
      "userEdge": {
        "node": {
          "uid": "uniq-id",
          "id": "VXNlcjo0",
          "username": "SOULs",
          "email": "info@test.com"
        }
      }
    }
  }
}
```

#### 表示

クエリ

```graphql
query {
  userSearch(filter: { isDeleted: false }) {
    edges {
      node {
        id
        uid
        username
        email
      }
    }
    nodes {
      id
    }
    pageInfo {
      hasNextPage
    }
  }
}
```

レスポンス

```json
{
  "data": {
    "userSearch": {
      "edges": [
        {
          "node": {
            "id": "VXNlcjo0",
            "uid": "uniq-id",
            "username": "SOULs",
            "email": "info@test.com"
          }
        }
      ],
      "nodes": [
        {
          "id": "VXNlcjo0"
        }
      ],
      "pageInfo": {
        "hasNextPage": false
      }
    }
  }
}
```


#### 更新

クエリ

```graphql
mutation {
  updateUser(input: {
    id: "VXNlcjo0"
    username: "SOULs API"
    }) {
    userEdge {
      node {
        id
        username
        email
      }
    }
  }
}
```

レスポンス

```json
{
  "data": {
    "updateUser": {
      "userEdge": {
        "node": {
          "id": "VXNlcjo0",
          "username": "SOULs API",
          "email": "info@test.com"
        }
      }
    }
  }
}
```

#### 物理削除(論理削除もあります)

クエリ

```graphql
mutation {
  destroyDeleteUser(input: { id: "VXNlcjo0" }) {
    user {
      id
      username
      email
    }
  }
}
```

レスポンス

```json
{
  "data": {
    "destroyDeleteUser": {
      "user": {
        "id": "VXNlcjo0",
        "username": "SOULs API",
        "email": "info@test.com"
      }
    }
  }
}
```

## 最後に
簡単にGraphQL APIが作れました。
今回はテストは触っていませんが、ほぼ自動生成されているのでFactoryBot用意するだけでテストが用意できます。