

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

# JavaScript tutorial resolver untuk AWS AppSync
<a name="tutorials-js"></a>

Sumber data dan resolver digunakan oleh AWS AppSync untuk menerjemahkan permintaan GraphQL dan mengambil informasi dari sumber daya Anda. AWS AWS AppSync mendukung penyediaan otomatis dan koneksi dengan tipe sumber data tertentu. AWS AppSync juga mendukung AWS Lambda, Amazon DynamoDB, database relasional (Amazon Aurora Tanpa Server), Layanan OpenSearch Amazon, dan titik akhir HTTP sebagai sumber data. Anda dapat menggunakan GraphQL API dengan sumber daya yang AWS ada atau membangun sumber data dan resolver dari awal. Bagian berikut dimaksudkan untuk menjelaskan beberapa kasus penggunaan GraphQL yang lebih umum dalam bentuk tutorial.

**Topics**
+ [Membuat aplikasi posting sederhana menggunakan DynamoDB resolvers JavaScript](tutorial-dynamodb-resolvers-js.md)
+ [Menggunakan AWS Lambda resolver](tutorial-lambda-resolvers-js.md)
+ [Menggunakan resolver lokal](tutorial-local-resolvers-js.md)
+ [Menggabungkan resolver GraphQL](tutorial-combining-graphql-resolvers-js.md)
+ [Menggunakan OpenSearch penyelesai Layanan](tutorial-elasticsearch-resolvers-js.md)
+ [Melakukan transaksi DynamoDB](tutorial-dynamodb-transact-js.md)
+ [Menggunakan operasi batch DynamoDB](tutorial-dynamodb-batch-js.md)
+ [Menggunakan resolver HTTP](tutorial-http-resolvers-js.md)
+ [Menggunakan Aurora PostgreSQL dengan Data API](aurora-serverless-tutorial-js.md)

# Membuat aplikasi posting sederhana menggunakan DynamoDB resolvers JavaScript
<a name="tutorial-dynamodb-resolvers-js"></a>

Dalam tutorial ini, Anda akan mengimpor tabel Amazon DynamoDB Anda dan menghubungkannya AWS AppSync untuk membangun GraphQL API yang berfungsi penuh JavaScript menggunakan resolver pipeline yang dapat Anda manfaatkan dalam aplikasi Anda sendiri.

Anda akan menggunakan AWS AppSync konsol untuk menyediakan sumber daya Amazon DynamoDB, membuat resolver, dan menghubungkannya ke sumber data Anda. Anda juga akan dapat membaca dan menulis ke database Amazon DynamoDB Anda melalui pernyataan GraphQL dan berlangganan data waktu nyata.

Ada langkah-langkah khusus yang harus diselesaikan agar pernyataan GraphQL diterjemahkan ke operasi Amazon DynamoDB dan agar tanggapan diterjemahkan kembali ke GraphQL. Tutorial ini menguraikan proses konfigurasi melalui beberapa skenario dunia nyata dan pola akses data.

## Membuat GraphQL API
<a name="create-graphql-api"></a>

**Untuk membuat GraphQL API di AWS AppSync**

1. Buka AppSync konsol dan pilih **Buat API**.

1. Pilih **Desain dari awal** dan pilih **Berikutnya**.

1. Beri nama API Anda`PostTutorialAPI`, lalu pilih **Berikutnya**. Lewati ke halaman ulasan sambil menjaga opsi lainnya disetel ke nilai defaultnya dan pilih`Create`.

 AWS AppSync Konsol membuat API GraphQL baru untuk Anda. Dengan detault, ini menggunakan mode otentikasi kunci API. Anda dapat menggunakan konsol untuk mengatur sisa GraphQL API dan menjalankan kueri terhadapnya selama sisa tutorial ini.

## Mendefinisikan API posting dasar
<a name="define-post-api"></a>

Sekarang setelah Anda memiliki GraphQL API, Anda dapat menyiapkan skema dasar yang memungkinkan pembuatan dasar, pengambilan, dan penghapusan data posting.

**Untuk menambahkan data ke skema Anda**

1. Di API Anda, pilih tab **Skema**.

1. Kami akan membuat skema yang mendefinisikan `Post` tipe dan operasi `addPost` untuk menambah dan mendapatkan `Post` objek. Di panel **Skema**, ganti konten dengan kode berikut:

   ```
   schema {
       query: Query
       mutation: Mutation
   }
   
   type Query {
       getPost(id: ID): Post
   }
   
   type Mutation {
       addPost(
           id: ID!
           author: String!
           title: String!
           content: String!
           url: String!
       ): Post!
   }
   
   type Post {
       id: ID!
       author: String
       title: String
       content: String
       url: String
       ups: Int!
       downs: Int!
       version: Int!
   }
   ```

1. Pilih **Simpan Skema**.

## Menyiapkan tabel Amazon DynamoDB Anda
<a name="configure-dynamodb"></a>

 AWS AppSync Konsol dapat membantu menyediakan AWS sumber daya yang diperlukan untuk menyimpan sumber daya Anda sendiri di tabel Amazon DynamoDB. Pada langkah ini, Anda akan membuat tabel Amazon DynamoDB untuk menyimpan posting Anda. Anda juga akan menyiapkan [indeks sekunder](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SecondaryIndexes.html) yang akan kita gunakan nanti.

**Untuk membuat tabel Amazon DynamoDB**

1. Pada halaman **Skema**, pilih **Buat Sumber Daya**.

1. Pilih **Gunakan tipe yang ada**, lalu pilih `Post` jenisnya.

1. Di bagian **Indeks Tambahan**, pilih **Tambah Indeks**.

1. Beri nama indeksnya`author-index`.

1. Atur `Primary key` ke `author` dan `Sort` kunci ke`None`.

1. Nonaktifkan **Secara otomatis menghasilkan GraphQL**. Dalam contoh ini, kita akan membuat resolver sendiri.

1. Pilih **Buat**.

Anda sekarang memiliki sumber data baru yang disebut`PostTable`, yang dapat Anda lihat dengan mengunjungi **Sumber data** di tab samping. Anda akan menggunakan sumber data ini untuk menautkan kueri dan mutasi Anda ke tabel Amazon DynamoDB Anda. 

## Menyiapkan AddPost resolver (Amazon DynamoDB) PutItem
<a name="configure-addpost"></a>

Sekarang AWS AppSync mengetahui tabel Amazon DynamoDB, Anda dapat menautkannya ke kueri dan mutasi individual dengan mendefinisikan resolver. Resolver pertama yang Anda buat adalah resolver `addPost` pipeline yang digunakan JavaScript, yang memungkinkan Anda membuat postingan di tabel Amazon DynamoDB Anda. Pipeline resolver memiliki komponen-komponen berikut: 
+ Lokasi dalam skema GraphQL untuk melampirkan resolver. Dalam hal ini, Anda menyiapkan resolver di `createPost` bidang pada tipe. `Mutation` Penyelesai ini akan dipanggil saat pemanggil memanggil mutasi. `{ addPost(...){...} }` 
+ Sumber data yang akan digunakan untuk resolver ini. Dalam hal ini, Anda ingin menggunakan sumber data DynamoDB yang Anda tentukan sebelumnya, sehingga Anda dapat menambahkan entri ke dalam `post-table-for-tutorial` tabel DynamoDB.
+ Penangan permintaan. Handler permintaan adalah fungsi yang menangani permintaan masuk dari pemanggil dan menerjemahkannya ke dalam instruksi untuk AWS AppSync melakukan terhadap DynamoDB.
+ Penangan respons. Tugas penangan respons adalah menangani respons dari DynamoDB dan menerjemahkannya kembali menjadi sesuatu yang diharapkan GraphQL. Ini berguna jika bentuk data di DynamoDB berbeda dengan `Post` tipe di GraphQL, tetapi dalam hal ini mereka memiliki bentuk yang sama, jadi Anda cukup melewatkan data. 

**Untuk mengatur resolver Anda**

1. Di API Anda, pilih tab **Skema**.

1. **Di panel **Resolvers**, temukan `addPost` bidang di bawah `Mutation` tipe, lalu pilih Lampirkan.**

1. Pilih sumber data Anda, lalu pilih **Buat**.

1. Di editor kode Anda, ganti kode dengan cuplikan ini:

   ```
   import { util } from '@aws-appsync/utils'
   import * as ddb from '@aws-appsync/utils/dynamodb'
   
   export function request(ctx) {
   	const item = { ...ctx.arguments, ups: 1, downs: 0, version: 1 }
   	const key = { id: ctx.args.id ?? util.autoId() }
   	return ddb.put({ key, item })
   }
   
   export function response(ctx) {
   	return ctx.result
   }
   ```

1. Pilih **Simpan**.

**catatan**  
Dalam kode ini, Anda menggunakan utilitas modul DynamoDB yang memungkinkan Anda untuk dengan mudah membuat permintaan DynamoDB.

AWS AppSync dilengkapi dengan utilitas untuk pembuatan ID otomatis yang disebut`util.autoId()`, yang digunakan untuk menghasilkan ID untuk posting baru Anda. Jika Anda tidak menentukan ID, utilitas akan secara otomatis menghasilkannya untuk Anda.

```
const key = { id: ctx.args.id ?? util.autoId() }
```

Untuk informasi selengkapnya tentang utilitas yang tersedia JavaScript, lihat [fitur JavaScript runtime untuk resolver](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference-js.html) dan fungsi. 

### Panggil API untuk menambahkan postingan
<a name="call-api-addpost"></a>

Sekarang resolver telah dikonfigurasi, AWS AppSync dapat menerjemahkan `addPost` mutasi yang masuk ke operasi Amazon DynamoDB. `PutItem` Anda sekarang dapat menjalankan mutasi untuk meletakkan sesuatu di tabel.

**Untuk menjalankan operasi**

1. Di API Anda, pilih tab **Kueri**.

1. Di panel **Kueri**, tambahkan mutasi berikut:

   ```
   mutation addPost {
     addPost(
       id: 123,
       author: "AUTHORNAME"
       title: "Our first post!"
       content: "This is our first post."
       url: "https://aws.amazon.com/appsync/"
     ) {
       id
       author
       title
       content
       url
       ups
       downs
       version
     }
   }
   ```

1. Pilih **Run** (tombol putar oranye), lalu pilih`addPost`. Hasil posting yang baru dibuat akan muncul di panel **Hasil** di sebelah kanan panel **Kueri.** Itu terlihat serupa dengan yang berikut ini:

   ```
   {
     "data": {
       "addPost": {
         "id": "123",
         "author": "AUTHORNAME",
         "title": "Our first post!",
         "content": "This is our first post.",
         "url": "https://aws.amazon.com/appsync/",
         "ups": 1,
         "downs": 0,
         "version": 1
       }
     }
   }
   ```

Penjelasan berikut menunjukkan apa yang terjadi:

1. AWS AppSync menerima permintaan `addPost` mutasi.

1. AWS AppSync mengeksekusi penangan permintaan dari resolver. `ddb.put`Fungsi membuat `PutItem` permintaan yang terlihat seperti ini:

   ```
   {
     operation: 'PutItem',
     key: { id: { S: '123' } },
     attributeValues: {
       downs: { N: 0 },
       author: { S: 'AUTHORNAME' },
       ups: { N: 1 },
       title: { S: 'Our first post!' },
       version: { N: 1 },
       content: { S: 'This is our first post.' },
       url: { S: 'https://aws.amazon.com/appsync/' }
     }
   }
   ```

1. AWS AppSync menggunakan nilai ini untuk menghasilkan dan menjalankan permintaan Amazon `PutItem` DynamoDB.

1. AWS AppSync mengambil hasil `PutItem` permintaan dan mengubahnya kembali ke tipe GraphQL.

   ```
   {
       "id" : "123",
       "author": "AUTHORNAME",
       "title": "Our first post!",
       "content": "This is our first post.",
       "url": "https://aws.amazon.com/appsync/",
       "ups" : 1,
       "downs" : 0,
       "version" : 1
   }
   ```

1. Response handler mengembalikan hasilnya segera (`return ctx.result`).

1. Hasil akhir terlihat dalam respons GraphQL.

## Menyiapkan resolver GetPost (Amazon DynamoDB) GetItem
<a name="configure-getpost"></a>

Sekarang Anda dapat menambahkan data ke tabel Amazon DynamoDB, Anda perlu mengatur kueri sehingga `getPost` dapat mengambil data itu dari tabel. Untuk melakukan ini, Anda mengatur resolver lain.

**Untuk menambahkan resolver Anda**

1. Di API Anda, pilih tab **Skema**.

1. **Di panel **Resolvers** di sebelah kanan, temukan `getPost` bidang pada `Query` jenis dan kemudian pilih Lampirkan.**

1. Pilih sumber data Anda, lalu pilih **Buat**.

1. Di editor kode, ganti kode dengan cuplikan ini:

   ```
   import * as ddb from '@aws-appsync/utils/dynamodb'
   	
   export function request(ctx) {
   	return ddb.get({ key: { id: ctx.args.id } })
   }
   
   export const response = (ctx) => ctx.result
   ```

1. Simpan resolver Anda.

**catatan**  
Dalam resolver ini, kita menggunakan ekspresi fungsi panah untuk handler respon.

### Panggil API untuk mendapatkan postingan
<a name="call-api-getpost"></a>

Sekarang resolver telah diatur, AWS AppSync tahu cara menerjemahkan `getPost` kueri masuk ke operasi Amazon DynamoDB. `GetItem` Anda sekarang dapat menjalankan kueri untuk mengambil posting yang Anda buat sebelumnya.

**Untuk menjalankan kueri**

1. Di API Anda, pilih tab **Kueri**. 

1. Di panel **Kueri**, tambahkan kode berikut, dan gunakan id yang Anda salin setelah membuat posting Anda:

   ```
   query getPost {
     getPost(id: "123") {
       id
       author
       title
       content
       url
       ups
       downs
       version
     }
   }
   ```

1. Pilih **Run** (tombol putar oranye), lalu pilih`getPost`. Hasil posting yang baru dibuat akan muncul di panel **Hasil** di sebelah kanan panel **Kueri.**

1. **Postingan yang diambil dari Amazon DynamoDB akan muncul **di panel Hasil di** sebelah kanan panel Kueri.** Itu terlihat serupa dengan yang berikut ini:

   ```
   {
     "data": {
       "getPost": {
         "id": "123",
         "author": "AUTHORNAME",
         "title": "Our first post!",
         "content": "This is our first post.",
         "url": "https://aws.amazon.com/appsync/",
         "ups": 1,
         "downs": 0,
         "version": 1
       }
     }
   }
   ```

Atau, ambil contoh berikut:

```
query getPost {
  getPost(id: "123") {
    id
    author
    title
  }
}
```

Jika `getPost` kueri Anda hanya membutuhkan`id`,, dan `author``title`, Anda dapat mengubah fungsi permintaan Anda untuk menggunakan ekspresi proyeksi untuk menentukan hanya atribut yang Anda inginkan dari tabel DynamoDB Anda untuk menghindari transfer data yang tidak perlu dari DynamoDB ke. AWS AppSync Misalnya, fungsi permintaan mungkin terlihat seperti cuplikan di bawah ini:

```
import * as ddb from '@aws-appsync/utils/dynamodb'

export function request(ctx) {
	return ddb.get({
		key: { id: ctx.args.id },
		projection: ['author', 'id', 'title'],
	})
}

export const response = (ctx) => ctx.result
```

