

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

# Menggunakan Aurora PostgreSQL dengan Data API di AWS AppSync
<a name="aurora-serverless-tutorial-js"></a>

 

Pelajari cara menghubungkan GraphQL API Anda ke database Aurora PostgreSQL menggunakan. AWS AppSync Integrasi ini memungkinkan Anda untuk membangun aplikasi berbasis data yang dapat diskalakan dengan menjalankan kueri dan mutasi SQL melalui operasi GraphQL. AWS AppSync menyediakan sumber data untuk mengeksekusi pernyataan SQL terhadap klaster Amazon Aurora yang diaktifkan dengan API Data. Anda dapat menggunakan AWS AppSync resolver untuk menjalankan pernyataan SQL terhadap API data dengan kueri, mutasi, dan langganan GraphQL.

Sebelum memulai tutorial ini, Anda harus memiliki keakraban dasar dengan AWS layanan dan konsep GraphQL.

**catatan**  
Tutorial ini menggunakan Wilayah `US-EAST-1`. 

**Topics**
+ [Siapkan database Aurora PostgreSQL Anda](#creating-clusters)
+ [Membuat database dan tabel](#creating-db-table)
+ [Membuat skema GraphQL](#rds-graphql-schema)
+ [Resolver untuk RDS](#rds-resolvers)
+ [Menghapus klaster Anda](#rds-delete-cluster)

## Siapkan database Aurora PostgreSQL Anda
<a name="creating-clusters"></a>

Sebelum menambahkan sumber data Amazon RDS AWS AppSync, lakukan hal berikut.

1. Aktifkan API Data pada klaster Aurora Serverless v2.

1. Konfigurasikan rahasia menggunakan AWS Secrets Manager

1. Buat cluster menggunakan AWS CLI perintah berikut.

   ```
   aws rds create-db-cluster \
               --db-cluster-identifier appsync-tutorial \
               --engine aurora-postgresql \
               --engine-version 16.6 \
               --serverless-v2-scaling-configuration MinCapacity=0,MaxCapacity=1 \
               --master-username USERNAME \
               --master-user-password COMPLEX_PASSWORD \
               --enable-http-endpoint
   ```

Ini akan mengembalikan ARN untuk cluster. Setelah membuat cluster, Anda harus menambahkan instance v2 Tanpa Server dengan perintah berikut AWS CLI .

```
aws rds create-db-instance \
    --db-cluster-identifier appsync-tutorial \
    --db-instance-identifier appsync-tutorial-instance-1 \
    --db-instance-class db.serverless \
    --engine aurora-postgresql
```

**catatan**  
Titik akhir ini membutuhkan waktu untuk menjadi aktif. Anda dapat memeriksa statusnya di konsol RDS di tab **Konektivitas & keamanan** untuk cluster.

Periksa status cluster dengan AWS CLI perintah berikut.

```
aws rds describe-db-clusters \
    --db-cluster-identifier appsync-tutorial \
    --query "DBClusters[0].Status"
```

Buat Rahasia melalui AWS Secrets Manager Konsol atau AWS CLI dengan file input seperti berikut menggunakan `USERNAME` dan `COMPLEX_PASSWORD` dari langkah sebelumnya:

```
{
    "username": "USERNAME",
    "password": "COMPLEX_PASSWORD"
}
```

Berikan ini sebagai parameter ke AWS CLI:

```
aws secretsmanager create-secret \
    --name appsync-tutorial-rds-secret \
    --secret-string file://creds.json
```

Ini akan mengembalikan ARN untuk rahasianya. **Catat** ARN cluster Aurora Serverless v2 Anda dan Rahasia untuk nanti saat membuat sumber data di konsol. AWS AppSync 

## Membuat database dan tabel
<a name="creating-db-table"></a>

Pertama, buat database bernama`TESTDB`. Dalam PostgreSQL, database adalah wadah yang menyimpan tabel dan objek SQL lainnya. Validasi bahwa klaster Aurora Serverless v2 Anda dikonfigurasi dengan benar sebelum menambahkannya ke API Anda. AWS AppSync Pertama, buat database *TESTDB* dengan `--sql` parameter sebagai berikut.

```
aws rds-data execute-statement \
    --resource-arn "arn:aws:rds:us-east-1:111122223333 ISN:cluster:appsync-tutorial" \
    --secret-arn "arn:aws:secretsmanager:us-east-1:111122223333 ISN:secret:appsync-tutorial-rds-secret" \
    --sql "create DATABASE \"testdb\"" \
    --database "postgres"
```

 Jika ini berjalan tanpa kesalahan, tambahkan dua tabel dengan `create table` perintah:

```
 aws rds-data execute-statement \
    --resource-arn "arn:aws:rds:us-east-1:111122223333 ISN:cluster:appsync-tutorial" \
    --secret-arn "arn:aws:secretsmanager:us-east-1:111122223333 ISN:secret:appsync-tutorial-rds-secret" \
    --database "testdb" \
    --sql 'create table public.todos (id serial constraint todos_pk primary key, description text not null, due date not null, "createdAt" timestamp default now());'

aws rds-data execute-statement \
    --resource-arn "arn:aws:rds:us-east-1:111122223333 ISN:cluster:appsync-tutorial" \
    --secret-arn "arn:aws:secretsmanager:us-east-1:111122223333 ISN:secret:appsync-tutorial-rds-secret" \
    --database "testdb" \
    --sql 'create table public.tasks (id serial constraint tasks_pk primary key, description varchar, "todoId" integer not null constraint tasks_todos_id_fk references public.todos);'
```

Jika berhasil, tambahkan cluster sebagai sumber data di API Anda.

## Membuat skema GraphQL
<a name="rds-graphql-schema"></a>

Sekarang setelah Aurora Serverless v2 Data API Anda berjalan dengan tabel yang dikonfigurasi, kami akan membuat skema GraphQL. Anda dapat membuat API dengan cepat dengan mengimpor konfigurasi tabel dari database yang ada menggunakan wizard pembuatan API.

Untuk memulai: 

1. Di AWS AppSync konsol, pilih **Buat API**, lalu **Mulai dengan cluster Amazon Aurora**. 

1. Tentukan detail **API seperti nama API**, lalu pilih database Anda untuk menghasilkan API.

1. Pilih database Anda. Jika perlu, perbarui Wilayah, lalu pilih cluster Aurora dan database *TESTDB* Anda. 

1. Pilih Rahasia Anda, lalu pilih **Impor**. 

1. Setelah tabel ditemukan, perbarui nama jenisnya. Ubah `Todos` ke `Todo` dan `Tasks` ke`Task`. 

1. Pratinjau skema yang dihasilkan dengan memilih **Skema Pratinjau**. Skema Anda akan terlihat seperti ini: 

   ```
   type Todo {
     id: Int!
     description: String!
     due: AWSDate!
     createdAt: String
   }
   
   type Task {
     id: Int!
     todoId: Int!
     description: String
   }
   ```

1. Untuk peran tersebut, Anda dapat AWS AppSync membuat peran baru atau membuat peran dengan kebijakan yang mirip dengan yang di bawah ini:

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Action": [
                   "rds-data:ExecuteStatement"
               ],
               "Resource": [
                   "arn:aws:rds:us-east-1:111122223333:cluster:appsync-tutorial",
                   "arn:aws:rds:us-east-1:111122223333:cluster:appsync-tutorial:*"
               ]
           },
           {
               "Effect": "Allow",
               "Action": [
                   "secretsmanager:GetSecretValue"
               ],
               "Resource": [
               "arn:aws:secretsmanager:us-east-1:111122223333:secret:appsync-tutorial-rds-secret",
               "arn:aws:secretsmanager:us-east-1:111122223333:secret:appsync-tutorial-rds-secret:*"
               ]
           }
       ]
   }
   ```

------

   Perhatikan bahwa ada dua pernyataan dalam kebijakan ini yang Anda berikan akses peran. Sumber daya pertama adalah cluster Aurora Anda dan yang kedua adalah AWS Secrets Manager ARN Anda. 

   Pilih **Berikutnya**, tinjau detail konfigurasi, lalu pilih **Buat API**. Anda sekarang memiliki API yang beroperasi penuh. Anda dapat meninjau detail lengkap API Anda di halaman **Skema**. 

## Resolver untuk RDS
<a name="rds-resolvers"></a>

Alur pembuatan API secara otomatis membuat resolver untuk berinteraksi dengan tipe kami. Jika Anda melihat halaman **Skema**, Anda akan menemukan resolver beberapa resolver berikut.
+ Buat `todo` melalui `Mutation.createTodo` bidang.
+ Perbarui `todo` melalui `Mutation.updateTodo` bidang.
+ Hapus `todo` melalui `Mutation.deleteTodo` bidang.
+ Dapatkan satu `todo` melalui `Query.getTodo` lapangan.
+ Daftar semua `todos` melalui `Query.listTodos` bidang.

Anda akan menemukan bidang dan resolver serupa yang dilampirkan untuk jenisnya. `Task` Mari kita lihat lebih dekat beberapa resolver. 

### mutasi.createTodo
<a name="createtodo"></a>

Dari editor skema di AWS AppSync konsol, di sisi kanan, pilih di `testdb` sebelah`createTodo(...): Todo`. Kode resolver menggunakan `insert` fungsi dari `rds` modul untuk secara dinamis membuat pernyataan insert yang menambahkan data ke tabel. `todos` Karena kami bekerja dengan Postgres, kami dapat memanfaatkan `returning` pernyataan untuk mendapatkan data yang dimasukkan kembali.

Perbarui resolver berikut untuk menentukan `DATE` jenis bidang dengan benar. `due`

```
import { util } from '@aws-appsync/utils';
import { insert, createPgStatement, toJsonObject, typeHint } from '@aws-appsync/utils/rds';

export function request(ctx) {
    const { input } = ctx.args;
    // if a due date is provided, cast is as `DATE`
    if (input.due) {
        input.due = typeHint.DATE(input.due)
    }
    const insertStatement = insert({
        table: 'todos',
        values: input,
        returning: '*',
    });
    return createPgStatement(insertStatement)
}

export function response(ctx) {
    const { error, result } = ctx;
    if (error) {
        return util.appendError(
            error.message,
            error.type,
            result
        )
    }
    return toJsonObject(result)[0][0]
}
```

Simpan resolver. Petunjuk tipe menandai `due` dengan benar di objek input kami sebagai `DATE` tipe. Hal ini memungkinkan mesin Postgres untuk menafsirkan nilai dengan benar. Selanjutnya, perbarui skema Anda untuk menghapus `id` dari `CreateTodo` input. Karena database Postgres kami dapat mengembalikan ID yang dihasilkan, Anda dapat mengandalkannya untuk pembuatan dan mengembalikan hasilnya sebagai satu permintaan sebagai berikut.

```
input CreateTodoInput {
    due: AWSDate!
    createdAt: String
    description: String!
}
```

Buat perubahan dan perbarui skema Anda. Buka editor **Query** untuk menambahkan item ke database sebagai berikut.

```
mutation CreateTodo {
  createTodo(input: {description: "Hello World!", due: "2023-12-31"}) {
    id
    due
    description
    createdAt
  }
}
```

Anda mendapatkan hasil sebagai berikut.

```
{
  "data": {
    "createTodo": {
      "id": 1,
      "due": "2023-12-31",
      "description": "Hello World!",
      "createdAt": "2023-11-14 20:47:11.875428"
    }
  }
}
```

### Query.listTodos
<a name="listtodo"></a>

Dari editor skema di konsol, di sisi kanan, pilih di `testdb` sebelah`listTodos(id: ID!): Todo`. Handler permintaan menggunakan fungsi utilitas pilih untuk membangun permintaan secara dinamis pada waktu berjalan.

```
export function request(ctx) {
    const { filter = {}, limit = 100, nextToken } = ctx.args;
    const offset = nextToken ? +util.base64Decode(nextToken) : 0;
    const statement = select({
        table: 'todos',
        columns: '*',
        limit,
        offset,
        where: filter,
    });
    return createPgStatement(statement)
}
```

Kami ingin memfilter `todos` berdasarkan `due` tanggal. Mari kita perbarui resolver untuk mentransmisikan `due` nilai ke. `DATE` Perbarui daftar impor dan penangan permintaan sebagai berikut.

```
import { util } from '@aws-appsync/utils';
import * as rds from '@aws-appsync/utils/rds';

export function request(ctx) {
  const { filter: where = {}, limit = 100, nextToken } = ctx.args;
  const offset = nextToken ? +util.base64Decode(nextToken) : 0;

  // if `due` is used in a filter, CAST the values to DATE.
  if (where.due) {
    Object.entries(where.due).forEach(([k, v]) => {
      if (k === 'between') {
        where.due[k] = v.map((d) => rds.typeHint.DATE(d));
      } else {
        where.due[k] = rds.typeHint.DATE(v);
      }
    });
  }

  const statement = rds.select({
    table: 'todos',
    columns: '*',
    limit,
    offset,
    where,
  });
  return rds.createPgStatement(statement);
}

export function response(ctx) {
  const {
    args: { limit = 100, nextToken },
    error,
    result,
  } = ctx;
  if (error) {
    return util.appendError(error.message, error.type, result);
  }
  const offset = nextToken ? +util.base64Decode(nextToken) : 0;
  const items = rds.toJsonObject(result)[0];
  const endOfResults = items?.length < limit;
  const token = endOfResults ? null : util.base64Encode(`${offset + limit}`);
  return { items, nextToken: token };
}
```

Dalam **Queries** editor lakukan hal berikut.

```
query LIST {
  listTodos(limit: 10, filter: {due: {between: ["2021-01-01", "2025-01-02"]}}) {
    items {
      id
      due
      description
    }
  }
}
```

### mutasi.updateTodo
<a name="updatetodo"></a>

Anda juga bisa `update` a`Todo`. Dari editor **Queries**, mari kita perbarui `Todo` item pertama kami. `id` `1`

```
mutation UPDATE {
  updateTodo(input: {id: 1, description: "edits"}) {
    description
    due
    id
  }
}
```

Perhatikan bahwa Anda harus menentukan item `id` yang Anda perbarui. Anda juga dapat menentukan kondisi untuk hanya memperbarui item yang memenuhi kondisi tertentu. Misalnya, kami mungkin hanya ingin mengedit item jika deskripsi dimulai dengan `edits` sebagai berikut.

```
mutation UPDATE {
  updateTodo(input: {id: 1, description: "edits: make a change"}, condition: {description: {beginsWith: "edits"}}) {
    description
    due
    id
  }
}
```

Sama seperti cara kami menangani `create` dan `list` operasi kami, kami dapat memperbarui resolver kami untuk mentransmisikan `due` bidang ke a. `DATE` Simpan perubahan ini `updateTodo` sebagai berikut.

```
import { util } from '@aws-appsync/utils';
import * as rds from '@aws-appsync/utils/rds';

export function request(ctx) {
  const { input: { id, ...values }, condition = {}, } = ctx.args;
  const where = { ...condition, id: { eq: id } };

  // if `due` is used in a condition, CAST the values to DATE.
  if (condition.due) {
    Object.entries(condition.due).forEach(([k, v]) => {
      if (k === 'between') {
        condition.due[k] = v.map((d) => rds.typeHint.DATE(d));
      } else {
        condition.due[k] = rds.typeHint.DATE(v);
      }
    });
  }

  // if a due date is provided, cast is as `DATE`
  if (values.due) {
    values.due = rds.typeHint.DATE(values.due);
  }

  const updateStatement = rds.update({
    table: 'todos',
    values,
    where,
    returning: '*',
  });
  return rds.createPgStatement(updateStatement);
}

export function response(ctx) {
  const { error, result } = ctx;
  if (error) {
    return util.appendError(error.message, error.type, result);
  }
  return rds.toJsonObject(result)[0][0];
}
```

Sekarang coba pembaruan dengan kondisi:

```
mutation UPDATE {
  updateTodo(
    input: {
        id: 1, description: "edits: make a change", due: "2023-12-12"},
    condition: {
        description: {beginsWith: "edits"}, due: {ge: "2023-11-08"}})
    {
          description
          due
          id
        }
}
```

### mutasi.deleteTodo
<a name="deletetodo"></a>

Anda bisa `delete` a `Todo` dengan `deleteTodo` mutasi. Ini berfungsi seperti `updateTodo` mutasi, dan Anda harus menentukan item `id` yang ingin Anda hapus sebagai berikut.

```
mutation DELETE {
  deleteTodo(input: {id: 1}) {
    description
    due
    id
  }
}
```

### Menulis kueri kustom
<a name="writing-custom-queries"></a>

Kami telah menggunakan utilitas `rds` modul untuk membuat pernyataan SQL kami. Kami juga dapat menulis pernyataan statis kustom kami sendiri untuk berinteraksi dengan database kami. Pertama, perbarui skema untuk menghapus `id` bidang dari `CreateTask` input.

```
input CreateTaskInput {
    todoId: Int!
    description: String
}
```

Selanjutnya, buat beberapa tugas. Suatu tugas memiliki hubungan kunci asing dengan `Todo` sebagai berikut.

```
mutation TASKS {
  a: createTask(input: {todoId: 2, description: "my first sub task"}) { id }
  b:createTask(input: {todoId: 2, description: "another sub task"}) { id }
  c: createTask(input: {todoId: 2, description: "a final sub task"}) { id }
}
```

Buat bidang baru dalam `Query` tipe Anda yang disebut `getTodoAndTasks` sebagai berikut.

```
getTodoAndTasks(id: Int!): Todo
```

Tambahkan `tasks` bidang ke `Todo` jenis sebagai berikut.

```
type Todo {
    due: AWSDate!
    id: Int!
    createdAt: String
    description: String!
    tasks:TaskConnection
}
```

Simpan skema. Dari editor skema di konsol, di sisi kanan, pilih **Lampirkan Resolver** untuk. `getTodosAndTasks(id: Int!): Todo` Pilih sumber data Amazon RDS Anda. Perbarui resolver Anda dengan kode berikut.

```
import { sql, createPgStatement,toJsonObject } from '@aws-appsync/utils/rds';

export function request(ctx) {
    return createPgStatement(
        sql`SELECT * from todos where id = ${ctx.args.id}`,
        sql`SELECT * from tasks where "todoId" = ${ctx.args.id}`);
}

export function response(ctx) {
    const result = toJsonObject(ctx.result);
    const todo = result[0][0];
    if (!todo) {
        return null;
    }
    todo.tasks = { items: result[1] };
    return todo;
}
```

Dalam kode ini, kita menggunakan template `sql` tag untuk menulis pernyataan SQL bahwa kita dapat dengan aman meneruskan nilai dinamis pada waktu berjalan. `createPgStatement`dapat mengambil hingga dua permintaan SQL pada satu waktu. Kami menggunakannya untuk mengirim satu kueri untuk kami `todo` dan yang lain untuk kami`tasks`. Anda bisa melakukan ini dengan `JOIN` pernyataan atau metode lain dalam hal ini. Idenya adalah mampu menulis pernyataan SQL Anda sendiri untuk mengimplementasikan logika bisnis Anda. Untuk menggunakan kueri di editor **Kueri**, lakukan hal berikut.

```
query TodoAndTasks {
  getTodosAndTasks(id: 2) {
    id
    due
    description
    tasks {
      items {
        id
        description
      }
    }
  }
}
```

## Menghapus klaster Anda
<a name="rds-delete-cluster"></a>

**penting**  
Menghapus cluster bersifat permanen. Tinjau proyek Anda secara menyeluruh sebelum melakukan tindakan ini.

Untuk menghapus klaster Anda:

```
$ aws rds delete-db-cluster \
    --db-cluster-identifier appsync-tutorial \
    --skip-final-snapshot
```