AppSync上のGraphQL APIをapollo-linkで呼ぶ(Lambdaからも)

# なぜこれを書くのか

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96277/01a187a5-2b42-7254-73f6-4e1cbdd014eb.png)

GraphQLといえば、手軽さでいうとこのマネージドサービス、AWS AppSyncが出てきます。
バンバン使っていきたいところです。

公式のやりかたはこちらの[ドキュメント](https://docs.aws.amazon.com/appsync/latest/devguide/building-a-client-app-node.html)です。
以下のソースコードのようにAWSAppSyncClientというライブラリを使っていて分かりづらい。
`apollo-link`を直接使ったほうがブラウザ側だけでなくサーバ側アプリにも使えて可搬性が高い。
**なので単なるapollo-linkだけを使ったNode.jsアプリからAppSyncにアクセスしたいです**。
なので、そういう事例を作ってみます。


```読みづらい公式サンプル.js
const AWSAppSyncClient = require('aws-appsync').default;

// Set up Apollo client
const client = new AWSAppSyncClient({
    url: url,
    region: region,
    auth: {
        type: type,
        credentials: credentials,
    }
    //disableOffline: true      //Uncomment for AWS Lambda
});

client.hydrated().then(function (client) {
    //Now run a query
    client.query({ query: query })
    //client.query({ query: query, fetchPolicy: 'network-only' })   //Uncomment for AWS Lambda
        .then(function logData(data) {
            console.log('results of query: ', data);
        })
        .catch(console.error);
});
```

# AppSyncで少し遊んで原理を確認

結局、いろいろみても、

- [jQueryを使った事例](https://www.pressmantech.com/tech/serverless/4786)にもあるように、AppSyncに単なるjQueryのAjaxでもアクセスできています。
- AWSAppSyncClientは単なるapollo-clientのラッパですと公式に書いてあります。

本当は難しくないはずなのです。

## 怖がらなくても、AppSyncは単なるAPI_KEY認証のGraphQLサーバ

AppSyncでのAPIの作り方は、[Qiita記事](https://qiita.com/nakazax/items/209e4fac5fa42db6e8c8)を参考にするとすぐにできます。

作ったAPIの管理画面から「Download configs」をクリックするとこんなファイルがダウンロードできます。

![FireShot Capture 32 - AWS AppSync Console_ - https___console.aws.amazon.com_appsync_home.png](https://qiita-image-store.s3.amazonaws.com/0/96277/e27b1826-3c84-53b6-f8b2-78fe1e8cba1b.png)

ダウンロードした`aws-exports.js`ファイル

```aws-exports.js
const awsmobile =  {
    "aws_appsync_graphqlEndpoint": "https://***************.appsync-api.**********.amazonaws.com/graphql",
    "aws_appsync_region": "*************",
    "aws_appsync_authenticationType": "API_KEY",
    "aws_appsync_apiKey": "*******************",
};

export default awsmobile;
```

中身にはAPI_KEYなどが入っています。
このAPI_keyが使えればいいわけです。

ひとまず、InsomniaというAPIテストツールから実行してみます。
URLには上記でダウンロードしたconfig内のURL、X-Api-Keyにはconfig内のapiKeyを設定して、クエリを実行すると、下のようにAPIにアクセスできました。

![2018-12-29.png](https://qiita-image-store.s3.amazonaws.com/0/96277/1c6fdbb7-ca96-2f43-929c-d4208d43976d.png)

## API_KEY認証はapollo-linkのheadersでできます

`X-Api-Key`というヘッダを付ければ動く、ということがわかれば、あとはapollo-linkがそれに対応するかどうかですが、
むろんapollo-linkは対応しています。
こんな風に`HttpLink`のコンストラクタに`headers`を入れて好きにヘッダを書き込めばいいです。

https://github.com/apollographql/apollo-link/issues/44

```apollo-link出の書き方.ts
new HttpLink({
  uri: URL, 
  headers: {
    'X-ヘッダの名前': ヘッダの値
  }
})
```

# 本編：apollo-linkを使ったアクセスの実装

今回は、Node.jsのアプリでAppSyncにアクセスします。
アプリのソースコードのフォルダで作業します。

まず、先ほどAPI管理画面の「Download configs」からダウンロードした下記の`aws-exports.js`をアプリのフォルダに入れます。

```aws-exports.js
const awsmobile =  {
    "aws_appsync_graphqlEndpoint": "https://***************.appsync-api.**********.amazonaws.com/graphql",
    "aws_appsync_region": "*************",
    "aws_appsync_authenticationType": "API_KEY",
    "aws_appsync_apiKey": "*******************",
};

export default awsmobile;
```

アプリのファイル`index.js`にインポートします。

```index.js
const aws_exports = require('./aws-exports').default;
```

さいごに、apollo-linkで上記設定を使ってLinkを作ります。

```index.js
const { HttpLink } = require('apollo-link-http');
//もしくはimport { HttpLink } from 'apollo-link-http';
const fetch = require('node-fetch');
const uri = aws_exports.aws_appsync_graphqlEndpoint,
const apiKey = aws_exports.aws_appsync_apiKey;
const link = new HttpLink({uri, fetch, headers: {
  'X-Api-Key': apiKey
}});
```

このLinkを使ってAppSyncにクエリを投げます。

# 実装例

私はNode.jsを使って、`graphql-tools`のリモートスキーマを作成しAppSyncの結果をそのままフォワードするアプリを作りました。

```index.js
import {makeRemoteExecutableSchema, mergeSchemas, introspectSchema} from 'graphql-tools';
const fetch = require('node-fetch');
const { HttpLink } = require('apollo-link-http');
const { ApolloServer, gql } = require("apollo-server-express");
import serverless from "serverless-http";
import express from "express";

const aws_exports = require('./aws-exports').default;

const createSchema = async () => {
	const createRemoteSchema = async (uri, apiKey) => {
		const link = new HttpLink({uri, fetch, headers: {
			'X-Api-Key': apiKey
		}});
		return makeRemoteExecutableSchema({
			schema: await introspectSchema(link),
			link
		});
	};
  	

	const citySchema = await createRemoteSchema(
		aws_exports.aws_appsync_graphqlEndpoint,
		aws_exports.aws_appsync_apiKey);
	return citySchema
};

const run = async () => {
	const schema = await createSchema();

	const app = express();
	const server = new ApolloServer({ schema });
	server.applyMiddleware({ app });
	app.listen({ port: 4000 }, () =>
	console.log(`Server ready at http://localhost:4000${server.graphqlPath}`)
	);
}

run()
```

## AWS Lambdaでも動く

最後のrun()以降を下記に変えると、**AWS Lambda上でもちゃんと動きました**。

```index.js
const createServer = (schema) => {
	const app = express();
	const server = new ApolloServer({ schema });
	server.applyMiddleware({ app });
	return serverless(app);
};

let sls
exports.graphqlHandler = async (event, context) => {
	if(sls == null) {
		const schema = await createSchema();
		sls = createServer(schema);
	} else {
		console.log("Already initialized")
	}
	return await sls(event, context);
}
```

# まとめ

AppSyncのGraphQLにアクセスするアプリを作るため、apollo-linkを使った実装例を示しました。
Node.jsサーバアプリとしても動きますし、ちょっと変えればAWS Lambdaでも動きます。

AppSyncだけではちょっと難しい複雑な処理をLambda上でやらせるときに使えそうです。
使ってみてください。