Anda juga dapat menggunakan [selectionSetList](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html#aws-appsync-resolver-context-reference-info-js)with `getPost` untuk mewakili`expression`:

```
import * as ddb from '@aws-appsync/utils/dynamodb'

export function request(ctx) {
	const projection = ctx.info.selectionSetList.map((field) => field.replace('/', '.'))
	return ddb.get({ key: { id: ctx.args.id }, projection })
}

export const response = (ctx) => ctx.result
```

## Buat mutasi UpdatePost (Amazon DynamoDB) UpdateItem
<a name="configure-updatepost"></a>

Sejauh ini, Anda dapat membuat dan mengambil `Post` objek di Amazon DynamoDB. Selanjutnya, Anda akan menyiapkan mutasi baru untuk memperbarui objek. Dibandingkan dengan `addPost` mutasi yang mengharuskan semua bidang ditentukan, mutasi ini memungkinkan Anda untuk hanya menentukan bidang yang ingin Anda ubah. Ini juga memperkenalkan `expectedVersion` argumen baru yang memungkinkan Anda menentukan versi yang ingin Anda modifikasi. Anda akan mengatur kondisi yang memastikan bahwa Anda memodifikasi versi terbaru dari objek. Anda akan melakukan ini menggunakan operasi `UpdateItem` Amazon DynamoD.sc

**Untuk memperbarui resolver Anda**

1. Di API Anda, pilih tab **Skema**.

1. Di panel **Skema**, ubah `Mutation` tipe untuk menambahkan `updatePost` mutasi baru sebagai berikut:

   ```
   type Mutation {
       updatePost(
           id: ID!,
           author: String,
           title: String,
           content: String,
           url: String,
           expectedVersion: Int!
       ): Post
       
       addPost(
           id: ID
           author: String!
           title: String!
           content: String!
           url: String!
       ): Post!
   }
   ```

1. Pilih **Simpan Skema**.

1. **Di panel **Resolvers** di sebelah kanan, temukan `updatePost` bidang yang baru dibuat pada `Mutation` jenisnya, lalu pilih Lampirkan.** Buat resolver baru Anda menggunakan cuplikan di bawah ini:

   ```
   import { util } from '@aws-appsync/utils';
   import * as ddb from '@aws-appsync/utils/dynamodb';
   
   export function request(ctx) {
     const { id, expectedVersion, ...rest } = ctx.args;
     const values = Object.entries(rest).reduce((obj, [key, value]) => {
       obj[key] = value ?? ddb.operations.remove();
       return obj;
     }, {});
   
     return ddb.update({
       key: { id },
       condition: { version: { eq: expectedVersion } },
       update: { ...values, version: ddb.operations.increment(1) },
     });
   }
   
   export function response(ctx) {
     const { error, result } = ctx;
     if (error) {
       util.appendError(error.message, error.type);
     }
     return result;
   ```

1. Simpan semua perubahan yang Anda buat.

Resolver ini digunakan `ddb.update` untuk membuat permintaan Amazon DynamoDB. `UpdateItem` Alih-alih menulis seluruh item, Anda hanya meminta Amazon DynamoDB untuk memperbarui atribut tertentu. Ini dilakukan dengan menggunakan ekspresi pembaruan Amazon DynamoDB.

`ddb.update`Fungsi mengambil kunci dan objek update sebagai argumen. Kemudian, Anda memeriksa nilai-nilai argumen yang masuk. Ketika nilai diatur ke`null`, gunakan operasi `remove` DynamoDB untuk memberi sinyal bahwa nilai harus dihapus dari item DynamoDB.

Ada juga `condition` bagian baru. Ekspresi kondisi memungkinkan Anda memberi tahu AWS AppSync dan Amazon DynamoDB apakah permintaan harus berhasil atau tidak berdasarkan status objek yang sudah ada di Amazon DynamoDB sebelum operasi dilakukan. Dalam hal ini, Anda hanya ingin `UpdateItem` permintaan berhasil jika `version` bidang item yang saat ini ada di Amazon DynamoDB sama persis dengan `expectedVersion` argumen. Ketika item diperbarui, kami ingin menambah nilai. `version` Ini mudah dilakukan dengan fungsi operasi`increment`.

Untuk informasi selengkapnya tentang ekspresi kondisi, lihat dokumentasi [Ekspresi kondisi](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-condition-expressions).

Untuk info selengkapnya tentang `UpdateItem` permintaan, lihat [UpdateItem](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-updateitem)dokumentasi dan dokumentasi modul [DynamoDB](https://docs.aws.amazon.com/appsync/latest/devguide/built-in-modules-js.html). 

Untuk informasi selengkapnya tentang cara menulis ekspresi pembaruan, lihat dokumentasi [DynamoDB UpdateExpressions](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html).

### Panggil API untuk memperbarui posting
<a name="call-api-updatepost"></a>

Mari kita coba memperbarui `Post` objek dengan resolver baru.

**Untuk memperbarui objek Anda**

1. Di API Anda, pilih tab **Kueri**.

1. Di panel **Kueri**, tambahkan mutasi berikut. Anda juga perlu memperbarui `id` argumen ke nilai yang Anda catat sebelumnya:

   ```
   mutation updatePost {
     updatePost(
       id:123
       title: "An empty story"
       content: null
       expectedVersion: 1
     ) {
       id
       author
       title
       content
       url
       ups
       downs
       version
     }
   }
   ```

1. Pilih **Run** (tombol putar oranye), lalu pilih`updatePost`.

1. **Posting yang diperbarui di Amazon DynamoDB akan muncul di panel Hasil **di** sebelah kanan panel Kueri.** Itu terlihat serupa dengan yang berikut ini:

   ```
   {
     "data": {
       "updatePost": {
         "id": "123",
         "author": "A new author",
         "title": "An empty story",
         "content": null,
         "url": "https://aws.amazon.com/appsync/",
         "ups": 1,
         "downs": 0,
         "version": 2
       }
     }
   }
   ```

Dalam permintaan ini, Anda meminta AWS AppSync dan Amazon DynamoDB untuk memperbarui dan bidang saja`title`. `content` Semua bidang lainnya dibiarkan sendiri (selain menambah `version` bidang). Anda mengatur `title` atribut ke nilai baru dan menghapus `content` atribut dari posting. Bidang `author``url`,`ups`, dan `downs` ladang dibiarkan tak tersentuh. Coba jalankan permintaan mutasi lagi sambil meninggalkan permintaan persis apa adanya. Anda akan melihat respons yang mirip dengan berikut ini:

```
{
  "data": {
    "updatePost": null
  },
  "errors": [
    {
      "path": [
        "updatePost"
      ],
      "data": null,
      "errorType": "DynamoDB:ConditionalCheckFailedException",
      "errorInfo": null,
      "locations": [
        {
          "line": 2,
          "column": 3,
          "sourceName": null
        }
      ],
      "message": "The conditional request failed (Service: DynamoDb, Status Code: 400, Request ID: 1RR3QN5F35CS8IV5VR4OQO9NNBVV4KQNSO5AEMVJF66Q9ASUAAJG)"
    }
  ]
}
```

Permintaan gagal karena ekspresi kondisi dievaluasi menjadi`false`: 

1. Pertama kali Anda menjalankan permintaan, nilai `version` bidang posting di Amazon DynamoDB `1` adalah, yang cocok dengan argumen. `expectedVersion` Permintaan berhasil, yang berarti `version` bidang tersebut bertambah di Amazon DynamoDB ke. `2`

1. Kedua kalinya Anda menjalankan permintaan, nilai `version` bidang posting di Amazon DynamoDB `2` adalah, yang tidak cocok dengan argumen. `expectedVersion`

Pola ini biasanya disebut *penguncian optimis*.

## Buat mutasi suara (Amazon DynamoDB UpdateItem)
<a name="configure-vote-mutations"></a>

`Post`Jenis berisi `ups` dan `downs` bidang untuk mengaktifkan perekaman upvotes dan downvotes. Namun, pada saat ini, API tidak membiarkan kami melakukan apa pun dengan mereka. Mari tambahkan mutasi untuk memungkinkan kita meningkatkan dan menurunkan suara posting.

**Untuk menambahkan mutasi Anda**

1. Di API Anda, pilih tab **Skema**.

1. Di panel **Skema**, ubah `Mutation` jenisnya dan tambahkan `DIRECTION` enum untuk menambahkan mutasi suara baru:

   ```
   type Mutation {
       vote(id: ID!, direction: DIRECTION!): Post
       updatePost(
           id: ID!,
           author: String,
           title: String,
           content: String,
           url: String,
           expectedVersion: Int!
       ): Post
       addPost(
           id: ID,
           author: String!,
           title: String!,
           content: String!,
           url: String!
       ): Post!
   }
   
   enum DIRECTION {
     UP
     DOWN
   }
   ```

1. Pilih **Simpan Skema**.

1. **Di panel **Resolvers** di sebelah kanan, temukan `vote` bidang yang baru dibuat pada `Mutation` jenisnya, lalu pilih Lampirkan.** Buat resolver baru dengan membuat dan mengganti kode dengan cuplikan berikut:

   ```
   import * as ddb from '@aws-appsync/utils/dynamodb';
   
   export function request(ctx) {
     const field = ctx.args.direction === 'UP' ? 'ups' : 'downs';
     return ddb.update({
       key: { id: ctx.args.id },
       update: {
         [field]: ddb.operations.increment(1),
         version: ddb.operations.increment(1),
       },
     });
   }
   
   export const response = (ctx) => ctx.result;
   ```

1. Simpan semua perubahan yang Anda buat.

### Panggil API untuk melakukan upvote atau downvote sebuah postingan
<a name="call-api-vote"></a>

Sekarang resolver baru telah disiapkan, AWS AppSync tahu cara menerjemahkan masuk `upvotePost` atau `downvote` mutasi ke operasi Amazon DynamoDB. `UpdateItem` Anda sekarang dapat menjalankan mutasi untuk upvote atau downvote posting yang Anda buat sebelumnya.

**Untuk menjalankan mutasi Anda**

1. Di API Anda, pilih tab **Kueri**.

1. Di panel **Kueri**, tambahkan mutasi berikut. Anda juga perlu memperbarui `id` argumen ke nilai yang Anda catat sebelumnya:

   ```
   mutation votePost {
     vote(id:123, direction: UP) {
       id
       author
       title
       content
       url
       ups
       downs
       version
     }
   }
   ```

1. Pilih **Run** (tombol putar oranye), lalu pilih`votePost`.

1. **Posting yang diperbarui di Amazon DynamoDB akan muncul di panel Hasil **di** sebelah kanan panel Kueri.** Itu terlihat serupa dengan yang berikut ini:

   ```
   {
     "data": {
       "vote": {
         "id": "123",
         "author": "A new author",
         "title": "An empty story",
         "content": null,
         "url": "https://aws.amazon.com/appsync/",
         "ups": 6,
         "downs": 0,
         "version": 4
       }
     }
   }
   ```

1. Pilih **Jalankan** beberapa kali lagi. Anda akan melihat `version` bidang `ups` dan bertambah `1` setiap kali Anda menjalankan kueri.

1. Ubah kueri untuk menyebutnya dengan yang berbeda`DIRECTION`.

   ```
   mutation votePost {
     vote(id:123, direction: DOWN) {
       id
       author
       title
       content
       url
       ups
       downs
       version
     }
   }
   ```

1. Pilih **Run** (tombol putar oranye), lalu pilih`votePost`.

   Kali ini, Anda akan melihat `version` bidang `downs` dan bertambah `1` setiap kali Anda menjalankan kueri.

## Menyiapkan resolver DeletePost (Amazon DynamoDB) DeleteItem
<a name="configure-deletepost"></a>

Selanjutnya, Anda akan ingin membuat mutasi untuk menghapus posting. Anda akan melakukan ini menggunakan operasi `DeleteItem` Amazon DynamoDB.

**Untuk menambahkan mutasi Anda**

1. Dalam skema Anda, pilih tab **Skema**.

1. Di panel **Skema**, ubah `Mutation` tipe untuk menambahkan mutasi baru`deletePost`:

   ```
   type Mutation {
       deletePost(id: ID!, expectedVersion: Int): Post
       vote(id: ID!, direction: DIRECTION!): Post
       updatePost(
           id: ID!,
           author: String,
           title: String,
           content: String,
           url: String,
           expectedVersion: Int!
       ): Post
       addPost(
           id: ID
           author: String!,
           title: String!,
           content: String!,
           url: String!
       ): Post!
   }
   ```

1. Kali ini, Anda membuat `expectedVersion` bidang opsional. Selanjutnya, pilih **Simpan Skema**.

1. **Di panel **Resolvers** di sebelah kanan, temukan `delete` bidang yang baru dibuat dalam `Mutation` tipe, lalu pilih Lampirkan.** Buat resolver baru menggunakan kode berikut:

   ```
   import { util } from '@aws-appsync/utils'
   
   import { util } from '@aws-appsync/utils';
   import * as ddb from '@aws-appsync/utils/dynamodb';
   
   export function request(ctx) {
     let condition = null;
     if (ctx.args.expectedVersion) {
       condition = {
         or: [
           { id: { attributeExists: false } },
           { version: { eq: ctx.args.expectedVersion } },
         ],
       };
     }
     return ddb.remove({ key: { id: ctx.args.id }, condition });
   }
   
   export function response(ctx) {
     const { error, result } = ctx;
     if (error) {
       util.appendError(error.message, error.type);
     }
     return result;
   }
   ```
**catatan**  
`expectedVersion`Argumen adalah argumen opsional. Jika pemanggil menetapkan `expectedVersion` argumen dalam permintaan, penangan permintaan menambahkan kondisi yang hanya memungkinkan `DeleteItem` permintaan berhasil jika item sudah dihapus atau jika `version` atribut posting di Amazon DynamoDB sama persis dengan. `expectedVersion` Jika ditinggalkan, tidak ada ekspresi kondisi yang ditentukan pada `DeleteItem` permintaan. Itu berhasil terlepas dari nilai `version` atau apakah item tersebut ada di Amazon DynamoDB.  
Meskipun Anda menghapus item, Anda dapat mengembalikan item yang telah dihapus, jika belum dihapus.

Untuk info lebih lanjut tentang `DeleteItem` permintaan, lihat [DeleteItem](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-deleteitem)dokumentasi.

### Panggil API untuk menghapus postingan
<a name="call-api-delete"></a>

Sekarang resolver telah diatur, AWS AppSync tahu cara menerjemahkan `delete` mutasi yang masuk ke operasi Amazon DynamoDB. `DeleteItem` Anda sekarang dapat menjalankan mutasi untuk menghapus sesuatu di tabel.

**Untuk menjalankan mutasi Anda**

1. Di API Anda, pilih tab **Kueri**.

1. Di panel **Kueri**, tambahkan mutasi berikut. Anda juga perlu memperbarui `id` argumen ke nilai yang Anda catat sebelumnya:

   ```
   mutation deletePost {
     deletePost(id:123) {
       id
       author
       title
       content
       url
       ups
       downs
       version
     }
   }
   ```

1. Pilih **Run** (tombol putar oranye), lalu pilih`deletePost`.

1. Posting dihapus dari Amazon DynamoDB. **Perhatikan bahwa AWS AppSync mengembalikan nilai item yang telah dihapus dari Amazon DynamoDB, yang akan muncul di panel Hasil **di** sebelah kanan panel Kueri.** Itu terlihat serupa dengan yang berikut ini:

   ```
   {
     "data": {
       "deletePost": {
         "id": "123",
         "author": "A new author",
         "title": "An empty story",
         "content": null,
         "url": "https://aws.amazon.com/appsync/",
         "ups": 6,
         "downs": 4,
         "version": 12
       }
     }
   }
   ```

1. Nilai hanya dikembalikan jika panggilan ini `deletePost` adalah salah satu yang benar-benar menghapusnya dari Amazon DynamoDB. Pilih **Jalankan** lagi.

1. Panggilan masih berhasil, tetapi tidak ada nilai yang dikembalikan:

   ```
   {
     "data": {
       "deletePost": null
     }
   }
   ```

1. Sekarang, mari kita coba menghapus posting, tapi kali ini menentukan. `expectedValue` Pertama, Anda harus membuat posting baru karena Anda baru saja menghapus yang telah Anda kerjakan sejauh ini.

1. Di panel **Kueri**, tambahkan mutasi berikut:

   ```
   mutation addPost {
     addPost(
       id:123
       author: "AUTHORNAME"
       title: "Our second post!"
       content: "A new post."
       url: "https://aws.amazon.com/appsync/"
     ) {
       id
       author
       title
       content
       url
       ups
       downs
       version
     }
   }
   ```

1. Pilih **Run** (tombol putar oranye), lalu pilih`addPost`.

1. Hasil posting yang baru dibuat akan muncul di panel **Hasil** di sebelah kanan panel **Kueri.** Rekam `id` objek yang baru dibuat karena Anda akan membutuhkannya hanya dalam beberapa saat. Itu terlihat serupa dengan yang berikut ini:

   ```
   {
     "data": {
       "addPost": {
         "id": "123",
         "author": "AUTHORNAME",
         "title": "Our second post!",
         "content": "A new post.",
         "url": "https://aws.amazon.com/appsync/",
         "ups": 1,
         "downs": 0,
         "version": 1
       }
     }
   }
   ```

1. Sekarang, mari kita coba menghapus posting itu dengan nilai ilegal untuk **ExpectedVersion**. Di panel **Kueri**, tambahkan mutasi berikut. Anda juga perlu memperbarui `id` argumen ke nilai yang Anda catat sebelumnya:

   ```
   mutation deletePost {
     deletePost(
       id:123
       expectedVersion: 9999
     ) {
       id
       author
       title
       content
       url
       ups
       downs
       version
     }
   }
   ```

1. Pilih **Run** (tombol putar oranye), lalu pilih`deletePost`. Hasil berikut dikembalikan:

   ```
   {
     "data": {
       "deletePost": null
     },
     "errors": [
       {
         "path": [
           "deletePost"
         ],
         "data": null,
         "errorType": "DynamoDB:ConditionalCheckFailedException",
         "errorInfo": null,
         "locations": [
           {
             "line": 2,
             "column": 3,
             "sourceName": null
           }
         ],
         "message": "The conditional request failed (Service: DynamoDb, Status Code: 400, Request ID: 7083O037M1FTFRK038A4CI9H43VV4KQNSO5AEMVJF66Q9ASUAAJG)"
       }
     ]
   }
   ```

1. Permintaan gagal karena ekspresi kondisi dievaluasi. `false` Nilai untuk `version` posting di Amazon DynamoDB tidak cocok dengan yang ditentukan `expectedValue` dalam argumen. Nilai objek saat ini dikembalikan di `data` bidang di `errors` bagian respons GraphQL. Coba lagi permintaannya, tetapi `expectedVersion` perbaiki: 

   ```
   mutation deletePost {
     deletePost(
       id:123
       expectedVersion: 1
     ) {
       id
       author
       title
       content
       url
       ups
       downs
       version
     }
   }
   ```

1. Pilih **Run** (tombol putar oranye), lalu pilih`deletePost`. 

   Kali ini permintaan berhasil, dan nilai yang dihapus dari Amazon DynamoDB dikembalikan:

   ```
   {
     "data": {
       "deletePost": {
         "id": "123",
         "author": "AUTHORNAME",
         "title": "Our second post!",
         "content": "A new post.",
         "url": "https://aws.amazon.com/appsync/",
         "ups": 1,
         "downs": 0,
         "version": 1
       }
     }
   }
   ```

1. Pilih **Jalankan** lagi. Panggilan masih berhasil, tetapi kali ini tidak ada nilai yang dikembalikan karena posting sudah dihapus di Amazon DynamoDB.

   ```
   { "data": { "deletePost": null } }
   ```

## Menyiapkan resolver AllPost (Amazon DynamoDB Scan)
<a name="configure-allpost"></a>

Sejauh ini, API hanya berguna jika Anda mengetahui setiap posting yang ingin Anda lihat. `id` Mari tambahkan resolver baru yang mengembalikan semua posting dalam tabel.

**Untuk menambahkan mutasi Anda**

1. Di API Anda, pilih tab **Skema**.

1. Di panel **Skema**, ubah `Query` jenis untuk menambahkan `allPost` kueri baru sebagai berikut:

   ```
   type Query {
       allPost(limit: Int, nextToken: String): PaginatedPosts!
       getPost(id: ID): Post
   }
   ```

1. Tambahkan `PaginationPosts` tipe baru:

   ```
   type PaginatedPosts {
       posts: [Post!]!
       nextToken: String
   }
   ```

1. Pilih **Simpan Skema**.

1. **Di panel **Resolvers** di sebelah kanan, temukan `allPost` bidang yang baru dibuat dalam `Query` tipe, lalu pilih Lampirkan.** Buat resolver baru dengan kode berikut:

   ```
   import * as ddb from '@aws-appsync/utils/dynamodb';
   
   export function request(ctx) {
     const { limit = 20, nextToken } = ctx.arguments;
     return ddb.scan({ limit, nextToken });
   }
   
   export function response(ctx) {
     const { items: posts = [], nextToken } = ctx.result;
     return { posts, nextToken };
   }
   ```

   Handler permintaan resolver ini mengharapkan dua argumen opsional: 
   + `limit`- Menentukan jumlah maksimum item untuk kembali dalam satu panggilan.
   + `nextToken`- Digunakan untuk mengambil set hasil berikutnya (kami akan menunjukkan dari mana nilai untuk `nextToken` berasal nanti).

1. Simpan perubahan apa pun yang dilakukan pada resolver Anda.

Untuk informasi selengkapnya tentang `Scan` permintaan, lihat dokumentasi referensi [Pindai](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-scan).

### Panggil API untuk memindai semua posting
<a name="call-api-scan"></a>

Sekarang resolver telah diatur, AWS AppSync tahu cara menerjemahkan `allPost` kueri masuk ke operasi Amazon DynamoDB. `Scan` Anda sekarang dapat memindai tabel untuk mengambil semua posting. Sebelum Anda dapat mencobanya, Anda perlu mengisi tabel dengan beberapa data karena Anda telah menghapus semua yang telah Anda kerjakan sejauh ini.

**Untuk menambah dan menanyakan data**

1. Di API Anda, pilih tab **Kueri**.

1. Di panel **Kueri**, tambahkan mutasi berikut:

   ```
   mutation addPost {
     post1: addPost(id:1 author: "AUTHORNAME" title: "A series of posts, Volume 1" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
     post2: addPost(id:2 author: "AUTHORNAME" title: "A series of posts, Volume 2" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
     post3: addPost(id:3 author: "AUTHORNAME" title: "A series of posts, Volume 3" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
     post4: addPost(id:4 author: "AUTHORNAME" title: "A series of posts, Volume 4" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
     post5: addPost(id:5 author: "AUTHORNAME" title: "A series of posts, Volume 5" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
     post6: addPost(id:6 author: "AUTHORNAME" title: "A series of posts, Volume 6" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
     post7: addPost(id:7 author: "AUTHORNAME" title: "A series of posts, Volume 7" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
     post8: addPost(id:8 author: "AUTHORNAME" title: "A series of posts, Volume 8" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
     post9: addPost(id:9 author: "AUTHORNAME" title: "A series of posts, Volume 9" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
   }
   ```

1. Pilih **Run** (tombol putar oranye). 

1. Sekarang, mari kita pindai tabel, mengembalikan lima hasil sekaligus. Di panel **Kueri**, tambahkan kueri berikut:

   ```
   query allPost {
     allPost(limit: 5) {
       posts {
         id
         title
       }
       nextToken
     }
   }
   ```

1. Pilih **Run** (tombol putar oranye), lalu pilih`allPost`.

   Lima posting pertama akan muncul di panel **Hasil** di sebelah kanan panel **Kueri.** Itu terlihat serupa dengan yang berikut ini:

   ```
   {
     "data": {
       "allPost": {
         "posts": [
           {
             "id": "5",
             "title": "A series of posts, Volume 5"
           },
           {
             "id": "1",
             "title": "A series of posts, Volume 1"
           },
           {
             "id": "6",
             "title": "A series of posts, Volume 6"
           },
           {
             "id": "9",
             "title": "A series of posts, Volume 9"
           },
           {
             "id": "7",
             "title": "A series of posts, Volume 7"
           }
         ],
         "nextToken": "<token>"
       }
     }
   }
   ```

1. Anda menerima lima hasil dan `nextToken` yang dapat Anda gunakan untuk mendapatkan set hasil berikutnya. Perbarui `allPost` kueri untuk menyertakan `nextToken` dari kumpulan hasil sebelumnya: 

   ```
   query allPost {
     allPost(
       limit: 5
       nextToken: "<token>"
     ) {
       posts {
         id
         author
       }
       nextToken
     }
   }
   ```

1. Pilih **Run** (tombol putar oranye), lalu pilih`allPost`.

   Empat posting yang tersisa akan muncul di panel **Hasil** di sebelah kanan panel **Kueri.** Tidak ada `nextToken` dalam rangkaian hasil ini karena Anda telah membaca semua sembilan posting dengan tidak ada yang tersisa. Itu terlihat serupa dengan yang berikut ini:

   ```
   {
     "data": {
       "allPost": {
         "posts": [
           {
             "id": "2",
             "title": "A series of posts, Volume 2"
           },
           {
             "id": "3",
             "title": "A series of posts, Volume 3"
           },
           {
             "id": "4",
             "title": "A series of posts, Volume 4"
           },
           {
             "id": "8",
             "title": "A series of posts, Volume 8"
           }
         ],
         "nextToken": null
       }
     }
   }
   ```

## Menyiapkan resolver allPostsBy Penulis (Amazon DynamoDB Query)
<a name="configure-query"></a>

Selain memindai Amazon DynamoDB untuk semua posting, Anda juga dapat meminta Amazon DynamoDB untuk mengambil posting yang dibuat oleh penulis tertentu. Tabel Amazon DynamoDB yang Anda buat sebelumnya sudah memiliki panggilan `GlobalSecondaryIndex` yang `author-index` dapat Anda gunakan dengan operasi Amazon DynamoDB untuk mengambil semua `Query` posting yang dibuat oleh penulis tertentu.

**Untuk menambahkan kueri Anda**

1. Di API Anda, pilih tab **Skema**.

1. Di panel **Skema**, ubah `Query` jenis untuk menambahkan `allPostsByAuthor` kueri baru sebagai berikut:

   ```
   type Query {
       allPostsByAuthor(author: String!, limit: Int, nextToken: String): PaginatedPosts!
       allPost(limit: Int, nextToken: String): PaginatedPosts!
       getPost(id: ID): Post
   }
   ```

   Perhatikan bahwa ini menggunakan `PaginatedPosts` jenis yang sama dengan yang Anda gunakan dengan `allPost` kueri.

1. Pilih **Simpan Skema**.

1. **Di panel **Resolvers** di sebelah kanan, temukan `allPostsByAuthor` bidang yang baru dibuat pada `Query` jenisnya, lalu pilih Lampirkan.** Buat resolver menggunakan cuplikan di bawah ini:

   ```
   import * as ddb from '@aws-appsync/utils/dynamodb';
   
   export function request(ctx) {
     const { limit = 20, nextToken, author } = ctx.arguments;
     return ddb.query({
       index: 'author-index',
       query: { author: { eq: author } },
       limit,
       nextToken,
     });
   }
   
   export function response(ctx) {
     const { items: posts = [], nextToken } = ctx.result;
     return { posts, nextToken };
   }
   ```

   Seperti `allPost` resolver, resolver ini memiliki dua argumen opsional:
   + `limit`- Menentukan jumlah maksimum item untuk kembali dalam satu panggilan.
   + `nextToken`- Mengambil set hasil berikutnya (nilai untuk `nextToken` dapat diperoleh dari panggilan sebelumnya).

1. Simpan perubahan apa pun yang dilakukan pada resolver Anda.

Untuk informasi selengkapnya tentang `Query` permintaan, lihat dokumentasi referensi [kueri](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-query).

### Panggil API untuk menanyakan semua posting oleh penulis
<a name="call-api-query"></a>

Sekarang resolver telah diatur, AWS AppSync tahu bagaimana menerjemahkan `allPostsByAuthor` mutasi yang masuk ke operasi DynamoDB terhadap indeks. `Query` `author-index` Anda sekarang dapat meminta tabel untuk mengambil semua posting oleh penulis tertentu.

Sebelum ini, bagaimanapun, mari kita mengisi tabel dengan beberapa posting lagi, karena setiap posting sejauh ini memiliki penulis yang sama.

**Untuk menambahkan data dan kueri**

1. Di API Anda, pilih tab **Kueri**.

1. Di panel **Kueri**, tambahkan mutasi berikut:

   ```
   mutation addPost {
     post1: addPost(id:10 author: "Nadia" title: "The cutest dog in the world" content: "So cute. So very, very cute." url: "https://aws.amazon.com/appsync/" ) { author, title }
     post2: addPost(id:11 author: "Nadia" title: "Did you know...?" content: "AppSync works offline?" url: "https://aws.amazon.com/appsync/" ) { author, title }
     post3: addPost(id:12 author: "Steve" title: "I like GraphQL" content: "It's great" url: "https://aws.amazon.com/appsync/" ) { author, title }
   }
   ```

1. Pilih **Run** (tombol putar oranye), lalu pilih`addPost`.

1. Sekarang, mari kita menanyakan tabel, mengembalikan semua posting yang ditulis oleh. `Nadia` Di panel **Kueri**, tambahkan kueri berikut: 

   ```
   query allPostsByAuthor {
     allPostsByAuthor(author: "Nadia") {
       posts {
         id
         title
       }
       nextToken
     }
   }
   ```

1. Pilih **Run** (tombol putar oranye), lalu pilih`allPostsByAuthor`. **Semua posting yang ditulis oleh akan `Nadia` muncul di panel **Hasil** di sebelah kanan panel Queries.** Itu terlihat serupa dengan yang berikut ini:

   ```
   {
     "data": {
       "allPostsByAuthor": {
         "posts": [
           {
             "id": "10",
             "title": "The cutest dog in the world"
           },
           {
             "id": "11",
             "title": "Did you know...?"
           }
         ],
         "nextToken": null
       }
     }
   }
   ```

1. Pagination bekerja untuk hal yang `Query` sama seperti yang dilakukannya. `Scan` Sebagai contoh, mari kita cari semua posting dengan`AUTHORNAME`, mendapatkan lima sekaligus.

1. Di panel **Kueri**, tambahkan kueri berikut: 

   ```
   query allPostsByAuthor {
     allPostsByAuthor(
       author: "AUTHORNAME"
       limit: 5
     ) {
       posts {
         id
         title
       }
       nextToken
     }
   }
   ```

1. Pilih **Run** (tombol putar oranye), lalu pilih`allPostsByAuthor`. **Semua posting yang ditulis oleh akan `AUTHORNAME` muncul di panel **Hasil** di sebelah kanan panel Queries.** Itu terlihat serupa dengan yang berikut ini:

   ```
   {
     "data": {
       "allPostsByAuthor": {
         "posts": [
           {
             "id": "6",
             "title": "A series of posts, Volume 6"
           },
           {
             "id": "4",
             "title": "A series of posts, Volume 4"
           },
           {
             "id": "2",
             "title": "A series of posts, Volume 2"
           },
           {
             "id": "7",
             "title": "A series of posts, Volume 7"
           },
           {
             "id": "1",
             "title": "A series of posts, Volume 1"
           }
         ],
         "nextToken": "<token>"
       }
     }
   }
   ```

1. Perbarui `nextToken` argumen dengan nilai yang dikembalikan dari kueri sebelumnya sebagai berikut:

   ```
   query allPostsByAuthor {
     allPostsByAuthor(
       author: "AUTHORNAME"
       limit: 5
       nextToken: "<token>"
     ) {
       posts {
         id
         title
       }
       nextToken
     }
   }
   ```

1. Pilih **Run** (tombol putar oranye), lalu pilih`allPostsByAuthor`. **Posting yang tersisa yang ditulis oleh `AUTHORNAME` akan muncul di panel **Hasil** di sebelah kanan panel Kueri.** Itu terlihat serupa dengan yang berikut ini:

   ```
   {
     "data": {
       "allPostsByAuthor": {
         "posts": [
           {
             "id": "8",
             "title": "A series of posts, Volume 8"
           },
           {
             "id": "5",
             "title": "A series of posts, Volume 5"
           },
           {
             "id": "3",
             "title": "A series of posts, Volume 3"
           },
           {
             "id": "9",
             "title": "A series of posts, Volume 9"
           }
         ],
         "nextToken": null
       }
     }
   }
   ```

## Menggunakan set
<a name="using-sets"></a>

Sampai saat ini, `Post` tipenya telah menjadi key/value objek datar. Anda juga dapat memodelkan objek kompleks dengan resolver Anda, seperti set, daftar, dan peta. Mari kita perbarui `Post` jenis untuk menyertakan tag. Sebuah posting dapat memiliki nol atau lebih tag, yang disimpan di DynamoDB sebagai String Set. Anda juga akan menyiapkan beberapa mutasi untuk menambah dan menghapus tag, dan kueri baru untuk memindai posting dengan tag tertentu.

**Untuk mengatur data Anda**

1. Di API Anda, pilih tab **Skema**. 

1. Di panel **Skema**, ubah `Post` jenis untuk menambahkan `tags` bidang baru sebagai berikut:

   ```
   type Post {
     id: ID!
     author: String
     title: String
     content: String
     url: String
     ups: Int!
     downs: Int!
     version: Int!
     tags: [String!]
   }
   ```

1. Di panel **Skema**, ubah `Query` jenis untuk menambahkan `allPostsByTag` kueri baru sebagai berikut:

   ```
   type Query {
     allPostsByTag(tag: String!, limit: Int, nextToken: String): PaginatedPosts!
     allPostsByAuthor(author: String!, limit: Int, nextToken: String): PaginatedPosts!
     allPost(limit: Int, nextToken: String): PaginatedPosts!
     getPost(id: ID): Post
   }
   ```

1. Di panel **Skema**, ubah `Mutation` tipe untuk menambahkan baru `addTag` dan `removeTag` mutasi sebagai berikut:

   ```
   type Mutation {
     addTag(id: ID!, tag: String!): Post
     removeTag(id: ID!, tag: String!): Post
     deletePost(id: ID!, expectedVersion: Int): Post
     upvotePost(id: ID!): Post
     downvotePost(id: ID!): Post
     updatePost(
       id: ID!,
       author: String,
       title: String,
       content: String,
       url: String,
       expectedVersion: Int!
     ): Post
     addPost(
       author: String!,
       title: String!,
       content: String!,
       url: String!
     ): Post!
   }
   ```

1. Pilih **Simpan Skema**.

1. **Di panel **Resolvers** di sebelah kanan, temukan `allPostsByTag` bidang yang baru dibuat pada `Query` jenisnya, lalu pilih Lampirkan.** Buat resolver Anda menggunakan cuplikan di bawah ini:

   ```
   import * as ddb from '@aws-appsync/utils/dynamodb';
   
   export function request(ctx) {
     const { limit = 20, nextToken, tag } = ctx.arguments;
     return ddb.scan({ limit, nextToken, filter: { tags: { contains: tag } } });
   }
   
   export function response(ctx) {
     const { items: posts = [], nextToken } = ctx.result;
     return { posts, nextToken };
   }
   ```

1. Simpan perubahan apa pun yang telah Anda buat pada resolver Anda.

1. Sekarang, lakukan hal yang sama untuk `Mutation` bidang `addTag` menggunakan cuplikan di bawah ini:
**catatan**  
Meskipun utilitas DynamoDB saat ini tidak mendukung operasi set, Anda masih dapat berinteraksi dengan set dengan membuat permintaan sendiri.

   ```
   import { util } from '@aws-appsync/utils'
   
   export function request(ctx) {
   	const { id, tag } = ctx.arguments
   	const expressionValues = util.dynamodb.toMapValues({ ':plusOne': 1 })
   	expressionValues[':tags'] = util.dynamodb.toStringSet([tag])
   
   	return {
   		operation: 'UpdateItem',
   		key: util.dynamodb.toMapValues({ id }),
   		update: {
   			expression: `ADD tags :tags, version :plusOne`,
   			expressionValues,
   		},
   	}
   }
   
   export const response = (ctx) => ctx.result
   ```

1. Simpan perubahan apa pun yang dilakukan pada resolver Anda.

1. Ulangi ini sekali lagi untuk `Mutation` bidang `removeTag` menggunakan cuplikan di bawah ini:

   ```
   import { util } from '@aws-appsync/utils';
   	
   export function request(ctx) {
   	  const { id, tag } = ctx.arguments;
   	  const expressionValues = util.dynamodb.toMapValues({ ':plusOne': 1 });
   	  expressionValues[':tags'] = util.dynamodb.toStringSet([tag]);
   	
   	  return {
   	    operation: 'UpdateItem',
   	    key: util.dynamodb.toMapValues({ id }),
   	    update: {
   	      expression: `DELETE tags :tags ADD version :plusOne`,
   	      expressionValues,
   	    },
   	  };
   	}
   	
   	export const response = (ctx) => ctx.resultexport
   ```

1. Simpan perubahan apa pun yang dilakukan pada resolver Anda.

### Panggil API untuk bekerja dengan tag
<a name="call-api-tags"></a>

Sekarang setelah Anda menyiapkan resolver, AWS AppSync tahu cara menerjemahkan masuk `addTag``removeTag`, dan permintaan `allPostsByTag` ke DynamoDB dan operasi. `UpdateItem` `Scan` Untuk mencobanya, mari pilih salah satu posting yang Anda buat sebelumnya. Misalnya, mari kita gunakan posting yang ditulis oleh. `Nadia`

**Untuk menggunakan tag**

1. Di API Anda, pilih tab **Kueri**.

1. Di panel **Kueri**, tambahkan kueri berikut:

   ```
   query allPostsByAuthor {
     allPostsByAuthor(
       author: "Nadia"
     ) {
       posts {
         id
         title
       }
       nextToken
     }
   }
   ```

1. Pilih **Run** (tombol putar oranye), lalu pilih`allPostsByAuthor`.

1. Semua postingan Nadia akan muncul di panel **Hasil** di sebelah kanan panel **Kueri**. Itu terlihat serupa dengan yang berikut ini:

   ```
   {
     "data": {
       "allPostsByAuthor": {
         "posts": [
           {
             "id": "10",
             "title": "The cutest dog in the world"
           },
           {
             "id": "11",
             "title": "Did you known...?"
           }
         ],
         "nextToken": null
       }
     }
   }
   ```

1. Mari kita gunakan yang memiliki judul *The cutest dog in the world*. Rekam `id` karena Anda akan menggunakannya nanti. Sekarang, mari kita coba menambahkan `dog` tag.

1. Di panel **Kueri**, tambahkan mutasi berikut. Anda juga perlu memperbarui `id` argumen ke nilai yang Anda catat sebelumnya.

   ```
   mutation addTag {
     addTag(id:10 tag: "dog") {
       id
       title
       tags
     }
   }
   ```

1. Pilih **Run** (tombol putar oranye), lalu pilih`addTag`. Posting diperbarui dengan tag baru:

   ```
   {
     "data": {
       "addTag": {
         "id": "10",
         "title": "The cutest dog in the world",
         "tags": [
           "dog"
         ]
       }
     }
   }
   ```

1. Anda dapat menambahkan lebih banyak tag. Perbarui mutasi untuk mengubah `tag` argumen menjadi`puppy`:

   ```
   mutation addTag {
     addTag(id:10 tag: "puppy") {
       id
       title
       tags
     }
   }
   ```

1. Pilih **Run** (tombol putar oranye), lalu pilih`addTag`. Posting diperbarui dengan tag baru:

   ```
   {
     "data": {
       "addTag": {
         "id": "10",
         "title": "The cutest dog in the world",
         "tags": [
           "dog",
           "puppy"
         ]
       }
     }
   }
   ```

1. Anda juga dapat menghapus tag. Di panel **Kueri**, tambahkan mutasi berikut. Anda juga perlu memperbarui `id` argumen ke nilai yang Anda catat sebelumnya:

   ```
   mutation removeTag {
     removeTag(id:10 tag: "puppy") {
       id
       title
       tags
     }
   }
   ```

1. Pilih **Run** (tombol putar oranye), lalu pilih`removeTag`. Posting diperbarui dan `puppy` tag dihapus.

   ```
   {
     "data": {
       "addTag": {
         "id": "10",
         "title": "The cutest dog in the world",
         "tags": [
           "dog"
         ]
       }
     }
   }
   ```

1. Anda juga dapat mencari semua posting yang memiliki tag. Di panel **Kueri**, tambahkan kueri berikut: 

   ```
   query allPostsByTag {
     allPostsByTag(tag: "dog") {
       posts {
         id
         title
         tags
       }
       nextToken
     }
   }
   ```

1. Pilih **Run** (tombol putar oranye), lalu pilih`allPostsByTag`. Semua posting yang memiliki `dog` tag dikembalikan sebagai berikut:

   ```
   {
     "data": {
       "allPostsByTag": {
         "posts": [
           {
             "id": "10",
             "title": "The cutest dog in the world",
             "tags": [
               "dog",
               "puppy"
             ]
           }
         ],
         "nextToken": null
       }
     }
   }
   ```

## Kesimpulan
<a name="conclusion-dynamodb-tutorial-js"></a>

Dalam tutorial ini, Anda telah membangun API yang memungkinkan Anda memanipulasi `Post` objek di AWS AppSync DynamoDB menggunakan dan GraphQL. 

Untuk membersihkan, Anda dapat menghapus AWS AppSync GraphQL API dari konsol. 

**Untuk menghapus peran yang terkait dengan tabel DynamoDB Anda, pilih sumber data Anda di tabel Sumber **Data dan klik** edit.** Perhatikan nilai peran di bawah **Buat atau gunakan peran yang ada**. Buka konsol IAM untuk menghapus peran.

Untuk menghapus tabel DynamoDB Anda, klik pada nama tabel dalam daftar sumber data. Ini akan membawa Anda ke konsol DynamoDB tempat Anda dapat menghapus tabel. 

# Menggunakan AWS Lambda resolver di AWS AppSync
<a name="tutorial-lambda-resolvers-js"></a>

Anda dapat menggunakan AWS Lambda dengan AWS AppSync untuk menyelesaikan bidang GraphQL apa pun. Misalnya, kueri GraphQL mungkin mengirim panggilan ke instance Amazon Relational Database Service (Amazon RDS), dan mutasi GraphQL mungkin menulis ke aliran Amazon Kinesis. Di bagian ini, kami akan menunjukkan cara menulis fungsi Lambda yang melakukan logika bisnis berdasarkan pemanggilan operasi lapangan GraphQL.

## Powertools untuk AWS Lambda
<a name="powertools-graphql"></a>

Powertools for AWS Lambda GraphQL event handler menyederhanakan perutean dan pemrosesan peristiwa GraphQL dalam fungsi Lambda. Ini tersedia untuk Python dan TypeScript. Baca selengkapnya tentang GraphQL API Event Handler di Powertools AWS Lambda untuk dokumentasi, lihat referensi berikut.
+ [Powertools untuk AWS Lambda GraphQL Event Handler (Python)](https://docs.aws.amazon.com/powertools/python/latest/core/event_handler/appsync/)
+ [Powertools untuk AWS Lambda GraphQL Event Handler (TypeScript)](https://docs.aws.amazon.com/powertools/typescript/latest/features/event-handler/appsync-graphql/) 

## Buat fungsi Lambda
<a name="create-a-lam-function-js"></a>

Contoh berikut menunjukkan fungsi Lambda ditulis dalam `Node.js` (runtime: Node.js 18.x) yang melakukan operasi yang berbeda pada posting blog sebagai bagian dari aplikasi posting blog. Perhatikan bahwa kode harus disimpan dalam nama file dengan ekstensi.mis.

```
export const handler = async (event) => {
console.log('Received event {}', JSON.stringify(event, 3))

  const posts = {
1: { id: '1', title: 'First book', author: 'Author1', url: 'https://amazon.com/', content: 'SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1', ups: '100', downs: '10', },
    2: { id: '2', title: 'Second book', author: 'Author2', url: 'https://amazon.com', content: 'SAMPLE TEXT AUTHOR 2 SAMPLE TEXT AUTHOR 2 SAMPLE TEXT', ups: '100', downs: '10', },
    3: { id: '3', title: 'Third book', author: 'Author3', url: null, content: null, ups: null, downs: null },
    4: { id: '4', title: 'Fourth book', author: 'Author4', url: 'https://www.amazon.com/', content: 'SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4', ups: '1000', downs: '0', },
    5: { id: '5', title: 'Fifth book', author: 'Author5', url: 'https://www.amazon.com/', content: 'SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT', ups: '50', downs: '0', },
  }

  const relatedPosts = {
1: [posts['4']],
    2: [posts['3'], posts['5']],
    3: [posts['2'], posts['1']],
    4: [posts['2'], posts['1']],
    5: [],
  }

  console.log('Got an Invoke Request.')
  let result
  switch (event.field) {
case 'getPost':
      return posts[event.arguments.id]
    case 'allPosts':
      return Object.values(posts)
    case 'addPost':
      // return the arguments back
return event.arguments
    case 'addPostErrorWithData':
      result = posts[event.arguments.id]
      // attached additional error information to the post
      result.errorMessage = 'Error with the mutation, data has changed'
      result.errorType = 'MUTATION_ERROR'
return result
    case 'relatedPosts':
      return relatedPosts[event.source.id]
    default:
      throw new Error('Unknown field, unable to resolve ' + event.field)
  }
}
```

Fungsi Lambda ini mengambil posting dengan ID, menambahkan posting, mengambil daftar posting, dan mengambil posting terkait untuk posting tertentu. 

**catatan**  
Fungsi Lambda menggunakan `switch` pernyataan `event.field` untuk menentukan bidang mana yang sedang diselesaikan.

Buat fungsi Lambda ini menggunakan AWS Management Console.

## Konfigurasikan sumber data untuk Lambda
<a name="configure-data-source-for-lamlong-js"></a>

**Setelah Anda membuat fungsi Lambda, navigasikan ke GraphQL API Anda di AWS AppSync konsol, lalu pilih tab Sumber Data.**

Pilih **Buat sumber data**, masukkan **nama sumber data** yang ramah (misalnya,**Lambda**), dan kemudian untuk **tipe sumber data**, pilih **AWS Lambda fungsi**. Untuk **Region**, pilih Region yang sama dengan fungsi Anda. Untuk **Fungsi ARN**, pilih Amazon Resource Name (ARN) dari fungsi Lambda Anda.

Setelah memilih fungsi Lambda, Anda dapat membuat peran baru AWS Identity and Access Management (IAM) (yang AWS AppSync menetapkan izin yang sesuai) atau memilih peran yang ada yang memiliki kebijakan sebaris berikut:

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

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "lambda:InvokeFunction"
            ],
            "Resource": "arn:aws:lambda:us-east-1:111122223333:function:LAMBDA_FUNCTION"
        }
    ]
}
```

------

Anda juga harus mengatur hubungan kepercayaan dengan AWS AppSync peran IAM sebagai berikut:

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

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "appsync.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

------

## Buat skema GraphQL
<a name="creating-a-graphql-schema-js"></a>

Sekarang sumber data terhubung ke fungsi Lambda Anda, buat skema GraphQL.

Dari editor skema di AWS AppSync konsol, pastikan skema Anda cocok dengan skema berikut:

```
schema {
    query: Query
    mutation: Mutation
}
type Query {
    getPost(id:ID!): Post
    allPosts: [Post]
}
type Mutation {
    addPost(id: ID!, author: String!, title: String, content: String, url: String): Post!
}
type Post {
    id: ID!
    author: String!
    title: String
    content: String
    url: String
    ups: Int
    downs: Int
    relatedPosts: [Post]
}
```

## Konfigurasikan resolver
<a name="configuring-resolvers-js"></a>

Setelah mendaftarkan sumber data Lambda dan skema GraphQL yang valid, Anda dapat menghubungkan bidang GraphQL ke sumber data Lambda menggunakan resolver.

Anda akan membuat resolver yang menggunakan runtime AWS AppSync JavaScript (`APPSYNC_JS`) dan berinteraksi dengan fungsi Lambda Anda. Untuk mempelajari selengkapnya tentang menulis AWS AppSync resolver dan fungsi dengan JavaScript, lihat [fitur JavaScript runtime untuk resolver](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference-js.html) dan fungsi.

Untuk informasi selengkapnya tentang template pemetaan Lambda, lihat referensi [JavaScript fungsi resolver](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-lambda-js.html) untuk Lambda.

Pada langkah ini, Anda melampirkan resolver ke fungsi Lambda untuk bidang berikut:`getPost(id:ID!): Post`,,, `allPosts: [Post]` dan. `addPost(id: ID!, author: String!, title: String, content: String, url: String): Post!` `Post.relatedPosts: [Post]` Dari editor **Skema** di AWS AppSync konsol, di panel **Resolvers**, pilih **Lampirkan di sebelah bidang**. `getPost(id:ID!): Post` Pilih sumber data Lambda Anda. Selanjutnya, berikan kode berikut:

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

export function request(ctx) {
  const {source, args} = ctx
  return {
    operation: 'Invoke',
    payload: { field: ctx.info.fieldName, arguments: args, source },
  };
}

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

Kode resolver ini meneruskan nama bidang, daftar argumen, dan konteks tentang objek sumber ke fungsi Lambda saat memanggilnya. Pilih **Simpan**.

Anda telah berhasil melampirkan resolver pertama Anda. Ulangi operasi ini untuk bidang yang tersisa. 

## Uji GraphQL API
<a name="testing-your-graphql-api-js"></a>

Sekarang fungsi Lambda Anda terhubung ke resolver GraphQL, Anda dapat menjalankan beberapa mutasi dan kueri menggunakan konsol atau aplikasi klien.

Di sisi kiri AWS AppSync konsol, pilih **Kueri**, lalu tempel kode berikut:

### AddPost Mutasi
<a name="addpost-mutation-js"></a>

```
mutation AddPost {
    addPost(
        id: 6
        author: "Author6"
        title: "Sixth book"
        url: "https://www.amazon.com/"
        content: "This is the book is a tutorial for using GraphQL with AWS AppSync."
    ) {
        id
        author
        title
        content
        url
        ups
        downs
    }
}
```

### Kueri GetPost
<a name="getpost-query-js"></a>

```
query GetPost {
    getPost(id: "2") {
        id
        author
        title
        content
        url
        ups
        downs
    }
}
```

### AllPosts Query
<a name="allposts-query-js"></a>

```
query AllPosts {
    allPosts {
        id
        author
        title
        content
        url
        ups
        downs
        relatedPosts {
            id
            title
        }
    }
}
```

## Mengembalikan kesalahan
<a name="returning-errors-js"></a>

Setiap resolusi bidang yang diberikan dapat mengakibatkan kesalahan. Dengan AWS AppSync, Anda dapat meningkatkan kesalahan dari sumber-sumber berikut:
+ Penangan respons penyelesai
+ Fungsi Lambda

### Dari penangan respons resolver
<a name="from-the-resolver-response-handler-js"></a>

Untuk meningkatkan kesalahan yang disengaja, Anda dapat menggunakan metode `util.error` utilitas. Dibutuhkan argumen an`errorMessage`, an`errorType`, dan `data` nilai opsional. `data`Hal ini berguna untuk mengembalikan data tambahan kembali ke klien ketika terjadi kesalahan. `data`Objek ditambahkan ke `errors` dalam respon akhir GraphQL.

Contoh berikut menunjukkan cara menggunakannya dalam penangan respon `Post.relatedPosts: [Post]` resolver.

```
// the Post.relatedPosts response handler
export function response(ctx) {
    util.error("Failed to fetch relatedPosts", "LambdaFailure", ctx.result)
    return ctx.result;
}
```

Ini menghasilkan respons GraphQL yang mirip dengan yang berikut:

```
{
    "data": {
        "allPosts": [
            {
                "id": "2",
                "title": "Second book",
                "relatedPosts": null
            },
            ...
        ]
    },
    "errors": [
        {
            "path": [
                "allPosts",
                0,
                "relatedPosts"
            ],
            "errorType": "LambdaFailure",
            "locations": [
                {
                    "line": 5,
                    "column": 5
                }
            ],
            "message": "Failed to fetch relatedPosts",
            "data": [
                {
                  "id": "2",
                  "title": "Second book"
                },
                {
                  "id": "1",
                  "title": "First book"
                }
            ]
        }
    ]
}
```

`allPosts[0].relatedPosts`Dimana *nol* karena kesalahan dan`errorMessage`,`errorType`, dan `data` hadir dalam `data.errors[0]` objek.

### Dari fungsi Lambda
<a name="from-the-lam-function-js"></a>

AWS AppSync juga memahami kesalahan yang dilemparkan fungsi Lambda. Model pemrograman Lambda memungkinkan Anda meningkatkan kesalahan yang *ditangani*. Jika fungsi Lambda memunculkan kesalahan, AWS AppSync gagal menyelesaikan bidang saat ini. Hanya pesan kesalahan yang dikembalikan dari Lambda yang disetel dalam respons. Saat ini, Anda tidak dapat meneruskan data asing apa pun kembali ke klien dengan memunculkan kesalahan dari fungsi Lambda. 

**catatan**  
Jika fungsi Lambda Anda memunculkan kesalahan yang *tidak tertangani, AWS AppSync gunakan pesan kesalahan yang disetel* Lambda.

Fungsi Lambda berikut menimbulkan kesalahan:

```
export const handler = async (event) => {
  console.log('Received event {}', JSON.stringify(event, 3))
  throw new Error('I always fail.')
}
```

Kesalahan diterima di handler respons Anda. Anda dapat mengirimnya kembali dalam respons GraphQL dengan menambahkan kesalahan ke respons dengan. `util.appendError` Untuk melakukannya, ubah penangan respons AWS AppSync fungsi Anda menjadi ini:

```
// the lambdaInvoke response handler
export function response(ctx) {
  const { error, result } = ctx;
  if (error) {
    util.appendError(error.message, error.type, result);
  }
  return result;
}
```

Ini mengembalikan respon GraphQL mirip dengan berikut ini:

```
{
  "data": {
    "allPosts": null
  },
  "errors": [
    {
      "path": [
        "allPosts"
      ],
      "data": null,
      "errorType": "Lambda:Unhandled",
      "errorInfo": null,
      "locations": [
        {
          "line": 2,
          "column": 3,
          "sourceName": null
        }
      ],
      "message": "I fail. always"
    }
  ]
}
```

## Kasus penggunaan lanjutan: Batching
<a name="advanced-use-case-batching-js"></a>

Fungsi Lambda dalam contoh ini memiliki `relatedPosts` bidang yang mengembalikan daftar posting terkait untuk posting tertentu. Dalam contoh kueri, pemanggilan `allPosts` bidang dari fungsi Lambda mengembalikan lima posting. Karena kami menetapkan bahwa kami juga ingin menyelesaikan `relatedPosts` untuk setiap posting yang dikembalikan, operasi `relatedPosts` lapangan dipanggil lima kali.

```
query {
    allPosts {   // 1 Lambda invocation - yields 5 Posts
        id
        author
        title
        content
        url
        ups
        downs
        relatedPosts {   // 5 Lambda invocations - each yields 5 posts
            id
            title
        }
    }
}
```

Meskipun ini mungkin tidak terdengar substansif dalam contoh spesifik ini, pengambilan berlebihan yang diperparah ini dapat dengan cepat merusak aplikasi.

Jika Anda mengambil `relatedPosts` lagi pada terkait yang dikembalikan `Posts` dalam kueri yang sama, jumlah pemanggilan akan meningkat secara dramatis.

```
query {
    allPosts {   // 1 Lambda invocation - yields 5 Posts
        id
        author
        title
        content
        url
        ups
        downs
        relatedPosts {   // 5 Lambda invocations - each yield 5 posts = 5 x 5 Posts
            id
            title
            relatedPosts {  // 5 x 5 Lambda invocations - each yield 5 posts = 25 x 5 Posts
                id
                title
                author
            }
        }
    }
}
```

Dalam kueri yang relatif sederhana ini, AWS AppSync akan memanggil fungsi Lambda 1 \$1 5 \$1 25 = 31 kali.

Ini adalah tantangan yang cukup umum dan sering disebut masalah N\$11 (dalam hal ini, N = 5), dan dapat menimbulkan peningkatan latensi dan biaya untuk aplikasi.

Salah satu pendekatan untuk memecahkan masalah ini adalah dengan mengumpulkan permintaan penyelesai bidang yang serupa bersama-sama. Dalam contoh ini, alih-alih memiliki fungsi Lambda menyelesaikan daftar posting terkait untuk satu posting tertentu, itu malah bisa menyelesaikan daftar posting terkait untuk kumpulan posting tertentu.

Untuk mendemonstrasikan ini, mari kita perbarui resolver `relatedPosts` untuk menangani batching.

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

export function request(ctx) {
  const {source, args} = ctx
  return {
    operation: ctx.info.fieldName === 'relatedPosts' ? 'BatchInvoke' : 'Invoke',
    payload: { field: ctx.info.fieldName, arguments: args, source },
  };
}

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

Kode sekarang mengubah operasi dari `Invoke` `BatchInvoke` saat `fieldName` sedang diselesaikan`relatedPosts`. Sekarang, aktifkan batching pada fungsi di bagian **Configure Batching**. Atur ukuran batching maksimum yang disetel ke`5`. Pilih **Simpan**.

Dengan perubahan ini, saat menyelesaikan`relatedPosts`, fungsi Lambda menerima yang berikut ini sebagai input:

```
[
    {
        "field": "relatedPosts",
        "source": {
            "id": 1
        }
    },
    {
        "field": "relatedPosts",
        "source": {
            "id": 2
        }
    },
    ...
]
```

Ketika `BatchInvoke` ditentukan dalam permintaan, fungsi Lambda menerima daftar permintaan dan mengembalikan daftar hasil.

Secara khusus, daftar hasil harus sesuai dengan ukuran dan urutan entri payload permintaan sehingga AWS AppSync dapat cocok dengan hasil yang sesuai.

Dalam contoh batching ini, fungsi Lambda mengembalikan sekumpulan hasil sebagai berikut:

```
[
    [{"id":"2","title":"Second book"}, {"id":"3","title":"Third book"}],   // relatedPosts for id=1
    [{"id":"3","title":"Third book"}]                                     // relatedPosts for id=2
]
```

Anda dapat memperbarui kode Lambda Anda untuk menangani batching untuk: `relatedPosts`

```
export const handler = async (event) => {
  console.log('Received event {}', JSON.stringify(event, 3))
  //throw new Error('I fail. always')

  const posts = {
    1: { id: '1', title: 'First book', author: 'Author1', url: 'https://amazon.com/', content: 'SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1', ups: '100', downs: '10', },
    2: { id: '2', title: 'Second book', author: 'Author2', url: 'https://amazon.com', content: 'SAMPLE TEXT AUTHOR 2 SAMPLE TEXT AUTHOR 2 SAMPLE TEXT', ups: '100', downs: '10', },
    3: { id: '3', title: 'Third book', author: 'Author3', url: null, content: null, ups: null, downs: null },
    4: { id: '4', title: 'Fourth book', author: 'Author4', url: 'https://www.amazon.com/', content: 'SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4', ups: '1000', downs: '0', },
    5: { id: '5', title: 'Fifth book', author: 'Author5', url: 'https://www.amazon.com/', content: 'SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT', ups: '50', downs: '0', },
  }

  const relatedPosts = {
    1: [posts['4']],
    2: [posts['3'], posts['5']],
    3: [posts['2'], posts['1']],
    4: [posts['2'], posts['1']],
    5: [],
  }
  
  if (!event.field && event.length){
    console.log(`Got a BatchInvoke Request. The payload has ${event.length} items to resolve.`);
    return event.map(e => relatedPosts[e.source.id])
  }

  console.log('Got an Invoke Request.')
  let result
  switch (event.field) {
    case 'getPost':
      return posts[event.arguments.id]
    case 'allPosts':
      return Object.values(posts)
    case 'addPost':
      // return the arguments back
      return event.arguments
    case 'addPostErrorWithData':
      result = posts[event.arguments.id]
      // attached additional error information to the post
      result.errorMessage = 'Error with the mutation, data has changed'
      result.errorType = 'MUTATION_ERROR'
      return result
    case 'relatedPosts':
      return relatedPosts[event.source.id]
    default:
      throw new Error('Unknown field, unable to resolve ' + event.field)
  }
}
```

### Mengembalikan kesalahan individu
<a name="returning-individual-errors-js"></a>

Contoh sebelumnya menunjukkan bahwa dimungkinkan untuk mengembalikan satu kesalahan dari fungsi Lambda atau memunculkan kesalahan dari penangan respons Anda. Untuk pemanggilan batch, memunculkan kesalahan dari fungsi Lambda menandai seluruh batch sebagai gagal. Ini mungkin dapat diterima untuk skenario tertentu di mana terjadi kesalahan yang tidak dapat dipulihkan, seperti koneksi yang gagal ke penyimpanan data. Namun, dalam kasus di mana beberapa item dalam batch berhasil dan yang lainnya gagal, dimungkinkan untuk mengembalikan kesalahan dan data yang valid. Karena AWS AppSync memerlukan respons batch untuk mencantumkan elemen yang cocok dengan ukuran asli batch, Anda harus menentukan struktur data yang dapat membedakan data yang valid dari kesalahan.

*Misalnya, jika fungsi Lambda diharapkan mengembalikan kumpulan posting terkait, Anda dapat memilih untuk mengembalikan daftar `Response` objek di mana setiap objek memiliki *data opsional, ErrorMessage, dan bidang* *ErrorType*.* Jika bidang *ErrorMessage* ada, itu berarti terjadi kesalahan.

Kode berikut menunjukkan bagaimana Anda dapat memperbarui fungsi Lambda:

```
export const handler = async (event) => {
console.log('Received event {}', JSON.stringify(event, 3))
  // throw new Error('I fail. always')
const posts = {
1: { id: '1', title: 'First book', author: 'Author1', url: 'https://amazon.com/', content: 'SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1', ups: '100', downs: '10', },
    2: { id: '2', title: 'Second book', author: 'Author2', url: 'https://amazon.com', content: 'SAMPLE TEXT AUTHOR 2 SAMPLE TEXT AUTHOR 2 SAMPLE TEXT', ups: '100', downs: '10', },
    3: { id: '3', title: 'Third book', author: 'Author3', url: null, content: null, ups: null, downs: null },
    4: { id: '4', title: 'Fourth book', author: 'Author4', url: 'https://www.amazon.com/', content: 'SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4', ups: '1000', downs: '0', },
    5: { id: '5', title: 'Fifth book', author: 'Author5', url: 'https://www.amazon.com/', content: 'SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT', ups: '50', downs: '0', },
  }

  const relatedPosts = {
1: [posts['4']],
    2: [posts['3'], posts['5']],
    3: [posts['2'], posts['1']],
    4: [posts['2'], posts['1']],
    5: [],
  }
  
  if (!event.field && event.length){
console.log(`Got a BatchInvoke Request. The payload has ${event.length} items to resolve.`);
    return event.map(e => {
// return an error for post 2
if (e.source.id === '2') {
return { 'data': null, 'errorMessage': 'Error Happened', 'errorType': 'ERROR' }
      }
      return {data: relatedPosts[e.source.id]}
      })
  }

  console.log('Got an Invoke Request.')
  let result
  switch (event.field) {
case 'getPost':
      return posts[event.arguments.id]
    case 'allPosts':
      return Object.values(posts)
    case 'addPost':
      // return the arguments back
return event.arguments
    case 'addPostErrorWithData':
      result = posts[event.arguments.id]
      // attached additional error information to the post
      result.errorMessage = 'Error with the mutation, data has changed'
      result.errorType = 'MUTATION_ERROR'
return result
    case 'relatedPosts':
      return relatedPosts[event.source.id]
    default:
      throw new Error('Unknown field, unable to resolve ' + event.field)
  }
}
```

Perbarui kode `relatedPosts` resolver:

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

export function request(ctx) {
  const {source, args} = ctx
  return {
    operation: ctx.info.fieldName === 'relatedPosts' ? 'BatchInvoke' : 'Invoke',
    payload: { field: ctx.info.fieldName, arguments: args, source },
  };
}

export function response(ctx) {
  const { error, result } = ctx;
  if (error) {
    util.appendError(error.message, error.type, result);
  } else if (result.errorMessage) {
    util.appendError(result.errorMessage, result.errorType, result.data)
  } else if (ctx.info.fieldName === 'relatedPosts') {
      return result.data
  } else {
      return result
  }
}
```

