📣

AmplifyでSQL? をどうやって解決してくれているのか

2024/05/28に公開

はじめに

こんにちは!
犬専用の音楽アプリ オトとりっぷでエンジニアしています、足立です!

https://www.oto-trip.com/

この記事では、Amplify が SQL をどうやって解決してくれているのかに焦点を当てたいと思います。
Amplify とは?Gen2 とは?の基礎部分には触れませんので、そちらがまだの方は以下の公式を一読されることをお勧めします。

https://aws.amazon.com/jp/blogs/news/amplify-gen2-ga/

Amplify で SQL?

先日 Amplify ローンチウィークという Amplify に関連する多くのリリースがありました。その中に リレーショナルデータベースを利用している様子があって、中身がどうなっているのか気になって調べたのが本稿です。

こちらの記事がネタ元です。

https://aws.amazon.com/jp/blogs/news/new-in-aws-amplify-integrate-with-sql-databases-oidc-saml-providers-and-the-aws-cdk/

なんと、Amplify Gen2 では、PostgreSQL などのリレーショナルデータベースに簡単にアクセスできるようになったんです。詳細は記事本文に譲りますが、Neonというサーバーレスなデータベースサービスに対して、ユーザーが設定したカスタムの SQL を利用してデータフェッチしています。

重要な箇所だけ抜き出すと、

  1. Neon コンソール上で、events テーブル(GEOMETRY 型を含む)を作成し、サンプルデータを挿入する
-- Create a table to events
CREATE TABLE events (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100),
  geom GEOMETRY(Point, 4326)
);
SQL 全文
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS postgis;

-- Create a table to events
CREATE TABLE events (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100),
  geom GEOMETRY(Point, 4326)
);

-- Create a spatial index on the geometry column
CREATE INDEX idx_events_geom ON events USING GIST (geom);

-- Insert some sample data
INSERT INTO events (name, geom)
VALUES
  ('Event A', ST_SetSRID(ST_MakePoint(-73.985428, 40.748817), 4326)),
  ('Event B', ST_SetSRID(ST_MakePoint(-73.990673, 40.742051), 4326)),
  ('Event C', ST_SetSRID(ST_MakePoint(-73.981226, 40.744681), 4326)),
  ('Event D', ST_SetSRID(ST_MakePoint(-73.987369, 40.739917), 4326)),
  ('Event E', ST_SetSRID(ST_MakePoint(-73.992743, 40.746035), 4326));

-- Create a notes table where we'll add owner-based authorization
CREATE TABLE notes (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    content TEXT,
    owner TEXT
);
  1. ローカルから Neon に接続して、スキーマファイルを生成する
$ npx ampx generate schema-from-database --connection-uri-secret SQL_CONNECTION_STRING --out amplify/data/schema.sql.ts
  1. Amplify Data のスキーマを変更し、SQL カスタムクエリを追加する
.addToSchema({
  // Adds a new query that uses PostgreSQL's PostGIS extension to
  // convert a geom type to lat and long coordinates
  listEventWithCoord: a
    .query()
    .returns(a.ref('EventWithCoord').array())
    .authorization((allow) => allow.authenticated())
    .handler(
      a.handler.inlineSql(`
        SELECT id, name, ST_X(geom) AS longitude, ST_Y(geom) AS latitude FROM events;
      `)
    ),
/amplify/data/resource.ts 全文
const schema = generatedSqlSchema
  .renameModels(() => [['notes', 'Note']])
  .setAuthorization((models) => [
    models.Note.authorization((allow) => allow.ownerDefinedIn('owner')),
  ])
  .addToSchema({
    // Adds a new query that uses PostgreSQL's PostGIS extension to
    // convert a geom type to lat and long coordinates
    listEventWithCoord: a
      .query()
      .returns(a.ref('EventWithCoord').array())
      .authorization((allow) => allow.authenticated())
      .handler(
        a.handler.inlineSql(`
          SELECT id, name, ST_X(geom) AS longitude, ST_Y(geom) AS latitude FROM events;
        `)
      ),

    // Defines a custom type that matches the response shape of the query
    EventWithCoord: a.customType({
      id: a.integer(),
      name: a.string(),
      latitude: a.float(),
      longitude: a.float(),
    }),
  });

Amplify Data カテゴリの inlineSql クエリで SQL を書いてますね。
実際にこれを実行すると、Neon 上に保存されたデータが表示されるのが確認できると思います。
(実際に動いている動画は上記の公式ブログに掲載されておりますので、そちらをご覧ください。)

SQL はどこにいく?

ここで疑問なのが、inlineSql で記載した SQL はどこで実行されているのか?です。

Amplify Data カテゴリなので、AppSync 経由でクエリが投げられているのでしょう。
とりあえずコンソールから AppSync のデータソースを確認してみます。

AWS Lambda のリソースが呼び出されていますね。
Lambda のコードの中身は、トランスパイルされた TypeScript でした。読みにくかったので、TypeScript の元コードを探すと、ここにありました。

https://github.com/aws-amplify/amplify-category-api/blob/main/packages/amplify-graphql-model-transformer/rds-lambda/handler.ts#L21-L35

adapter.executeRequest(event, debugMode);とありますので、AppSync からの Event 引数をそのまま Adopter が処理しているみたいで、ここが SQL を実行している正体みたいです。

では、inlineSql クエリで記載した SQL 自体はどこに置いてあるのでしょうか?また AppSync に戻って確認します。
QueryListEventWithCoordDataResolverFnという関数の VTL を確認すると、VTL 内にstatementという項目にユーザーが設定した SQL が記載されているのが確認できます。

どうやら、Amplify Data カテゴリの VTL 生成時にパラメータとして読み込まれるみたいです。VTL 生成時に利用されるライブラリ内にそのような該当場所を確認することができます。

https://github.com/aws-amplify/amplify-category-api/blob/main/packages/amplify-graphql-sql-transformer/src/graphql-sql-transformer.ts#L228-L244

Client サイドから直接 SQL を投げているわけではなさそうで、一安心ですね。

ネタバレ

ちゃんと公式ドキュメントに記載があります。

https://docs.amplify.aws/react-native/build-a-backend/data/connect-to-existing-data-sources/connect-postgres-mysql-database/

Neon だけでなく、VPC 内の MySQL データベースなどにも接続可能なようです。
ちなみに基礎となる技術自体は Gen1 の時点で登場してますから、Gen2 になってこれらを抽象的に呼び出しているだけということですね。

https://aws.amazon.com/jp/blogs/news/create-a-graphql-api-for-any-existing-mysql-and-postgresql-database/

最後に

ここまで読んでいただきありがとうございました。
Gen2 になってますます使いやすくなった Amplify、もっと活用していきたいですね。

もし犬専用の音楽アプリに興味を持っていただけたら、ぜひダウンロードしてみてください!

https://www.oto-trip.com/

Discussion