Closed11

Serverless FrameworkでAWS AppSync上にGraphQL APIを構築したい(HTTPデータソース)

qmotasqmotas

AWS AppSync JavaScript ResolverでHTTPデータソース(REST API)からデータを収集すると同じ仕様のAPIをServerless Frameworkで作りたい

Serverless FrameworkはYAMLで書いた定義と関連ファイル(今回はGraphQLスキーマとリゾルバ・関数のJSファイル)からCloudFormationのテンプレートファイルを生成してデプロイしてくれるフレームワークで、設定項目をいい感じに抽象化してくれる

もともとはAWS LambdaやGoogle Cloud Functions等のデプロイに使うのがメイン用途だけど、プラグイン(serverless-appsync-plugin)を使うことでAppSyncにも対応する

参考資料

qmotasqmotas

雑感

  • ほどよく抽象化されていて、リファレンスを見ながら書くことが困難でなかった
  • スキーマやリゾルバ、関数のコードを別ファイルで管理できるのでバージョン管理との相性がよい
    • 参照の仕方も直感的で違和感がない
  • デプロイの体験がよかった
    • CloudFormationのスタックを作成する前に定義エラーがわかる
    • 認証情報の設定からデプロイまで簡単なコマンドで完結する
qmotasqmotas

作業ログ

https://github.com/sid88in/serverless-appsync-plugin/blob/alpha/doc/resolvers.md を参考にテンプレートなしで構築(余計な要素を入れたくないので)

mkdir appsync-serverless-poc
cd appsync-serverless-poc
pnpm init
pnpm i -D serverless serverless-appsync-plugin@alpha @aws-appsync/utils @aws-appsync/eslint-plugin eslint prettier
qmotasqmotas

serverless.yml

スキーマと各リゾルバ、関数は外部ファイルで定義する

  • schema.graphql
  • functions/
    • getTracksForHome.js
    • getAuthor.js
  • resolvers/
    • Query.tracksForHome.js
    • Track.author.js
serverless.yml
service: appsync-serverless-poc
frameworkVersion: "3"
configValidationMode: error

plugins:
  - serverless-appsync-plugin

provider:
  name: aws
  region: ap-northeast-1

appSync:
  name: AppSync Playground(serverless)
  schema: schema.graphql

  authentication:
    type: API_KEY

  dataSources:
    catstronautsRestApi:
      type: HTTP
      config:
        endpoint: https://odyssey-lift-off-rest-api.herokuapp.com

  pipelineFunctions:
    getTracksForHome:
      dataSource: catstronautsRestApi
      code: functions/getTracksForHome.js

    getAuthor:
      dataSource: catstronautsRestApi
      code: functions/getAuthor.js

  resolvers:
    Query.tracksForHome:
      code: resolvers/Query.tracksForHome.js
      functions:
        - getTracksForHome

    Track.author:
      code: resolvers/Track.author.js
      functions:
        - getAuthor
qmotasqmotas

スキーマ

schema.graphql
type Author {
  id: ID!
  name: String!
  photo: String
}

type Query {
  tracksForHome: [Track!]!
}

type Track {
  id: ID!
  title: String!
  author: Author!
  thumbnail: String
  length: Int
  modulesCount: Int
}
qmotasqmotas

関数

functions/getTracksForHome.js
import { util } from "@aws-appsync/utils";

export function request(ctx) {
  return {
    method: "GET",
    params: {
      headers: { "Content-Type": "application/json" },
    },
    resourcePath: "/tracks",
  };
}

export function response(ctx) {
  const { error, result } = ctx;
  if (error) {
    ctx.stash.errors = ctx.stash.errors ?? [];
    ctx.stash.errors.push(ctx.error);
    return util.appendError(error.message, error.type, result);
  }

  if (result.statusCode === 200) {
    return JSON.parse(result.body);
  } else {
    return util.appendError(result.body, result.statusCode);
  }
}
functions/getAuthor.js
import { util } from "@aws-appsync/utils";

export function request(ctx) {
  return {
    method: "GET",
    params: {
      headers: { "Content-Type": "application/json" },
    },
    resourcePath: `/author/${ctx.source.authorId}`,
  };
}

export function response(ctx) {
  const { error, result } = ctx;
  if (error) {
    ctx.stash.errors = ctx.stash.errors ?? [];
    ctx.stash.errors.push(ctx.error);
    return util.appendError(error.message, error.type, result);
  }

  if (result.statusCode === 200) {
    return JSON.parse(result.body);
  } else {
    return util.appendError(result.body, result.statusCode);
  }
}
qmotasqmotas

リゾルバ

関数の結果をそのまま使うだけなので内容は共通

resolvers/Query.tracksForHome.js, resolvers/Track.author.js
export function request(ctx) {
  return {};
}

export function response(ctx) {
  return ctx.prev.result;
}
qmotasqmotas

動作確認

AppSyncのコンソールからAPI Keyを追加してクエリを実行

動いた

このスクラップは2023/02/15にクローズされました