Penangan respons sekarang memeriksa kesalahan yang dikembalikan oleh fungsi Lambda `Invoke` pada operasi, memeriksa kesalahan yang dikembalikan untuk item individual `BatchInvoke` untuk operasi, dan akhirnya memeriksa. `fieldName` Untuk`relatedPosts`, fungsi kembali`result.data`. Untuk semua bidang lainnya, fungsi hanya kembali`result`. Misalnya, lihat kueri di bawah ini:

```
query AllPosts {
  allPosts {
    id
    title
    content
    url
    ups
    downs
    relatedPosts {
      id
    }
    author
  }
}
```

Query ini mengembalikan respon GraphQL mirip dengan berikut ini:

```
{
  "data": {
    "allPosts": [
      {
        "id": "1",
        "relatedPosts": [
          {
            "id": "4"
          }
        ]
      },
      {
        "id": "2",
        "relatedPosts": null
      },
      {
        "id": "3",
        "relatedPosts": [
          {
            "id": "2"
          },
          {
            "id": "1"
          }
        ]
      },
      {
        "id": "4",
        "relatedPosts": [
          {
            "id": "2"
          },
          {
            "id": "1"
          }
        ]
      },
      {
        "id": "5",
        "relatedPosts": []
      }
    ]
  },
  "errors": [
    {
      "path": [
        "allPosts",
        1,
        "relatedPosts"
      ],
      "data": null,
      "errorType": "ERROR",
      "errorInfo": null,
      "locations": [
        {
          "line": 4,
          "column": 5,
          "sourceName": null
        }
      ],
      "message": "Error Happened"
    }
  ]
}
```

