graphql-rest-proxy で REST な API を GraphQL に変換する

![image.png](https://qiita-image-store.s3.amazonaws.com/0/103885/dccec959-b509-08a7-94d6-061b48b5db9b.png)

`graphql-rest-proxy` という、 REST な API を GraphQL サーバーに変換するツールを作りました。

https://github.com/acro5piano/graphql-rest-proxy

# 動機

GraphQL は素晴らしいので、 REST API から GraphQL に移行したいと思ったとします。
ただ、既存の REST API を GraphQL に置き換えるのは大変でしょう。

`graphql-rest-proxy` は、既存の REST API に変更を与えることなく、 GraphQL サーバーを稼働させることができます。

# インストール

```sh
npm -g install graphql-rest-proxy
```

または Yarn で

```sh
yarn global add graphql-rest-proxy
```


# 使い方

**STEP 1. GraphQL の Schema を定義する**

```graphql:schema.graphql
type User {
  id: Int
  name: String
  isActive: Boolean
}

type Query {
  getUser: User @proxy(get: "https://my-rest-api.com/user")
}
```

**STEP 2. Proxy server を起動する**

```sh
graphql-rest-proxy schema.graphql

# => graphql-rest-proxy is running on http://localhost:5252
```

**STEP 3. クライアントからGraphQLのリクエストをする**

```sh
curl -XPOST -H 'Content-Type: application/json' \
    -d '{ "query": "{ getUser { id name isActive } }" }' \
    http://localhost:5252/graphql
```

このようなレスポンスが帰ってきます。

```json

{
  "data": {
    "getUser": {
      "id": 1,
      "name": "Tom",
      "isActive": false
    }
  }
}
```

# Schema 定義例

**基本的なクエリ**

```graphql
type User {
  id: Int
  name: String
}

type Query {
  getUser: User @proxy(get: "https://my-rest-api.com/user")
  getUsers: [User] @proxy(get: "https://my-rest-api.com/users")
}
```

**パラメータ付きのクエリ**

`id` というクエリパラメータは `$id` という変数名で参照できます。

```graphql
type User {
  id: Int
  name: String
}

type Query {
  getUserById(id: Int!): User @proxy(get: "https://my-rest-api.com/users/$id")
}
```

**Input Parameters**

Mutation は `variables` を REST API に送信します。

```graphql
type UserInput {
  name: String!
}

type User {
  id: Int
  name: String
}

type Mutation {
  createUser(user: UserInput!): User @proxy(post: "https://my-rest-api.com/users")
  updateUser(id: Int!, user: UserInput!): User @proxy(patch: "https://my-rest-api.com/users/$id")
}
```

リクエスト例

```javascript
fetch('http://localhost:5252/graphql', {
  method: 'patch',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    query: gql`
      mutation UpdateUser($id: Int!, $user: UserInput!) {
        updateUser(id: $id, user: $user) {
          id
          name
        }
      }
    `,
    variables: {
      id: 1,
      user: {
        name: 'acro5piano',
      },
    },
  }),
})
```

**ネストしたオブジェクト**

親の ID は、これまた `$id` で参照できます。

```graphql
type Post {
  id: Int
  title: String
}

type User {
  id: Int
  name: String
  posts: [Post] @proxy(get: "https://my-rest-api.com/users/$id/posts")
}

type Query {
  getUser: User @proxy(get: "https://my-rest-api.com/user")
}
```

**base url を設定する**

冗長な記述をへらすため、 `baseUrl` という CLI オプションを設定できます。

```sh
graphql-rest-proxy --baseUrl https://my-rest-api.com schema.graphql
```

```graphql
type Query {
  getUser: User @proxy(get: "/user")
}
```

# 工夫点

最小のリクエストで済むように、子オブジェクトがレスポンスに含まれていた場合、それを返します。

例えば Schema が下記のようだったとして

```graphql
type Post {
  id: Int
  title: String
}

type User {
  id: Int
  name: String
  posts: [Post] @proxy(get: "https://my-rest-api.com/users/$id/posts")
}

type Query {
  getUser: User @proxy(get: "https://my-rest-api.com/user")
}
```

REST API が下記のようなレスポンスを返した時、

```sh
curl https://my-rest-api.com/user
```

```json
{
  "id": 1,
  "name": "acro5piano",
  "posts": {
    "id": 1,
    "title": "graphql-rest-proxy"
  }
}
```

`posts` は `/user` に含まれているので、 `graphql-rest-proxy` は `https://my-rest-api.com/users/1/posts` にリクエストしません。

詳しい情報は、 https://github.com/acro5piano/graphql-rest-proxy を参照してくださいm(_ _)m

# おまけ

パッケージは今まで Rollup.js を使っていましたが、今回を機に @pika/pack を使ってみました。

https://www.pikapkg.com/blog/introducing-pika-pack/

Rollup では面倒だった、実行可能コマンドの指定やWebpack的なコード埋め込みが、ほぼ自動で設定できるようになり、非常に快適でした。 @pika/pack もいつか記事にしたい。