### Mengkonfigurasi ukuran batching maksimum
<a name="configure-max-batch-size-js"></a>

Untuk mengonfigurasi ukuran batching maksimum pada resolver, gunakan perintah berikut di (): AWS Command Line Interface AWS CLI

```
$ aws appsync create-resolver --api-id <api-id> --type-name Query --field-name relatedPosts \
 --code "<code-goes-here>" \
 --runtime name=APPSYNC_JS,runtimeVersion=1.0.0 \
 --data-source-name "<lambda-datasource>" \ 
 --max-batch-size X
```

**catatan**  
Saat menyediakan template pemetaan permintaan, Anda harus menggunakan `BatchInvoke` operasi untuk menggunakan batching.

# Menggunakan resolver lokal di AWS AppSync
<a name="tutorial-local-resolvers-js"></a>

AWS AppSync memungkinkan Anda menggunakan sumber data yang didukung (AWS Lambda, Amazon DynamoDB, atau OpenSearch Amazon Service) untuk melakukan berbagai operasi. Namun, dalam skenario tertentu, panggilan ke sumber data yang didukung mungkin tidak diperlukan.

Di sinilah resolver lokal berguna. Alih-alih memanggil sumber data jarak jauh, resolver lokal hanya akan **meneruskan** hasil penangan permintaan ke penangan respons. Resolusi lapangan tidak akan pergi AWS AppSync.

Resolver lokal berguna dalam banyak situasi. Kasus penggunaan yang paling populer adalah mempublikasikan notifikasi tanpa memicu panggilan sumber data. Untuk mendemonstrasikan kasus penggunaan ini, mari buat aplikasi pub/sub tempat pengguna dapat mempublikasikan dan berlangganan pesan. Contoh ini memanfaatkan *Langganan*, jadi jika Anda tidak terbiasa dengan *Langganan*, Anda dapat mengikuti tutorial Data [Waktu Nyata](aws-appsync-real-time-data.md).

## Membuat aplikasi pub/sub
<a name="create-the-pub-sub-application-js"></a>

Pertama, buat API GraphQL kosong dengan memilih opsi **Desain dari** awal dan mengonfigurasi detail opsional saat membuat GraphQL API Anda.

Dalam aplikasi pub/sub kami, klien dapat berlangganan dan mempublikasikan pesan. Setiap pesan yang diterbitkan mencakup nama dan data. Tambahkan ini ke skema:

```
type Channel {
	name: String!
	data: AWSJSON!
}

type Mutation {
	publish(name: String!, data: AWSJSON!): Channel
}

type Query {
	getChannel: Channel
}

type Subscription {
	subscribe(name: String!): Channel
		@aws_subscribe(mutations: ["publish"])
}
```

Selanjutnya, mari kita lampirkan resolver ke bidang. `Mutation.publish` **Di panel **Resolvers** di sebelah panel **Schema**, temukan `Mutation` jenisnya, lalu `publish(...): Channel` bidangnya, lalu klik Lampirkan.**

Buat sumber data *None* dan beri nama *PageDataSource*. Lampirkan ke resolver Anda.

Tambahkan implementasi resolver Anda menggunakan cuplikan berikut:

```
export function request(ctx) {
  return { payload: ctx.args };
}

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

Pastikan Anda membuat resolver dan menyimpan perubahan yang Anda buat.

## Kirim dan berlangganan pesan
<a name="send-and-subscribe-to-messages-js"></a>

Agar klien dapat menerima pesan, mereka harus terlebih dahulu berlangganan kotak masuk.

Di panel **Kueri**, jalankan langganan: `SubscribeToData`

```
subscription SubscribeToData {
    subscribe(name:"channel") {
        name
        data
    }
}
```

 Pelanggan akan menerima pesan setiap kali `publish` mutasi dipanggil tetapi hanya ketika pesan dikirim ke langganan. `channel` Mari kita coba ini di panel **Queries**. Saat langganan Anda masih berjalan di konsol, buka konsol lain dan jalankan permintaan berikut di panel **Kueri**:

**catatan**  
Kami menggunakan string JSON yang valid dalam contoh ini.

```
mutation PublishData {
    publish(data: "{\"msg\": \"hello world!\"}", name: "channel") {
        data
        name
    }
}
```

Hasilnya akan terlihat seperti ini:

```
{
  "data": {
    "publish": {
      "data": "{\"msg\":\"hello world!\"}",
      "name": "channel"
    }
  }
}
```

Kami baru saja mendemonstrasikan penggunaan resolver lokal, dengan menerbitkan pesan dan menerimanya tanpa meninggalkan layanan. AWS AppSync 

# Menggabungkan resolver GraphQL di AWS AppSync
<a name="tutorial-combining-graphql-resolvers-js"></a>

Resolver dan bidang dalam skema GraphQL memiliki hubungan 1:1 dengan tingkat fleksibilitas yang besar. Karena sumber data dikonfigurasi pada resolver secara independen dari skema, Anda memiliki kemampuan untuk menyelesaikan atau memanipulasi tipe GraphQL Anda melalui sumber data yang berbeda, memungkinkan Anda untuk mencampur dan mencocokkan skema untuk memenuhi kebutuhan Anda.

Skenario berikut menunjukkan cara mencampur dan mencocokkan sumber data dalam skema Anda. Sebelum memulai, Anda harus terbiasa dengan mengonfigurasi sumber data dan resolver untuk, Amazon AWS Lambda DynamoDB, dan Amazon Service. OpenSearch 

## Contoh skema
<a name="example-schema-js"></a>

Skema berikut memiliki jenis `Post` dengan tiga `Query` dan `Mutation` operasi masing-masing:

```
type Post {
    id: ID!
    author: String!
    title: String
    content: String
    url: String
    ups: Int
    downs: Int
    version: Int!
}

type Query {
    allPost: [Post]
    getPost(id: ID!): Post
    searchPosts: [Post]
}

type Mutation {
    addPost(
        id: ID!,
        author: String!,
        title: String,
        content: String,
        url: String
    ): Post
    updatePost(
        id: ID!,
        author: String!,
        title: String,
        content: String,
        url: String,
        ups: Int!,
        downs: Int!,
        expectedVersion: Int!
    ): Post
    deletePost(id: ID!): Post
}
```

Dalam contoh ini, Anda akan memiliki total enam resolver dengan masing-masing membutuhkan sumber data. Salah satu cara untuk mengatasi masalah ini adalah dengan menghubungkannya ke satu tabel Amazon DynamoDB, yang `Posts` disebut, di mana `AllPost` bidang menjalankan pemindaian dan `searchPosts` bidang menjalankan kueri ([JavaScriptlihat referensi fungsi resolver](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html) untuk DynamoDB). Namun, Anda tidak terbatas pada Amazon DynamoDB; sumber data yang berbeda seperti Lambda OpenSearch atau Layanan ada untuk memenuhi persyaratan bisnis Anda. 

## Mengubah data melalui resolver
<a name="alter-data-through-resolvers-js"></a>

Anda mungkin perlu mengembalikan hasil dari database pihak ketiga yang tidak didukung secara langsung oleh sumber AWS AppSync data. Anda mungkin juga harus melakukan modifikasi kompleks pada data sebelum dikembalikan ke klien API. Hal ini dapat disebabkan oleh pemformatan tipe data yang tidak tepat, seperti perbedaan stempel waktu pada klien, atau penanganan masalah kompatibilitas mundur. Dalam hal ini, menghubungkan AWS Lambda fungsi sebagai sumber data ke AWS AppSync API Anda adalah solusi yang tepat. Untuk tujuan ilustrasi, dalam contoh berikut, AWS Lambda fungsi memanipulasi data yang diambil dari penyimpanan data pihak ketiga:

```
export const handler = (event, context, callback) => {
    // fetch data
    const result = fetcher()

    // apply complex business logic
    const data = transform(result)	

    // return to AppSync
    return data
};
```

Ini adalah fungsi Lambda yang benar-benar valid dan dapat dilampirkan ke `AllPost` bidang dalam skema GraphQL sehingga setiap kueri yang mengembalikan semua hasil mendapat angka acak untuk naik/turun.

## DynamoDB dan Layanan OpenSearch
<a name="ddb-and-es-js"></a>

Untuk beberapa aplikasi, Anda mungkin melakukan mutasi atau kueri pencarian sederhana terhadap DynamoDB dan memiliki proses latar belakang mentransfer dokumen ke Layanan. OpenSearch Anda cukup melampirkan `searchPosts` resolver ke sumber data OpenSearch Layanan dan mengembalikan hasil pencarian (dari data yang berasal dari DynamoDB) menggunakan kueri GraphQL. Ini bisa sangat kuat saat menambahkan operasi pencarian lanjutan ke aplikasi Anda seperti kata kunci, kecocokan kata kabur, atau bahkan pencarian geospasial. Mentransfer data dari DynamoDB dapat dilakukan melalui proses ETL, atau sebagai alternatif, Anda dapat melakukan streaming dari DynamoDB menggunakan Lambda.

Untuk memulai dengan sumber data khusus ini, lihat tutorial [DynamoDB](https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-dynamodb-resolvers-js.html) [dan](https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-lambda-resolvers-js.html) Lambda kami.

Misalnya, menggunakan skema dari tutorial kami sebelumnya, mutasi berikut menambahkan item ke DynamoDB:

```
mutation addPost {
  addPost(
    id: 123
    author: "Nadia"
    title: "Our first post!"
    content: "This is our first post."
    url: "https://aws.amazon.com/appsync/"
  ) {
    id
    author
    title
    content
    url
    ups
    downs
    version
  }
}
```

Ini menulis data ke DynamoDB, yang kemudian mengalirkan data melalui Lambda ke OpenSearch Amazon Service, yang kemudian Anda gunakan untuk mencari posting berdasarkan bidang yang berbeda. Misalnya, karena data ada di OpenSearch Layanan Amazon, Anda dapat mencari bidang penulis atau konten dengan teks bentuk bebas, bahkan dengan spasi, sebagai berikut:

```
query searchName{
    searchAuthor(name:"   Nadia   "){
        id
        title
        content
    }
}

---------- or ----------

query searchContent{
    searchContent(text:"test"){
        id
        title
        content
    }
}
```

Karena data ditulis langsung ke DynamoDB, Anda masih dapat melakukan operasi pencarian daftar atau item yang efisien terhadap tabel dengan dan kueri. `allPost{...}` `getPost{...}` Tumpukan ini menggunakan kode contoh berikut untuk aliran DynamoDB:

**catatan**  
Kode Python ini adalah contoh dan tidak dimaksudkan untuk digunakan dalam kode produksi.

```
import boto3
import requests
from requests_aws4auth import AWS4Auth

region = '' # e.g. us-east-1
service = 'es'
credentials = boto3.Session().get_credentials()
awsauth = AWS4Auth(credentials.access_key, credentials.secret_key, region, service, session_token=credentials.token)

host = '' # the OpenSearch Service domain, e.g. https://search-mydomain.us-west-1.es.amazonaws.com
index = 'lambda-index'
datatype = '_doc'
url = host + '/' + index + '/' + datatype + '/'

headers = { "Content-Type": "application/json" }

def handler(event, context):
    count = 0
    for record in event['Records']:
        # Get the primary key for use as the OpenSearch ID
        id = record['dynamodb']['Keys']['id']['S']

        if record['eventName'] == 'REMOVE':
            r = requests.delete(url + id, auth=awsauth)
        else:
            document = record['dynamodb']['NewImage']
            r = requests.put(url + id, auth=awsauth, json=document, headers=headers)
        count += 1
    return str(count) + ' records processed.'
```

Anda kemudian dapat menggunakan aliran DynamoDB untuk melampirkan ini ke tabel DynamoDB dengan kunci `id` utama, dan setiap perubahan pada sumber DynamoDB akan mengalir ke domain Layanan Anda. OpenSearch Untuk informasi selengkapnya tentang mengonfigurasi ini, lihat dokumentasi [DynamoDB Streams](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.Lambda.html).

# Menggunakan OpenSearch resolver Layanan Amazon di AWS AppSync
<a name="tutorial-elasticsearch-resolvers-js"></a>

AWS AppSync mendukung penggunaan OpenSearch Layanan Amazon dari domain yang telah Anda sediakan di AWS akun Anda sendiri, asalkan tidak ada di dalam VPC. Setelah domain Anda disediakan, Anda dapat menghubungkannya menggunakan sumber data, di mana Anda dapat mengonfigurasi resolver dalam skema untuk melakukan operasi GraphQL seperti kueri, mutasi, dan langganan. Tutorial ini akan membawa Anda melalui beberapa contoh umum.

Untuk informasi lebih lanjut, lihat [referensi fungsi JavaScript resolver](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-elasticsearch-js.html) kami untuk. OpenSearch

## Buat domain OpenSearch Layanan baru
<a name="create-a-new-es-domain-js"></a>

Untuk memulai tutorial ini, Anda memerlukan domain OpenSearch Layanan yang ada. Jika Anda tidak memilikinya, Anda dapat menggunakan sampel berikut. Perhatikan bahwa diperlukan waktu hingga 15 menit untuk membuat domain OpenSearch Layanan sebelum Anda dapat melanjutkan untuk mengintegrasikannya dengan sumber AWS AppSync data.

```
aws cloudformation create-stack --stack-name AppSyncOpenSearch \
--template-url https://s3.us-west-2.amazonaws.com/awsappsync/resources/elasticsearch/ESResolverCFTemplate.yaml \
--parameters ParameterKey=OSDomainName,ParameterValue=ddtestdomain ParameterKey=Tier,ParameterValue=development \
--capabilities CAPABILITY_NAMED_IAM
```

Anda dapat meluncurkan AWS CloudFormation tumpukan berikut di Wilayah AS-Barat-2 (Oregon) di akun Anda: AWS 

 [https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/elasticsearch/ESResolverCFTemplate.yaml](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/elasticsearch/ESResolverCFTemplate.yaml)

## Mengkonfigurasi sumber data untuk OpenSearch Layanan
<a name="configure-data-source-for-es-js"></a>

**Setelah domain OpenSearch Layanan dibuat, navigasikan ke AWS AppSync GraphQL API Anda dan pilih tab Sumber Data.** Pilih **Buat sumber data** dan masukkan nama ramah untuk sumber data seperti “*oss*”. Kemudian, pilih ** OpenSearch domain Amazon** untuk **tipe sumber data**, pilih Wilayah yang sesuai, dan Anda akan melihat domain OpenSearch Layanan Anda terdaftar. Setelah memilihnya, Anda dapat membuat peran baru, dan AWS AppSync akan menetapkan izin yang sesuai peran, atau Anda dapat memilih peran yang ada, yang memiliki kebijakan sebaris berikut:

Anda juga perlu mengatur hubungan kepercayaan AWS AppSync untuk peran itu:

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

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "appsync.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

------

Selain itu, domain OpenSearch Layanan memiliki **Kebijakan Akses** sendiri yang dapat Anda modifikasi melalui konsol OpenSearch Layanan Amazon. Anda harus menambahkan kebijakan yang serupa dengan kebijakan di bawah ini dengan tindakan dan sumber daya yang sesuai untuk domain OpenSearch Layanan. Perhatikan bahwa **Principal** akan menjadi peran sumber AWS AppSync data, yang dapat ditemukan di konsol IAM jika Anda membiarkan konsol tersebut membuatnya.

## Menghubungkan resolver
<a name="connecting-a-resolver-js"></a>

Sekarang sumber data terhubung ke domain OpenSearch Layanan Anda, Anda dapat menghubungkannya ke skema GraphQL Anda dengan resolver seperti yang ditunjukkan pada contoh berikut:

```
 type Query {
   getPost(id: ID!): Post
   allPosts: [Post]
 }

 type Mutation {
   addPost(id: ID!, author: String, title: String, url: String, ups: Int, downs: Int, content: String): AWSJSON
 }

type Post {
  id: ID!
  author: String
  title: String
  url: String
  ups: Int
  downs: Int
  content: String
}
```

Perhatikan bahwa ada `Post` tipe yang ditentukan pengguna dengan bidang. `id` Dalam contoh berikut, kami berasumsi ada proses (yang dapat diotomatisasi) untuk memasukkan jenis ini ke dalam domain OpenSearch Layanan Anda, yang akan memetakan ke root jalur `/post/_doc` di mana `post` indeks. Dari jalur root ini, Anda dapat melakukan pencarian dokumen individual, pencarian wildcard dengan`/id/post*`, atau pencarian multi-dokumen dengan jalur. `/post/_search` Misalnya, jika Anda memiliki jenis lain yang dipanggil`User`, Anda dapat mengindeks dokumen di bawah indeks baru yang disebut`user`, lalu melakukan pencarian dengan **jalur**. `/user/_search` 

Dari editor **Skema** di AWS AppSync konsol, ubah `Posts` skema sebelumnya untuk menyertakan kueri: `searchPosts`

```
type Query {
  getPost(id: ID!): Post
  allPosts: [Post]
  searchPosts: [Post]
}
```

Simpan skema. **Di panel **Resolvers**, temukan `searchPosts` dan pilih Lampirkan.** Pilih sumber data OpenSearch Layanan Anda dan simpan resolver. Perbarui kode resolver Anda menggunakan cuplikan di bawah ini:

```
import { util } from '@aws-appsync/utils'

/**
 * Searches for documents by using an input term
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} the request
 */
export function request(ctx) {
	return {
		operation: 'GET',
		path: `/post/_search`,
		params: { body: { from: 0, size: 50 } },
	}
}

/**
 * Returns the fetched items
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} the result
 */
export function response(ctx) {
	if (ctx.error) {
		util.error(ctx.error.message, ctx.error.type)
	}
	return ctx.result.hits.hits.map((hit) => hit._source)
}
```

Ini mengasumsikan bahwa skema sebelumnya memiliki dokumen yang telah diindeks di Layanan di bawah bidang. OpenSearch `post` Jika Anda menyusun data Anda secara berbeda, Anda harus memperbaruinya.

## Memodifikasi pencarian Anda
<a name="modifying-your-searches-js"></a>

Handler permintaan resolver sebelumnya melakukan kueri sederhana untuk semua catatan. Misalkan Anda ingin mencari oleh penulis tertentu. Selanjutnya, misalkan Anda ingin penulis itu menjadi argumen yang didefinisikan dalam kueri GraphQL Anda. Di editor **Skema** AWS AppSync konsol, tambahkan `allPostsByAuthor` kueri:

```
type Query {
  getPost(id: ID!): Post
  allPosts: [Post]
  allPostsByAuthor(author: String!): [Post]
  searchPosts: [Post]
}
```

**Di panel **Resolvers**, temukan `allPostsByAuthor` dan pilih Lampirkan.** Pilih sumber data OpenSearch Layanan dan gunakan kode berikut:

```
import { util } from '@aws-appsync/utils'

/**
 * Searches for documents by `author`
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} the request
 */
export function request(ctx) {
	return {
		operation: 'GET',
		path: '/post/_search',
		params: {
			body: {
				from: 0,
				size: 50,
				query: { match: { author: ctx.args.author } },
			},
		},
	}
}

/**
 * Returns the fetched items
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} the result
 */
export function response(ctx) {
	if (ctx.error) {
		util.error(ctx.error.message, ctx.error.type)
	}
	return ctx.result.hits.hits.map((hit) => hit._source)
}
```

Perhatikan bahwa `body` diisi dengan kueri istilah untuk `author` bidang, yang diteruskan dari klien sebagai argumen. Secara opsional, Anda dapat menggunakan informasi yang telah diisi sebelumnya, seperti teks standar.

## Menambahkan data ke OpenSearch Layanan
<a name="adding-data-to-es-js"></a>

Anda mungkin ingin menambahkan data ke domain OpenSearch Layanan Anda sebagai hasil dari mutasi GraphQL. Ini adalah mekanisme yang kuat untuk pencarian dan tujuan lainnya. Karena Anda dapat menggunakan langganan GraphQL [untuk membuat data Anda](aws-appsync-real-time-data.md) real-time, ini dapat berfungsi sebagai mekanisme untuk memberi tahu klien tentang pembaruan data di domain Layanan Anda. OpenSearch 

Kembali ke halaman **Skema** di AWS AppSync konsol dan pilih **Lampirkan** untuk `addPost()` mutasi. Pilih sumber data OpenSearch Layanan lagi dan gunakan kode berikut:

```
import { util } from '@aws-appsync/utils'

/**
 * Searches for documents by `author`
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} the request
 */
export function request(ctx) {
	return {
		operation: 'PUT',
		path: `/post/_doc/${ctx.args.id}`,
		params: { body: ctx.args },
	}
}

/**
 * Returns the inserted post
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} the result
 */
export function response(ctx) {
	if (ctx.error) {
		util.error(ctx.error.message, ctx.error.type)
	}
	return ctx.result
}
```

Seperti sebelumnya, ini adalah contoh bagaimana data Anda mungkin terstruktur. Jika Anda memiliki nama bidang atau indeks yang berbeda, Anda perlu memperbarui `path` dan`body`. Contoh ini juga menunjukkan cara menggunakan`context.arguments`, yang juga dapat ditulis sebagai`ctx.args`, dalam penangan permintaan Anda.

## Mengambil satu dokumen
<a name="retrieving-a-single-document-js"></a>

**Terakhir, jika Anda ingin menggunakan `getPost(id:ID)` kueri dalam skema Anda untuk mengembalikan dokumen individual, temukan kueri ini di editor **Skema** AWS AppSync konsol dan pilih Lampirkan.** Pilih sumber data OpenSearch Layanan lagi dan gunakan kode berikut:

```
import { util } from '@aws-appsync/utils'

/**
 * Searches for documents by `author`
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} the request
 */
export function request(ctx) {
	return {
		operation: 'GET',
		path: `/post/_doc/${ctx.args.id}`,
	}
}

/**
 * Returns the post
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} the result
 */
export function response(ctx) {
	if (ctx.error) {
		util.error(ctx.error.message, ctx.error.type)
	}
	return ctx.result._source
}
```

## Lakukan kueri dan mutasi
<a name="tutorial-elasticsearch-resolvers-perform-queries-mutations-js"></a>

Anda sekarang harus dapat melakukan operasi GraphQL terhadap OpenSearch domain Layanan Anda. Arahkan ke tab **Kueri** AWS AppSync konsol dan tambahkan catatan baru:

```
mutation AddPost {
    addPost (
        id:"12345"
        author: "Fred"
        title: "My first book"
        content: "This will be fun to write!"
        url: "publisher website",
        ups: 100,
        downs:20 
       )
}
```

Anda akan melihat hasil mutasi di sebelah kanan. Demikian pula, Anda sekarang dapat menjalankan `searchPosts` kueri terhadap domain OpenSearch Layanan Anda:

```
query search {
    searchPosts {
        id
        title
        author
        content
    }
}
```

## Praktik terbaik
<a name="best-practices-js"></a>
+ OpenSearch Layanan harus untuk kueri data, bukan sebagai basis data utama Anda. [Anda mungkin ingin menggunakan OpenSearch Layanan bersama dengan Amazon DynamoDB sebagaimana diuraikan dalam Menggabungkan GraphQL Resolvers.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-combining-graphql-resolvers-js.html)
+ Hanya berikan akses ke domain Anda dengan mengizinkan peran AWS AppSync layanan mengakses klaster.
+ Anda dapat memulai dari yang kecil dalam pengembangan, dengan cluster berbiaya terendah, dan kemudian pindah ke cluster yang lebih besar dengan ketersediaan tinggi (HA) saat Anda pindah ke produksi.

# Melakukan transaksi DynamoDB di AWS AppSync
<a name="tutorial-dynamodb-transact-js"></a>

AWS AppSync mendukung penggunaan operasi transaksi Amazon DynamoDB di satu atau beberapa tabel dalam satu Wilayah. Operasi yang didukung adalah `TransactGetItems` dan`TransactWriteItems`. Dengan menggunakan fitur-fitur ini di AWS AppSync, Anda dapat melakukan tugas-tugas seperti:
+ Melewati daftar kunci dalam satu kueri dan mengembalikan hasil dari tabel
+ Membaca catatan dari satu atau beberapa tabel dalam satu kueri
+ Menulis catatan dalam transaksi ke satu atau lebih tabel dengan all-or-nothing cara
+ Menjalankan transaksi ketika beberapa kondisi terpenuhi

## Izin
<a name="permissions-js"></a>

Seperti resolver lainnya, Anda perlu membuat sumber data AWS AppSync dan membuat peran atau menggunakan yang sudah ada. Karena operasi transaksi memerlukan izin yang berbeda pada tabel DynamoDB, Anda perlu memberikan izin peran yang dikonfigurasi untuk tindakan baca atau tulis:

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

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Action": [
                "dynamodb:DeleteItem",
                "dynamodb:GetItem",
                "dynamodb:PutItem",
                "dynamodb:Query",
                "dynamodb:Scan",
                "dynamodb:UpdateItem"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:dynamodb:us-east-1:111122223333:table/TABLENAME",
                "arn:aws:dynamodb:us-east-1:111122223333:table/TABLENAME/*"
            ]
        }
    ]
}
```

------

**catatan**  
Peran terkait dengan sumber data di AWS AppSync, dan resolver pada bidang dipanggil terhadap sumber data. Sumber data yang dikonfigurasi untuk mengambil terhadap DynamoDB hanya memiliki satu tabel yang ditentukan untuk menjaga konfigurasi tetap sederhana. Oleh karena itu, saat melakukan operasi transaksi terhadap beberapa tabel dalam satu resolver, yang merupakan tugas yang lebih maju, Anda harus memberikan peran pada akses sumber data tersebut ke tabel mana pun yang akan berinteraksi dengan resolver. Ini akan dilakukan di bidang **Sumber Daya** dalam kebijakan IAM di atas. Konfigurasi panggilan transaksi terhadap tabel dilakukan dalam kode resolver, yang kami jelaskan di bawah ini.

## Sumber data
<a name="data-source-js"></a>

Demi kesederhanaan, kita akan menggunakan sumber data yang sama untuk semua resolver yang digunakan dalam tutorial ini. 

**Kita akan memiliki dua tabel yang disebut **SavingAccounts** **dan** CheckingAccounts, keduanya `accountNumber` dengan sebagai kunci partisi, dan tabel TransactionHistory dengan as partition key.** `transactionId` Anda dapat menggunakan perintah CLI di bawah ini untuk membuat tabel Anda. Pastikan untuk mengganti `region` dengan wilayah Anda.

**Dengan CLI**

```
aws dynamodb create-table --table-name savingAccounts \
  --attribute-definitions AttributeName=accountNumber,AttributeType=S \
  --key-schema AttributeName=accountNumber,KeyType=HASH \
  --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
  --table-class STANDARD --region region

aws dynamodb create-table --table-name checkingAccounts \
  --attribute-definitions AttributeName=accountNumber,AttributeType=S \
  --key-schema AttributeName=accountNumber,KeyType=HASH \
  --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
  --table-class STANDARD --region region

aws dynamodb create-table --table-name transactionHistory \
  --attribute-definitions AttributeName=transactionId,AttributeType=S \
  --key-schema AttributeName=transactionId,KeyType=HASH \
  --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
  --table-class STANDARD --region region
```

Di AWS AppSync konsol, di **Sumber data, buat sumber data** DynamoDB baru dan beri nama. **TransactTutorial** Pilih **SavingAccounts** sebagai tabel (meskipun tabel tertentu tidak masalah saat menggunakan transaksi). Pilih untuk membuat peran baru dan sumber data. Anda dapat meninjau konfigurasi sumber data untuk melihat nama peran yang dihasilkan. Di konsol IAM, Anda dapat menambahkan kebijakan in-line yang memungkinkan sumber data berinteraksi dengan semua tabel.

Ganti `region` dan `accountID` dengan Region dan ID akun Anda:

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

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Action": [
                "dynamodb:DeleteItem",
                "dynamodb:GetItem",
                "dynamodb:PutItem",
                "dynamodb:Query",
                "dynamodb:Scan",
                "dynamodb:UpdateItem"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:dynamodb:us-east-1:111122223333:table/savingAccounts",
                "arn:aws:dynamodb:us-east-1:111122223333:table/savingAccounts/*",
                "arn:aws:dynamodb:us-east-1:111122223333:table/checkingAccounts",
                "arn:aws:dynamodb:us-east-1:111122223333:table/checkingAccounts/*",
                "arn:aws:dynamodb:us-east-1:111122223333:table/transactionHistory",
                "arn:aws:dynamodb:us-east-1:111122223333:table/transactionHistory/*"
            ]
        }
    ]
}
```

------

## Transaksi
<a name="transactions-js"></a>

Untuk contoh ini, konteksnya adalah transaksi perbankan klasik, di mana kita akan menggunakan `TransactWriteItems` untuk:
+ Transfer uang dari rekening tabungan ke rekening giro
+ Menghasilkan catatan transaksi baru untuk setiap transaksi

Dan kemudian kita akan menggunakan `TransactGetItems` untuk mengambil rincian dari rekening tabungan dan rekening giro.

**Awas**  
`TransactWriteItems`tidak didukung saat digunakan dengan deteksi dan resolusi konflik. Pengaturan ini harus dinonaktifkan untuk mencegah kemungkinan kesalahan.

Kami mendefinisikan skema GraphQL kami sebagai berikut:

```
type SavingAccount {
    accountNumber: String!
    username: String
    balance: Float
}

type CheckingAccount {
    accountNumber: String!
    username: String
    balance: Float
}

type TransactionHistory {
    transactionId: ID!
    from: String
    to: String
    amount: Float
}

type TransactionResult {
    savingAccounts: [SavingAccount]
    checkingAccounts: [CheckingAccount]
    transactionHistory: [TransactionHistory]
}

input SavingAccountInput {
    accountNumber: String!
    username: String
    balance: Float
}

input CheckingAccountInput {
    accountNumber: String!
    username: String
    balance: Float
}

input TransactionInput {
    savingAccountNumber: String!
    checkingAccountNumber: String!
    amount: Float!
}

type Query {
    getAccounts(savingAccountNumbers: [String], checkingAccountNumbers: [String]): TransactionResult
}

type Mutation {
    populateAccounts(savingAccounts: [SavingAccountInput], checkingAccounts: [CheckingAccountInput]): TransactionResult
    transferMoney(transactions: [TransactionInput]): TransactionResult
}
```

### TransactWriteItems - Mengisi akun
<a name="transactwriteitems-populate-accounts-js"></a>

Untuk mentransfer uang antar akun, kita perlu mengisi tabel dengan detailnya. Kami akan menggunakan `Mutation.populateAccounts` operasi GraphQL untuk melakukannya.

Di bagian Skema, klik **Lampirkan** di sebelah `Mutation.populateAccounts` operasi. Pilih sumber `TransactTutorial` data dan pilih **Buat**.

Sekarang gunakan kode berikut:

```
import { util } from '@aws-appsync/utils'

export function request(ctx) {
	const { savingAccounts, checkingAccounts } = ctx.args

	const savings = savingAccounts.map(({ accountNumber, ...rest }) => {
		return {
			table: 'savingAccounts',
			operation: 'PutItem',
			key: util.dynamodb.toMapValues({ accountNumber }),
			attributeValues: util.dynamodb.toMapValues(rest),
		}
	})

	const checkings = checkingAccounts.map(({ accountNumber, ...rest }) => {
		return {
			table: 'checkingAccounts',
			operation: 'PutItem',
			key: util.dynamodb.toMapValues({ accountNumber }),
			attributeValues: util.dynamodb.toMapValues(rest),
		}
	})
	return {
		version: '2018-05-29',
		operation: 'TransactWriteItems',
		transactItems: [...savings, ...checkings],
	}
}

export function response(ctx) {
	if (ctx.error) {
		util.error(ctx.error.message, ctx.error.type, null, ctx.result.cancellationReasons)
	}
	const { savingAccounts: sInput, checkingAccounts: cInput } = ctx.args
	const keys = ctx.result.keys
	const savingAccounts = sInput.map((_, i) => keys[i])
	const sLength = sInput.length
	const checkingAccounts = cInput.map((_, i) => keys[sLength + i])
	return { savingAccounts, checkingAccounts }
}
```

Simpan resolver dan arahkan ke bagian **Kueri** AWS AppSync konsol untuk mengisi akun.

Jalankan mutasi berikut:

```
mutation populateAccounts {
  populateAccounts (
    savingAccounts: [
      {accountNumber: "1", username: "Tom", balance: 100},
      {accountNumber: "2", username: "Amy", balance: 90},
      {accountNumber: "3", username: "Lily", balance: 80},
    ]
    checkingAccounts: [
      {accountNumber: "1", username: "Tom", balance: 70},
      {accountNumber: "2", username: "Amy", balance: 60},
      {accountNumber: "3", username: "Lily", balance: 50},
    ]) {
    savingAccounts {
      accountNumber
    }
    checkingAccounts {
      accountNumber
    }
  }
}
```

Kami mengisi tiga rekening tabungan dan tiga rekening giro dalam satu mutasi.

****Gunakan konsol DynamoDB untuk memvalidasi bahwa data muncul di tabel SavingAccounts dan CheckingAccounts.****

### TransactWriteItems - Transfer uang
<a name="transactwriteitems-transfer-money-js"></a>

Lampirkan resolver ke `transferMoney` mutasi dengan kode berikut. Untuk setiap transfer, kami membutuhkan pengubah sukses untuk rekening giro dan tabungan, dan kami perlu melacak transfer dalam transaksi.

```
import { util } from '@aws-appsync/utils'

export function request(ctx) {
	const transactions = ctx.args.transactions

	const savings = []
	const checkings = []
	const history = []
	transactions.forEach((t) => {
		const { savingAccountNumber, checkingAccountNumber, amount } = t
		savings.push({
			table: 'savingAccounts',
			operation: 'UpdateItem',
			key: util.dynamodb.toMapValues({ accountNumber: savingAccountNumber }),
			update: {
				expression: 'SET balance = balance - :amount',
				expressionValues: util.dynamodb.toMapValues({ ':amount': amount }),
			},
		})
		checkings.push({
			table: 'checkingAccounts',
			operation: 'UpdateItem',
			key: util.dynamodb.toMapValues({ accountNumber: checkingAccountNumber }),
			update: {
				expression: 'SET balance = balance + :amount',
				expressionValues: util.dynamodb.toMapValues({ ':amount': amount }),
			},
		})
		history.push({
			table: 'transactionHistory',
			operation: 'PutItem',
			key: util.dynamodb.toMapValues({ transactionId: util.autoId() }),
			attributeValues: util.dynamodb.toMapValues({
				from: savingAccountNumber,
				to: checkingAccountNumber,
				amount,
			}),
		})
	})

	return {
		version: '2018-05-29',
		operation: 'TransactWriteItems',
		transactItems: [...savings, ...checkings, ...history],
	}
}

export function response(ctx) {
	if (ctx.error) {
		util.error(ctx.error.message, ctx.error.type, null, ctx.result.cancellationReasons)
	}
	const tInput = ctx.args.transactions
	const tLength = tInput.length
	const keys = ctx.result.keys
	const savingAccounts = tInput.map((_, i) => keys[tLength * 0 + i])
	const checkingAccounts = tInput.map((_, i) => keys[tLength * 1 + i])
	const transactionHistory = tInput.map((_, i) => keys[tLength * 2 + i])
	return { savingAccounts, checkingAccounts, transactionHistory }
}
```

Sekarang, navigasikan ke bagian **Kueri** AWS AppSync konsol dan jalankan mutasi **TransferMoney** sebagai berikut:

```
mutation write {
  transferMoney(
    transactions: [
      {savingAccountNumber: "1", checkingAccountNumber: "1", amount: 7.5},
      {savingAccountNumber: "2", checkingAccountNumber: "2", amount: 6.0},
      {savingAccountNumber: "3", checkingAccountNumber: "3", amount: 3.3}
    ]) {
    savingAccounts {
      accountNumber
    }
    checkingAccounts {
      accountNumber
    }
    transactionHistory {
      transactionId
    }
  }
}
```

Kami mengirim tiga transaksi perbankan dalam satu mutasi. ****Gunakan konsol DynamoDB untuk memvalidasi data yang muncul di tabel SavingAccounts, **CheckingAccounts**, dan TransactionHistory.****

### TransactGetItems - Ambil akun
<a name="transactgetitems-retrieve-accounts-js"></a>

Untuk mengambil detail dari tabungan dan rekening giro dalam satu permintaan transaksional, kami akan melampirkan resolver ke operasi `Query.getAccounts` GraphQL pada skema kami. Pilih **Lampirkan**, pilih sumber `TransactTutorial` data yang sama yang dibuat di awal tutorial. Gunakan kode berikut: 

```
import { util } from '@aws-appsync/utils'

export function request(ctx) {
	const { savingAccountNumbers, checkingAccountNumbers } = ctx.args

	const savings = savingAccountNumbers.map((accountNumber) => {
		return { table: 'savingAccounts', key: util.dynamodb.toMapValues({ accountNumber }) }
	})
	const checkings = checkingAccountNumbers.map((accountNumber) => {
		return { table: 'checkingAccounts', key: util.dynamodb.toMapValues({ accountNumber }) }
	})
	return {
		version: '2018-05-29',
		operation: 'TransactGetItems',
		transactItems: [...savings, ...checkings],
	}
}

export function response(ctx) {
	if (ctx.error) {
		util.error(ctx.error.message, ctx.error.type, null, ctx.result.cancellationReasons)
	}

	const { savingAccountNumbers: sInput, checkingAccountNumbers: cInput } = ctx.args
	const items = ctx.result.items
	const savingAccounts = sInput.map((_, i) => items[i])
	const sLength = sInput.length
	const checkingAccounts = cInput.map((_, i) => items[sLength + i])
	return { savingAccounts, checkingAccounts }
}
```

Simpan resolver dan navigasikan ke bagian **Kueri** konsol. AWS AppSync Untuk mengambil rekening tabungan dan giro, jalankan kueri berikut:

```
query getAccounts {
  getAccounts(
    savingAccountNumbers: ["1", "2", "3"],
    checkingAccountNumbers: ["1", "2"]
  ) {
    savingAccounts {
      accountNumber
      username
      balance
    }
    checkingAccounts {
      accountNumber
      username
      balance
    }
  }
}
```

Kami telah berhasil menunjukkan penggunaan transaksi DynamoDB menggunakan. AWS AppSync

# Menggunakan operasi batch DynamoDB di AWS AppSync
<a name="tutorial-dynamodb-batch-js"></a>

AWS AppSync mendukung penggunaan operasi batch Amazon DynamoDB di satu atau beberapa tabel dalam satu Wilayah. Operasi yang didukung adalah`BatchGetItem`,`BatchPutItem`, dan`BatchDeleteItem`. Dengan menggunakan fitur-fitur ini di AWS AppSync, Anda dapat melakukan tugas-tugas seperti:
+ Melewati daftar kunci dalam satu kueri dan mengembalikan hasil dari tabel
+ Membaca catatan dari satu atau beberapa tabel dalam satu kueri
+ Menulis catatan secara massal ke satu atau lebih tabel
+ Menulis atau menghapus catatan secara kondisional dalam beberapa tabel yang mungkin memiliki hubungan

Operasi Batch AWS AppSync memiliki dua perbedaan utama dari operasi non-batch:
+ Peran sumber data harus memiliki izin ke semua tabel yang akan diakses oleh resolver.
+ Spesifikasi tabel untuk resolver adalah bagian dari objek permintaan.

## Batch tabel tunggal
<a name="single-table-batch-js"></a>

**Awas**  
`BatchPutItem`dan `BatchDeleteItem` tidak didukung saat digunakan dengan deteksi dan resolusi konflik. Pengaturan ini harus dinonaktifkan untuk mencegah kemungkinan kesalahan.

Untuk memulai, mari buat GraphQL API baru. Di AWS AppSync konsol, pilih **Create API**, ** APIsGraphQL**, **dan** Design dari awal. **Beri nama API Anda`BatchTutorial API`, pilih **Berikutnya**, dan pada langkah **Tentukan sumber daya GraphQL**, pilih **Buat sumber daya GraphQL** nanti dan klik Berikutnya.** Tinjau detail Anda dan buat API. Pergi ke halaman **Skema** dan tempel skema berikut, perhatikan bahwa untuk kueri, kami akan meneruskan daftar: IDs

```
type Post {
    id: ID!
    title: String
}

input PostInput {
    id: ID!
    title: String
}

type Query {
    batchGet(ids: [ID]): [Post]
}

type Mutation {
    batchAdd(posts: [PostInput]): [Post]
    batchDelete(ids: [ID]): [Post]
}
```

Simpan skema Anda dan pilih **Buat Sumber Daya** di bagian atas halaman. Pilih **Gunakan tipe yang ada** dan pilih `Post` jenisnya. Beri nama meja Anda`Posts`. **Pastikan **Primary Key** diatur ke`id`, batalkan pilihan **Automatically generate GraphQL** (Anda akan memberikan kode Anda sendiri), dan pilih Create.** Untuk memulai, AWS AppSync buat tabel DynamoDB baru dan sumber data yang terhubung ke tabel dengan peran yang sesuai. Namun, masih ada beberapa izin yang perlu Anda tambahkan ke peran tersebut. Buka halaman **Sumber data** dan pilih sumber data baru. Di bawah **Pilih peran yang ada**, Anda akan melihat bahwa peran dibuat secara otomatis untuk tabel. Perhatikan peran (akan terlihat seperti`appsync-ds-ddb-aaabbbcccddd-Posts`) dan kemudian pergi ke konsol IAM ([https://console.aws.amazon.com/iam/](https://console.aws.amazon.com/iam/)). Di konsol IAM, pilih **Peran**, lalu pilih peran Anda dari tabel. Dalam peran Anda, di bawah **Kebijakan izin**, klik "`+`" di sebelah kebijakan (harus memiliki nama yang mirip dengan nama peran). Pilih **Edit** di bagian atas yang dapat dilipat saat kebijakan muncul. Anda perlu menambahkan izin batch ke kebijakan Anda, khususnya `dynamodb:BatchGetItem` dan`dynamodb:BatchWriteItem`. Ini akan terlihat seperti ini:

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

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:DeleteItem",
                "dynamodb:GetItem",
                "dynamodb:PutItem",
                "dynamodb:Query",
                "dynamodb:Scan",
                "dynamodb:UpdateItem",
                "dynamodb:BatchWriteItem",
                "dynamodb:BatchGetItem"
            ],
            "Resource": [
                "arn:aws:dynamodb:us-east-1:111122223333:table/locationReadings",
                "arn:aws:dynamodb:us-east-1:111122223333:table/locationReadings/*",
                "arn:aws:dynamodb:us-east-1:111122223333:table/temperatureReadings",
                "arn:aws:dynamodb:us-east-1:111122223333:table/temperatureReadings/*"
            ]
        }
    ]
}
```

------

Pilih **Berikutnya**, lalu **Simpan perubahan**. Kebijakan Anda harus mengizinkan pemrosesan batch sekarang.

Kembali ke AWS AppSync konsol, buka halaman **Skema** dan pilih **Lampirkan** di sebelah `Mutation.batchAdd` bidang. Buat resolver Anda menggunakan `Posts` tabel sebagai sumber data. Di editor kode, ganti handler dengan cuplikan di bawah ini. Cuplikan ini secara otomatis mengambil setiap item dalam `input PostInput` tipe GraphQL dan membuat peta, yang diperlukan untuk operasi: `BatchPutItem`

```
import { util } from "@aws-appsync/utils";

export function request(ctx) {
  return {
    operation: "BatchPutItem",
    tables: {
      Posts: ctx.args.posts.map((post) => util.dynamodb.toMapValues(post)),
    },
  };
}

export function response(ctx) {
  if (ctx.error) {
    util.error(ctx.error.message, ctx.error.type);
  }
  return ctx.result.data.Posts;
}
```

Arahkan ke halaman **Kueri** AWS AppSync konsol dan jalankan `batchAdd` mutasi berikut:

```
mutation add {
    batchAdd(posts:[{
            id: 1 title: "Running in the Park"},{
            id: 2 title: "Playing fetch"
        }]){
            id
            title
    }
}
```

Anda akan melihat hasil yang dicetak di layar; ini dapat divalidasi dengan meninjau konsol DynamoDB untuk memindai nilai yang ditulis ke tabel. `Posts`

Selanjutnya, ulangi proses melampirkan resolver tetapi untuk `Query.batchGet` bidang menggunakan `Posts` tabel sebagai sumber data. Ganti handler dengan kode di bawah ini. Ini secara otomatis mengambil setiap item dalam tipe `ids:[]` GraphQL dan membangun peta yang diperlukan untuk operasi: `BatchGetItem`

```
import { util } from "@aws-appsync/utils";

export function request(ctx) {
  return {
    operation: "BatchGetItem",
    tables: {
      Posts: {
        keys: ctx.args.ids.map((id) => util.dynamodb.toMapValues({ id })),
        consistentRead: true,
      },
    },
  };
}

export function response(ctx) {
  if (ctx.error) {
    util.error(ctx.error.message, ctx.error.type);
  }
  return ctx.result.data.Posts;
}
```

Sekarang, kembali ke halaman **Queries** AWS AppSync konsol dan jalankan `batchGet` kueri berikut:

```
query get {
    batchGet(ids:[1,2,3]){
        id
        title
    }
}
```

Ini akan mengembalikan hasil untuk dua `id` nilai yang Anda tambahkan sebelumnya. Perhatikan bahwa `null` nilai dikembalikan untuk `id` dengan nilai`3`. Ini karena belum ada catatan di `Posts` tabel Anda dengan nilai itu. Perhatikan juga bahwa AWS AppSync mengembalikan hasil dalam urutan yang sama dengan kunci yang diteruskan ke kueri, yang merupakan fitur tambahan yang AWS AppSync berkinerja atas nama Anda. Jadi, jika Anda beralih ke`batchGet(ids:[1,3,2])`, Anda akan melihat bahwa pesanan berubah. Anda juga akan tahu mana yang `id` mengembalikan `null` nilai.

Terakhir, lampirkan satu resolver lagi ke `Mutation.batchDelete` bidang menggunakan `Posts` tabel sebagai sumber data. Ganti handler dengan kode di bawah ini. Ini secara otomatis mengambil setiap item dalam tipe `ids:[]` GraphQL dan membangun peta yang diperlukan untuk operasi: `BatchGetItem`

```
import { util } from "@aws-appsync/utils";

export function request(ctx) {
  return {
    operation: "BatchDeleteItem",
    tables: {
      Posts: ctx.args.ids.map((id) => util.dynamodb.toMapValues({ id })),
    },
  };
}

export function response(ctx) {
  if (ctx.error) {
    util.error(ctx.error.message, ctx.error.type);
  }
  return ctx.result.data.Posts;
}
```

Sekarang, kembali ke halaman **Queries** AWS AppSync konsol dan jalankan `batchDelete` mutasi berikut:

```
mutation delete {
    batchDelete(ids:[1,2]){ id }
}
```

Catatan dengan `id` `1` dan sekarang `2` harus dihapus. Jika Anda menjalankan kembali `batchGet()` kueri dari sebelumnya, ini akan kembali`null`.

## Batch multi-tabel
<a name="multi-table-batch-js"></a>

**Awas**  
`BatchPutItem`dan `BatchDeleteItem` tidak didukung saat digunakan dengan deteksi dan resolusi konflik. Pengaturan ini harus dinonaktifkan untuk mencegah kemungkinan kesalahan.

AWS AppSync juga memungkinkan Anda untuk melakukan operasi batch di seluruh tabel. Mari kita membangun aplikasi yang lebih kompleks. Bayangkan kita sedang membangun aplikasi kesehatan hewan peliharaan di mana sensor melaporkan lokasi dan suhu tubuh hewan peliharaan. Sensor bertenaga baterai dan mencoba terhubung ke jaringan setiap beberapa menit. Ketika sensor membuat koneksi, ia mengirimkan bacaannya ke AWS AppSync API kami. Pemicu kemudian menganalisis data sehingga dasbor dapat disajikan kepada pemilik hewan peliharaan. Mari kita fokus pada merepresentasikan interaksi antara sensor dan penyimpanan data backend.

Di AWS AppSync konsol, pilih **Create API**, ** APIsGraphQL**, **dan** Design dari awal. **Beri nama API Anda`MultiBatchTutorial API`, pilih **Berikutnya**, dan pada langkah **Tentukan sumber daya GraphQL**, pilih **Buat sumber daya GraphQL** nanti dan klik Berikutnya.** Tinjau detail Anda dan buat API. Buka halaman **Skema** dan tempel dan simpan skema berikut:

```
type Mutation {
    # Register a batch of readings
    recordReadings(tempReadings: [TemperatureReadingInput], locReadings: [LocationReadingInput]): RecordResult
    # Delete a batch of readings
    deleteReadings(tempReadings: [TemperatureReadingInput], locReadings: [LocationReadingInput]): RecordResult
}

type Query {
    # Retrieve all possible readings recorded by a sensor at a specific time
    getReadings(sensorId: ID!, timestamp: String!): [SensorReading]
}

type RecordResult {
    temperatureReadings: [TemperatureReading]
    locationReadings: [LocationReading]
}

interface SensorReading {
    sensorId: ID!
    timestamp: String!
}

# Sensor reading representing the sensor temperature (in Fahrenheit)
type TemperatureReading implements SensorReading {
    sensorId: ID!
    timestamp: String!
    value: Float
}

# Sensor reading representing the sensor location (lat,long)
type LocationReading implements SensorReading {
    sensorId: ID!
    timestamp: String!
    lat: Float
    long: Float
}

input TemperatureReadingInput {
    sensorId: ID!
    timestamp: String
    value: Float
}

input LocationReadingInput {
    sensorId: ID!
    timestamp: String
    lat: Float
    long: Float
}
```

Kita perlu membuat dua tabel DynamoDB:
+ `locationReadings`akan menyimpan pembacaan lokasi sensor.
+ `temperatureReadings`akan menyimpan pembacaan suhu sensor.

Kedua tabel akan berbagi struktur kunci primer yang sama: `sensorId (String)` sebagai kunci partisi dan `timestamp (String)` sebagai kunci sortir.

Pilih **Buat Sumber Daya** di bagian atas halaman. Pilih **Gunakan tipe yang ada** dan pilih `locationReadings` jenisnya. Beri nama meja Anda`locationReadings`. Pastikan **Primary Key** diatur ke `sensorId` dan tombol sortir ke`timestamp`. **Hapus pilihan **Secara otomatis menghasilkan** GraphQL (Anda akan memberikan kode Anda sendiri), dan pilih Buat.** Ulangi proses ini untuk `temperatureReadings` menggunakan `temperatureReadings` sebagai tipe dan nama tabel. Gunakan tombol yang sama seperti di atas.

Tabel baru Anda akan berisi peran yang dihasilkan secara otomatis. Masih ada beberapa izin yang perlu Anda tambahkan ke peran tersebut. Buka halaman **Sumber data** dan pilih`locationReadings`. Di bawah **Pilih peran yang ada**, Anda dapat melihat peran tersebut. Perhatikan peran (akan terlihat seperti`appsync-ds-ddb-aaabbbcccddd-locationReadings`) dan kemudian pergi ke konsol IAM ([https://console.aws.amazon.com/iam/](https://console.aws.amazon.com/iam/)). Di konsol IAM, pilih **Peran**, lalu pilih peran Anda dari tabel. Dalam peran Anda, di bawah **Kebijakan izin**, klik "`+`" di sebelah kebijakan (harus memiliki nama yang mirip dengan nama peran). Pilih **Edit** di bagian atas yang dapat dilipat saat kebijakan muncul. Anda perlu menambahkan izin ke kebijakan ini. Ini akan terlihat seperti ini:

Pilih **Berikutnya**, lalu **Simpan perubahan**. Ulangi proses ini untuk sumber `temperatureReadings` data menggunakan cuplikan kebijakan yang sama di atas.

### BatchPutItem - Merekam pembacaan sensor
<a name="batchputitem-recording-sensor-readings-js"></a>

Sensor kami harus dapat mengirim bacaan mereka setelah mereka terhubung ke internet. `Mutation.recordReadings`Bidang GraphQL adalah API yang akan mereka gunakan untuk melakukannya. Kita perlu menambahkan resolver ke bidang ini.

Di halaman **Skema AWS AppSync ** konsol, pilih **Lampirkan** di sebelah `Mutation.recordReadings` bidang. Pada layar berikutnya, buat resolver Anda menggunakan `locationReadings` tabel sebagai sumber data.

Setelah membuat resolver Anda, ganti handler dengan kode berikut di editor. `BatchPutItem`Operasi ini memungkinkan kita untuk menentukan beberapa tabel: 

```
import { util } from '@aws-appsync/utils'

export function request(ctx) {
	const { locReadings, tempReadings } = ctx.args
	const locationReadings = locReadings.map((loc) => util.dynamodb.toMapValues(loc))
	const temperatureReadings = tempReadings.map((tmp) => util.dynamodb.toMapValues(tmp))

	return {
		operation: 'BatchPutItem',
		tables: {
			locationReadings,
			temperatureReadings,
		},
	}
}

export function response(ctx) {
	if (ctx.error) {
		util.appendError(ctx.error.message, ctx.error.type)
	}
	return ctx.result.data
}
```

Dengan operasi batch, mungkin ada kesalahan dan hasil yang dikembalikan dari pemanggilan. Dalam hal ini, kami bebas melakukan beberapa penanganan kesalahan tambahan.

**catatan**  
`utils.appendError()`Penggunaannya mirip dengan`util.error()`, dengan perbedaan utama bahwa itu tidak mengganggu evaluasi permintaan atau penangan respons. Sebaliknya, ini menandakan ada kesalahan dengan bidang tetapi memungkinkan penangan untuk dievaluasi dan akibatnya mengembalikan data kembali ke pemanggil. Kami menyarankan Anda menggunakan `utils.appendError()` ketika aplikasi Anda perlu mengembalikan sebagian hasil.

Simpan resolver dan navigasikan ke halaman **Kueri** di konsol. AWS AppSync Kami sekarang dapat mengirim beberapa pembacaan sensor.

Jalankan mutasi berikut:

```
mutation sendReadings {
  recordReadings(
    tempReadings: [
      {sensorId: 1, value: 85.5, timestamp: "2018-02-01T17:21:05.000+08:00"},
      {sensorId: 1, value: 85.7, timestamp: "2018-02-01T17:21:06.000+08:00"},
      {sensorId: 1, value: 85.8, timestamp: "2018-02-01T17:21:07.000+08:00"},
      {sensorId: 1, value: 84.2, timestamp: "2018-02-01T17:21:08.000+08:00"},
      {sensorId: 1, value: 81.5, timestamp: "2018-02-01T17:21:09.000+08:00"}
    ]
    locReadings: [
      {sensorId: 1, lat: 47.615063, long: -122.333551, timestamp: "2018-02-01T17:21:05.000+08:00"},
      {sensorId: 1, lat: 47.615163, long: -122.333552, timestamp: "2018-02-01T17:21:06.000+08:00"},
      {sensorId: 1, lat: 47.615263, long: -122.333553, timestamp: "2018-02-01T17:21:07.000+08:00"},
      {sensorId: 1, lat: 47.615363, long: -122.333554, timestamp: "2018-02-01T17:21:08.000+08:00"},
      {sensorId: 1, lat: 47.615463, long: -122.333555, timestamp: "2018-02-01T17:21:09.000+08:00"}
    ]) {
    locationReadings {
      sensorId
      timestamp
      lat
      long
    }
    temperatureReadings {
      sensorId
      timestamp
      value
    }
  }
}
```

Kami mengirim sepuluh pembacaan sensor dalam satu mutasi dengan pembacaan dibagi menjadi dua tabel. Gunakan konsol DynamoDB untuk memvalidasi bahwa data muncul di tabel dan tabel. `locationReadings` `temperatureReadings`

### BatchDeleteItem - Menghapus pembacaan sensor
<a name="batchdeleteitem-deleting-sensor-readings-js"></a>

Demikian pula, kita juga harus dapat menghapus batch pembacaan sensor. Mari kita gunakan bidang `Mutation.deleteReadings` GraphQL untuk tujuan ini. Di halaman **Skema AWS AppSync ** konsol, pilih **Lampirkan** di sebelah `Mutation.deleteReadings` bidang. Pada layar berikutnya, buat resolver Anda menggunakan `locationReadings` tabel sebagai sumber data.

Setelah membuat resolver Anda, ganti handler di editor kode dengan cuplikan di bawah ini. Dalam resolver ini, kita menggunakan helper function mapper yang mengekstrak `sensorId` dan `timestamp` dari input yang disediakan. 

```
import { util } from '@aws-appsync/utils'

export function request(ctx) {
	const { locReadings, tempReadings } = ctx.args
	const mapper = ({ sensorId, timestamp }) => util.dynamodb.toMapValues({ sensorId, timestamp })

	return {
		operation: 'BatchDeleteItem',
		tables: {
			locationReadings: locReadings.map(mapper),
			temperatureReadings: tempReadings.map(mapper),
		},
	}
}

export function response(ctx) {
	if (ctx.error) {
		util.appendError(ctx.error.message, ctx.error.type)
	}
	return ctx.result.data
}
```

Simpan resolver dan navigasikan ke halaman **Kueri** di konsol. AWS AppSync Sekarang, mari kita hapus beberapa pembacaan sensor.

Jalankan mutasi berikut:

```
mutation deleteReadings {
  # Let's delete the first two readings we recorded
  deleteReadings(
    tempReadings: [{sensorId: 1, timestamp: "2018-02-01T17:21:05.000+08:00"}]
    locReadings: [{sensorId: 1, timestamp: "2018-02-01T17:21:05.000+08:00"}]) {
    locationReadings {
      sensorId
      timestamp
      lat
      long
    }
    temperatureReadings {
      sensorId
      timestamp
      value
    }
  }
}
```

**catatan**  
Berlawanan dengan `DeleteItem` operasi, item yang dihapus sepenuhnya tidak dikembalikan dalam respons. Hanya kunci yang dilewati yang dikembalikan. Untuk mempelajari lebih lanjut, lihat [referensi fungsi BatchDeleteItem in JavaScript resolver untuk DynamoDB](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-batch-delete-item).

Validasi melalui konsol DynamoDB bahwa kedua pembacaan ini telah dihapus dari tabel dan. `locationReadings` `temperatureReadings`

### BatchGetItem - Ambil bacaan
<a name="batchgetitem-retrieve-readings-js"></a>

Operasi umum lainnya untuk aplikasi kami adalah mengambil pembacaan untuk sensor pada titik waktu tertentu. Mari lampirkan resolver ke bidang GraphQL pada skema `Query.getReadings` kami. Di halaman **Skema AWS AppSync ** konsol, pilih **Lampirkan** di sebelah `Query.getReadings` bidang. Pada layar berikutnya, buat resolver Anda menggunakan `locationReadings` tabel sebagai sumber data.

Mari kita gunakan kode berikut: 

```
import { util } from '@aws-appsync/utils'

export function request(ctx) {
	const keys = [util.dynamodb.toMapValues(ctx.args)]
	const consistentRead = true
	return {
		operation: 'BatchGetItem',
		tables: {
			locationReadings: { keys, consistentRead },
			temperatureReadings: { keys, consistentRead },
		},
	}
}

export function response(ctx) {
	if (ctx.error) {
		util.appendError(ctx.error.message, ctx.error.type)
	}
	const { locationReadings: locs, temperatureReadings: temps } = ctx.result.data

	return [
		...locs.map((l) => ({ ...l, __typename: 'LocationReading' })),
		...temps.map((t) => ({ ...t, __typename: 'TemperatureReading' })),
	]
}
```

Simpan resolver dan navigasikan ke halaman **Kueri** di konsol. AWS AppSync Sekarang, mari kita ambil pembacaan sensor kita.

Jalankan kueri berikut:

```
query getReadingsForSensorAndTime {
  # Let's retrieve the very first two readings
  getReadings(sensorId: 1, timestamp: "2018-02-01T17:21:06.000+08:00") {
    sensorId
    timestamp
    ...on TemperatureReading {
      value
    }
    ...on LocationReading {
      lat
      long
    }
  }
}
```

Kami telah berhasil mendemonstrasikan penggunaan operasi batch DynamoDB menggunakan. AWS AppSync

## Penanganan kesalahan
<a name="error-handling-js"></a>

Dalam AWS AppSync, operasi sumber data terkadang dapat mengembalikan hasil sebagian. Hasil sebagian adalah istilah yang akan kita gunakan untuk menunjukkan ketika output dari suatu operasi terdiri dari beberapa data dan kesalahan. Karena penanganan kesalahan secara inheren spesifik aplikasi, AWS AppSync memberi Anda kesempatan untuk menangani kesalahan dalam penangan respons. Kesalahan pemanggilan resolver, jika ada, tersedia dari konteks sebagai. `ctx.error` Kesalahan pemanggilan selalu menyertakan pesan dan tipe, dapat diakses sebagai properti `ctx.error.message` dan. `ctx.error.type` Dalam handler respon, Anda dapat menangani sebagian hasil dalam tiga cara:

1. Menelan kesalahan pemanggilan hanya dengan mengembalikan data.

1. Naikkan kesalahan (menggunakan`util.error(...)`) dengan menghentikan evaluasi handler, yang tidak akan mengembalikan data apa pun.

1. Tambahkan kesalahan (menggunakan`util.appendError(...)`) dan juga mengembalikan data.

Mari kita tunjukkan masing-masing dari tiga poin di atas dengan operasi batch DynamoDB.

### Operasi Batch DynamoDB
<a name="dynamodb-batch-operations-js"></a>

Dengan operasi batch DynamoDB, ada kemungkinan bahwa batch sebagian selesai. Artinya, ada kemungkinan bahwa beberapa item atau kunci yang diminta dibiarkan tidak diproses. Jika AWS AppSync tidak dapat menyelesaikan batch, item yang belum diproses dan kesalahan pemanggilan akan disetel pada konteksnya.

Kami akan menerapkan penanganan kesalahan menggunakan konfigurasi `Query.getReadings` bidang dari `BatchGetItem` operasi dari bagian sebelumnya dari tutorial ini. Kali ini, mari kita berpura-pura bahwa saat mengeksekusi `Query.getReadings` field, tabel DynamoDB `temperatureReadings` kehabisan throughput yang disediakan. DynamoDB mengangkat `ProvisionedThroughputExceededException` a selama upaya kedua AWS AppSync dengan memproses elemen yang tersisa dalam batch.

JSON berikut mewakili konteks serial setelah pemanggilan batch DynamoDB tetapi sebelum penangan respons dipanggil:

```
{
  "arguments": {
    "sensorId": "1",
    "timestamp": "2018-02-01T17:21:05.000+08:00"
  },
  "source": null,
  "result": {
    "data": {
      "temperatureReadings": [
        null
      ],
      "locationReadings": [
        {
          "lat": 47.615063,
          "long": -122.333551,
          "sensorId": "1",
          "timestamp": "2018-02-01T17:21:05.000+08:00"
        }
      ]
    },
    "unprocessedKeys": {
      "temperatureReadings": [
        {
          "sensorId": "1",
          "timestamp": "2018-02-01T17:21:05.000+08:00"
        }
      ],
      "locationReadings": []
    }
  },
  "error": {
    "type": "DynamoDB:ProvisionedThroughputExceededException",
    "message": "You exceeded your maximum allowed provisioned throughput for a table or for one or more global secondary indexes. (...)"
  },
  "outErrors": []
}
```

Beberapa hal yang perlu diperhatikan pada konteksnya:
+ Kesalahan pemanggilan telah disetel pada konteks di `ctx.error` by AWS AppSync, dan jenis kesalahan telah disetel ke. `DynamoDB:ProvisionedThroughputExceededException`
+ Hasil dipetakan per tabel di bawah `ctx.result.data` meskipun ada kesalahan.
+ Kunci yang dibiarkan tidak diproses tersedia di`ctx.result.data.unprocessedKeys`. Di sini, AWS AppSync tidak dapat mengambil item dengan kunci (sensorid:1, stempel waktu: 2018-02-01T 17:21:05.000 \$1 08:00) karena throughput tabel yang tidak mencukupi.

**catatan**  
Untuk`BatchPutItem`, itu`ctx.result.data.unprocessedItems`. Untuk`BatchDeleteItem`, itu`ctx.result.data.unprocessedKeys`.

Mari kita tangani kesalahan ini dengan tiga cara berbeda.

#### 1. Menelan kesalahan pemanggilan
<a name="swallowing-the-invocation-error-js"></a>

Mengembalikan data tanpa menangani kesalahan pemanggilan secara efektif menelan kesalahan, membuat hasil untuk bidang GraphQL yang diberikan selalu berhasil.

Kode yang kami tulis sudah tidak asing lagi dan hanya berfokus pada data hasil.

**Penangan respons**

```
export function response(ctx) {
  return ctx.result.data
}
```

**Tanggapan GraphQL**

```
{
  "data": {
    "getReadings": [
      {
        "sensorId": "1",
        "timestamp": "2018-02-01T17:21:05.000+08:00",
        "lat": 47.615063,
        "long": -122.333551
      },
      {
        "sensorId": "1",
        "timestamp": "2018-02-01T17:21:05.000+08:00",
        "value": 85.5
      }
    ]
  }
}
```

Tidak ada kesalahan yang akan ditambahkan ke respons kesalahan karena hanya data yang ditindaklanjuti.

#### 2. Memunculkan kesalahan untuk membatalkan eksekusi handler respons
<a name="raising-an-error-to-abort-the-response-execution-js"></a>

Ketika kegagalan sebagian harus diperlakukan sebagai kegagalan total dari perspektif klien, Anda dapat membatalkan eksekusi handler respons untuk mencegah pengembalian data. Metode `util.error(...)` utilitas mencapai perilaku ini dengan tepat.

**Kode penangan respons**

```
export function response(ctx) {
  if (ctx.error) {
    util.error(ctx.error.message, ctx.error.type, null, ctx.result.data.unprocessedKeys);
  }
  return ctx.result.data;
}
```

**Tanggapan GraphQL**

```
{
  "data": {
    "getReadings": null
  },
  "errors": [
    {
      "path": [
        "getReadings"
      ],
      "data": null,
      "errorType": "DynamoDB:ProvisionedThroughputExceededException",
      "errorInfo": {
        "temperatureReadings": [
          {
            "sensorId": "1",
            "timestamp": "2018-02-01T17:21:05.000+08:00"
          }
        ],
        "locationReadings": []
      },
      "locations": [
        {
          "line": 58,
          "column": 3
        }
      ],
      "message": "You exceeded your maximum allowed provisioned throughput for a table or for one or more global secondary indexes. (...)"
    }
  ]
}
```

*Meskipun beberapa hasil mungkin telah dikembalikan dari operasi batch DynamoDB, kami memilih untuk memunculkan kesalahan sehingga bidang `getReadings` GraphQL adalah nol dan kesalahan telah ditambahkan ke blok kesalahan respons GraphQL.*

#### 3. Menambahkan kesalahan untuk mengembalikan data dan kesalahan
<a name="appending-an-error-to-return-both-data-and-errors-js"></a>

Dalam kasus tertentu, untuk memberikan pengalaman pengguna yang lebih baik, aplikasi dapat mengembalikan sebagian hasil dan memberi tahu klien mereka tentang item yang belum diproses. Klien dapat memutuskan untuk menerapkan coba lagi atau menerjemahkan kesalahan kembali ke pengguna akhir. Ini `util.appendError(...)` adalah metode utilitas yang memungkinkan perilaku ini dengan membiarkan perancang aplikasi menambahkan kesalahan pada konteks tanpa mengganggu evaluasi penangan respons. Setelah mengevaluasi penangan respons, AWS AppSync akan memproses kesalahan konteks apa pun dengan menambahkannya ke blok kesalahan respons GraphQL.

**Kode penangan respons**

```
export function response(ctx) {
  if (ctx.error) {
    util.appendError(ctx.error.message, ctx.error.type, null, ctx.result.data.unprocessedKeys);
  }
  return ctx.result.data;
}
```

Kami meneruskan kesalahan pemanggilan dan `unprocessedKeys` elemen di dalam blok kesalahan respons GraphQL. `getReadings`Bidang ini juga mengembalikan sebagian data dari `locationReadings` tabel seperti yang Anda lihat pada respons di bawah ini.

**Tanggapan GraphQL**

```
{
  "data": {
    "getReadings": [
      null,
      {
        "sensorId": "1",
        "timestamp": "2018-02-01T17:21:05.000+08:00",
        "value": 85.5
      }
    ]
  },
  "errors": [
    {
      "path": [
        "getReadings"
      ],
      "data": null,
      "errorType": "DynamoDB:ProvisionedThroughputExceededException",
      "errorInfo": {
        "temperatureReadings": [
          {
            "sensorId": "1",
            "timestamp": "2018-02-01T17:21:05.000+08:00"
          }
        ],
        "locationReadings": []
      },
      "locations": [
        {
          "line": 58,
          "column": 3
        }
      ],
      "message": "You exceeded your maximum allowed provisioned throughput for a table or for one or more global secondary indexes. (...)"
    }
  ]
}
```

# Menggunakan resolver HTTP di AWS AppSync
<a name="tutorial-http-resolvers-js"></a>

AWS AppSync memungkinkan Anda menggunakan sumber data yang didukung (yaitu, Amazon DynamoDB AWS Lambda, Amazon Service, atau OpenSearch Amazon Aurora) untuk melakukan berbagai operasi, selain titik akhir HTTP arbitrer untuk menyelesaikan bidang GraphQL. Setelah titik akhir HTTP Anda tersedia, Anda dapat menghubungkannya menggunakan sumber data. Kemudian, Anda dapat mengonfigurasi resolver dalam skema untuk melakukan operasi GraphQL seperti kueri, mutasi, dan langganan. Tutorial ini memandu Anda melalui beberapa contoh umum.

Dalam tutorial ini Anda menggunakan REST API (dibuat menggunakan Amazon API Gateway dan Lambda) dengan titik akhir AWS AppSync GraphQL.

## Membuat REST API
<a name="creating-a-rest-api"></a>

Anda dapat menggunakan AWS CloudFormation template berikut untuk menyiapkan titik akhir REST yang berfungsi untuk tutorial ini:

[https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/http/http-api-gw.yaml](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/http/http-api-gw.yaml)

 AWS CloudFormation Tumpukan melakukan langkah-langkah berikut:

1. Menyiapkan fungsi Lambda yang berisi logika bisnis Anda untuk layanan mikro Anda.

1. Menyiapkan API REST API Gateway dengan kombinasi endpoint/method/content tipe berikut:


****  

| Jalur Sumber Daya API | Metode HTTP | Jenis Konten yang Didukung | 
| --- | --- | --- | 
|  /v1/pengguna  |  POST  |  aplikasi/json  | 
|  /v1/pengguna  |  GET  |  aplikasi/json  | 
|  /v1/pengguna/1  |  GET  |  aplikasi/json  | 
|  /v1/pengguna/1  |  PUT  |  aplikasi/json  | 
|  /v1/pengguna/1  |  DELETE  |  aplikasi/json  | 

## Membuat GraphQL API
<a name="creating-your-graphql-api"></a>

Untuk membuat GraphQL API di: AWS AppSync

1. Buka AWS AppSync konsol dan pilih **Buat API**.

1. Pilih ** APIsGraphQL** dan kemudian **pilih** Desain dari awal. Pilih **Berikutnya**.

1. Untuk nama API, ketik`UserData`. Pilih **Berikutnya**.

1. Pilih `Create GraphQL resources later`. Pilih **Berikutnya**.

1. Tinjau input Anda dan pilih **Buat API**.

 AWS AppSync Konsol membuat API GraphQL baru untuk Anda menggunakan mode autentikasi kunci API. Anda dapat menggunakan konsol untuk mengonfigurasi GraphQL API dan menjalankan permintaan lebih lanjut.

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

Sekarang Anda memiliki GraphQL API, mari buat skema GraphQL. Di editor **Skema** di AWS AppSync konsol, gunakan cuplikan di bawah ini:

```
type Mutation {
    addUser(userInput: UserInput!): User
    deleteUser(id: ID!): User
}

type Query {
    getUser(id: ID): User
    listUser: [User!]!
}

type User {
    id: ID!
    username: String!
    firstname: String
    lastname: String
    phone: String
    email: String
}

input UserInput {
    id: ID!
    username: String!
    firstname: String
    lastname: String
    phone: String
    email: String
}
```

## Konfigurasikan sumber data HTTP Anda
<a name="configure-your-http-data-source"></a>

Untuk mengonfigurasi sumber data HTTP Anda, lakukan hal berikut:

1. Di halaman **Sumber data** di AWS AppSync GraphQL API Anda, **pilih** Buat sumber data.

1. Masukkan nama untuk sumber data seperti`HTTP_Example`.

1. Di **tipe sumber data**, pilih **titik akhir HTTP**.

1. Setel titik akhir ke titik akhir API Gateway yang dibuat di awal tutorial. **Anda dapat menemukan titik akhir yang dihasilkan tumpukan jika Anda menavigasi ke konsol Lambda dan menemukan aplikasi Anda di bawah Aplikasi.** Di dalam pengaturan aplikasi Anda, Anda akan melihat **titik akhir API yang akan menjadi titik** akhir Anda. AWS AppSync Pastikan Anda tidak menyertakan nama panggung sebagai bagian dari titik akhir. Misalnya, jika titik akhir Anda`https://aaabbbcccd.execute-api.us-east-1.amazonaws.com/v1`, Anda akan mengetik. `https://aaabbbcccd.execute-api.us-east-1.amazonaws.com`

**catatan**  
Saat ini, hanya titik akhir publik yang didukung oleh AWS AppSync.  
Untuk informasi selengkapnya tentang otoritas sertifikasi yang diakui oleh AWS AppSync layanan, lihat [Otoritas Sertifikat (CA) yang Diakui oleh AWS AppSync untuk Titik Akhir HTTPS](http-cert-authorities.md#aws-appsync-http-certificate-authorities).

## Mengkonfigurasi resolver
<a name="configuring-resolvers"></a>

Pada langkah ini, Anda akan menghubungkan sumber data HTTP ke `getUser` dan `addUser` query.

Untuk mengatur `getUser` resolver:

1. **Di AWS AppSync GraphQL API Anda, pilih tab Schema.**

1. **Di sebelah kanan editor **Skema**, di panel **Resolvers** dan di bawah jenis **Kueri**, temukan `getUser` bidang dan pilih Lampirkan.**

1. Pertahankan jenis resolver ke `Unit` dan runtime ke. `APPSYNC_JS`

1. Di **nama sumber data**, pilih titik akhir HTTP yang Anda buat sebelumnya.

1. Pilih **Buat**.

1. Di editor kode **Resolver**, tambahkan cuplikan berikut sebagai penangan permintaan Anda:

   ```
   import { util } from '@aws-appsync/utils'
   
   export function request(ctx) {
   	return {
   		version: '2018-05-29',
   		method: 'GET',
   		params: {
   			headers: {
   				'Content-Type': 'application/json',
   			},
   		},
   		resourcePath: `/v1/users/${ctx.args.id}`,
   	}
   }
   ```

1. Tambahkan cuplikan berikut sebagai handler respons Anda:

   ```
   export function response(ctx) {
   	const { statusCode, body } = ctx.result
   	// if response is 200, return the response
   	if (statusCode === 200) {
   		return JSON.parse(body)
   	}
   	// if response is not 200, append the response to error block.
   	util.appendError(body, statusCode)
   }
   ```

1. Pilih tab **Query**, dan kemudian jalankan query berikut:

   ```
   query GetUser{
       getUser(id:1){
           id
           username
       }
   }
   ```

   Ini akan mengembalikan respons berikut:

   ```
   {
       "data": {
           "getUser": {
               "id": "1",
               "username": "nadia"
           }
       }
   }
   ```

Untuk mengatur `addUser` resolver:

1. Pilih tab **Skema**.

1. **Di sebelah kanan editor **Skema**, di panel **Resolvers** dan di bawah jenis **Kueri**, temukan `addUser` bidang dan pilih Lampirkan.**

1. Pertahankan jenis resolver ke `Unit` dan runtime ke. `APPSYNC_JS`

1. Di **nama sumber data**, pilih titik akhir HTTP yang Anda buat sebelumnya.

1. Pilih **Buat**.

1. Di editor kode **Resolver**, tambahkan cuplikan berikut sebagai penangan permintaan Anda:

   ```
   export function request(ctx) {
       return {
           "version": "2018-05-29",
           "method": "POST",
           "resourcePath": "/v1/users",
           "params":{
               "headers":{
                   "Content-Type": "application/json"
               },
           "body": ctx.args.userInput
           }
       }
   }
   ```

1. Tambahkan cuplikan berikut sebagai handler respons Anda:

   ```
   export function response(ctx) {
       if(ctx.error) {
           return util.error(ctx.error.message, ctx.error.type)
       }
       if (ctx.result.statusCode == 200) {
           return ctx.result.body
       } else {
           return util.appendError(ctx.result.body, "ctx.result.statusCode")
       }
   }
   ```

1. Pilih tab **Query**, dan kemudian jalankan query berikut:

   ```
   mutation addUser{
       addUser(userInput:{
           id:"2",
           username:"shaggy"
       }){
           id
           username
       }
   }
   ```

   Jika Anda menjalankan `getUser` kueri lagi, itu akan mengembalikan respons berikut:

   ```
   {
       "data": {
           "getUser": {
           "id": "2",
           "username": "shaggy"
           }
       }
   }
   ```

## Layanan Pemanggilan AWS
<a name="invoking-aws-services-js"></a>

Anda dapat menggunakan resolver HTTP untuk menyiapkan antarmuka GraphQL API untuk layanan. AWS Permintaan HTTP AWS harus ditandatangani dengan [proses Signature Version 4](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html) sehingga AWS dapat mengidentifikasi siapa yang mengirimnya. AWS AppSync menghitung tanda tangan atas nama Anda saat Anda mengaitkan peran IAM dengan sumber data HTTP.

Anda menyediakan dua komponen tambahan untuk memanggil AWS layanan dengan resolver HTTP:
+ Peran IAM dengan izin untuk memanggil layanan AWS APIs
+ Menandatangani konfigurasi di sumber data

Misalnya, jika Anda ingin memanggil [ListGraphqlApis operasi](https://docs.aws.amazon.com/appsync/latest/APIReference/API_ListGraphqlApis.html) dengan resolver HTTP, pertama-tama Anda [membuat peran IAM yang AWS AppSync diasumsikan dengan kebijakan](attaching-a-data-source.md#aws-appsync-getting-started-build-a-schema-from-scratch) berikut yang dilampirkan:

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

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Action": [
                "appsync:ListGraphqlApis"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}
```

------

Selanjutnya, buat sumber data HTTP untuk AWS AppSync. Dalam contoh ini, Anda menelepon AWS AppSync di Wilayah Barat AS (Oregon). Siapkan konfigurasi HTTP berikut dalam file bernama`http.json`, yang mencakup wilayah penandatanganan dan nama layanan:

```
{
    "endpoint": "https://appsync.us-west-2.amazonaws.com/",
    "authorizationConfig": {
        "authorizationType": "AWS_IAM",
        "awsIamConfig": {
            "signingRegion": "us-west-2",
            "signingServiceName": "appsync"
        }
    }
}
```

Kemudian, gunakan AWS CLI untuk membuat sumber data dengan peran terkait sebagai berikut:

```
aws appsync create-data-source --api-id <API-ID> \
                               --name AWSAppSync \
                               --type HTTP \
                               --http-config file:///http.json \
                               --service-role-arn <ROLE-ARN>
```

Saat Anda melampirkan resolver ke bidang dalam skema, gunakan templat pemetaan permintaan berikut untuk memanggil: AWS AppSync

```
{
    "version": "2018-05-29",
    "method": "GET",
    "resourcePath": "/v1/apis"
}
```

Saat Anda menjalankan kueri GraphQL untuk sumber data ini AWS AppSync , tandatangani permintaan menggunakan peran yang Anda berikan dan menyertakan tanda tangan dalam permintaan. Kueri mengembalikan daftar AWS AppSync APIs GraphQL di akun Anda di Wilayah tersebut. AWS 

# 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
```