

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# Amazon RDS 的AWS AppSync JavaScript 解析程式函數參考
<a name="resolver-reference-rds-js"></a>

 AWS AppSync RDS 函數和解析程式可讓開發人員使用 RDS Data API 將SQL查詢傳送至 Amazon Aurora 叢集資料庫，並取回這些查詢的結果。您可以使用 AWS AppSync`rds`的模組標記範本或使用`rds`模組的 `select`、`update`、 `insert`和 `remove` helper 函數來寫入傳送到資料 API 的SQL陳述`sql`式。 AWS AppSync 會利用 RDS Data Service [https://docs.aws.amazon.com//rdsdataservice/latest/APIReference/API_ExecuteStatement.html](https://docs.aws.amazon.com//rdsdataservice/latest/APIReference/API_ExecuteStatement.html)的動作，對資料庫執行 SQL 陳述式。

**主題**
+ [SQL 標記範本](#sql-tagged-templates)
+ [建立陳述式](#creating-statements)
+ [擷取資料](#retrieving-data)
+ [公用程式函數](#utility-functions)
+ [SQL Select](#utility-functions-select)
+ [SQL 插入](#utility-functions-insert)
+ [SQL 更新](#utility-functions-update)
+ [SQL 刪除](#utility-functions-delete)
+ [轉換](#casting)

## SQL 標記範本
<a name="sql-tagged-templates"></a>

AWS AppSync的`sql`標記範本可讓您建立靜態陳述式，以使用範本表達式在執行時間接收動態值。 會從表達式值 AWS AppSync 建置變數映射，以建構傳送至 Amazon Aurora Serverless Data API 的[https://docs.aws.amazon.com//rdsdataservice/latest/APIReference/API_SqlParameter.html](https://docs.aws.amazon.com//rdsdataservice/latest/APIReference/API_SqlParameter.html)查詢。使用此方法時，執行時間傳遞的動態值無法修改原始陳述式，這可能會導致意外執行。所有動態值都會以參數形式傳遞，無法修改原始陳述式，也不會由資料庫執行。這會使您的查詢較不易受到SQL注入攻擊。

**注意**  
在所有情況下，撰寫SQL陳述式時，您應該遵循安全準則，以正確處理作為輸入的接收資料。

**注意**  
`sql` 標記的範本僅支援傳遞變數值。您無法使用表達式動態指定資料欄或資料表名稱。不過，您可以使用公用程式函數來建置動態陳述式。

在下列範例中，我們會建立查詢，根據在執行時間於 GraphQL 查詢中動態設定的`col`引數值進行篩選。此值只能使用標籤表達式新增至 陳述式：

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

export function request(ctx) {
  const query = sql`
SELECT * FROM table 
WHERE column = ${ctx.args.col}`
  ;
  return createMySQLStatement(query);
}
```

透過透過變數映射傳遞所有動態值，我們依賴資料庫引擎安全地處理和淨化值。

## 建立陳述式
<a name="creating-statements"></a>

函數和解析程式可以與 MySQL 和 PostgreSQL 資料庫互動。`createPgStatement` 分別使用 `createMySQLStatement`和 來建置陳述式。例如， `createMySQLStatement`可以建立 MySQL 查詢。這些函數接受最多兩個陳述式，當請求應該立即擷取結果時很有用。使用 MySQL，您可以執行下列動作：

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

export function request(ctx) {
    const { id, text } = ctx.args;
    const s1 = sql`insert into Post(id, text) values(${id}, ${text})`;
    const s2 = sql`select * from Post where id = ${id}`;
    return createMySQLStatement(s1, s2);
}
```

**注意**  
`createPgStatement` 和 `createMySQLStatement`不會逸出或使用`sql`標記範本建置的陳述式或引號。

## 擷取資料
<a name="retrieving-data"></a>

執行的 SQL 陳述式結果可在 `context.result` 物件的回應處理常式中使用。結果是 JSON 字串，其中包含來自 `ExecuteStatement`動作的[回應元素](https://docs.aws.amazon.com//rdsdataservice/latest/APIReference/API_ExecuteStatement.html#API_ExecuteStatement_ResponseElements)。剖析時，結果的形狀如下：

```
type SQLStatementResults = {
    sqlStatementResults: {
        records: any[];
        columnMetadata: any[];
        numberOfRecordsUpdated: number;
        generatedFields?: any[]
    }[]
}
```

您可以使用 `toJsonObject`公用程式，將結果轉換為代表傳回資料列的 JSON 物件清單。例如：

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

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

請注意， `toJsonObject`會傳回一組陳述式結果。如果您提供了一個陳述式，則陣列長度為 `1`。如果您提供兩個陳述式，則陣列長度為 `2`。陣列中的每個結果都包含 `0` 或多個資料列。`null`如果結果值無效或非預期， 會`toJsonObject`傳回 。

## 公用程式函數
<a name="utility-functions"></a>

您可以使用 AWS AppSync RDS 模組的公用程式協助程式來與資料庫互動。

### SQL Select
<a name="utility-functions-select"></a>

`select` 公用程式會建立 `SELECT`陳述式來查詢您的關聯式資料庫。

**基本使用**

以其基本形式，您可以指定要查詢的資料表：

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

export function request(ctx) {

    // Generates statement: 
    // "SELECT * FROM "persons"
    return createPgStatement(select({table: 'persons'}));
}
```

請注意，您也可以在資料表識別符中指定結構描述：

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

export function request(ctx) {

    // Generates statement:
    // SELECT * FROM "private"."persons"
    return createPgStatement(select({table: 'private.persons'}));
}
```

**指定資料欄**

您可以使用 `columns` 屬性指定資料欄。如果未將此值設定為 ，則預設為 `*`：

```
export function request(ctx) {

    // Generates statement:
    // SELECT "id", "name"
    // FROM "persons"
    return createPgStatement(select({
        table: 'persons',
        columns: ['id', 'name']
    }));
}
```

您也可以指定資料欄的資料表：

```
export function request(ctx) {

    // Generates statement: 
    // SELECT "id", "persons"."name"
    // FROM "persons"
    return createPgStatement(select({
        table: 'persons',
        columns: ['id', 'persons.name']
    }));
}
```

**限制和位移**

您可以套用 `limit`和 `offset`至查詢：

```
export function request(ctx) {

    // Generates statement: 
    // SELECT "id", "name"
    // FROM "persons"
    // LIMIT :limit
    // OFFSET :offset
    return createPgStatement(select({
        table: 'persons',
        columns: ['id', 'name'],
        limit: 10,
        offset: 40
    }));
}
```

**排序依據**

您可以使用 `orderBy` 屬性來排序結果。提供指定 欄和選用 `dir` 屬性的物件陣列：

```
export function request(ctx) {

    // Generates statement: 
    // SELECT "id", "name" FROM "persons"
    // ORDER BY "name", "id" DESC
    return createPgStatement(select({
        table: 'persons',
        columns: ['id', 'name'],
        orderBy: [{column: 'name'}, {column: 'id', dir: 'DESC'}]
    }));
}
```

**篩選條件**

您可以使用特殊條件物件來建置篩選條件：

```
export function request(ctx) {

    // Generates statement:
    // SELECT "id", "name"
    // FROM "persons"
    // WHERE "name" = :NAME
    return createPgStatement(select({
        table: 'persons',
        columns: ['id', 'name'],
        where: {name: {eq: 'Stephane'}}
    }));
}
```

您也可以合併篩選條件：

```
export function request(ctx) {

    // Generates statement:
    // SELECT "id", "name"
    // FROM "persons"
    // WHERE "name" = :NAME and "id" > :ID
    return createPgStatement(select({
        table: 'persons',
        columns: ['id', 'name'],
        where: {name: {eq: 'Stephane'}, id: {gt: 10}}
    }));
}
```

您也可以建立`OR`陳述式：

```
export function request(ctx) {

    // Generates statement:
    // SELECT "id", "name"
    // FROM "persons"
    // WHERE "name" = :NAME OR "id" > :ID
    return createPgStatement(select({
        table: 'persons',
        columns: ['id', 'name'],
        where: { or: [
            { name: { eq: 'Stephane'} },
            { id: { gt: 10 } }
        ]}
    }));
}
```

您也可以使用 否定條件`not`：

```
export function request(ctx) {

    // Generates statement:
    // SELECT "id", "name"
    // FROM "persons"
    // WHERE NOT ("name" = :NAME AND "id" > :ID)
    return createPgStatement(select({
        table: 'persons',
        columns: ['id', 'name'],
        where: { not: [
            { name: { eq: 'Stephane'} },
            { id: { gt: 10 } }
        ]}
    }));
}
```

您也可以使用下列運算子來比較值：


| 
| 
| 運算子 | Description | 可能的值類型 | 
| --- |--- |--- |
| eq | Equal | number, string, boolean | 
| ne | Not equal | number, string, boolean | 
| le | Less than or equal | number, string | 
| lt | Less than | number, string | 
| ge | Greater than or equal | number, string | 
| gt | Greater than | number, string | 
| contains | Like | string | 
| notContains | Not like | string | 
| beginsWith | Starts with prefix | string | 
| between | Between two values | number, string | 
| attributeExists | The attribute is not null | number, string, boolean | 
| size | checks the length of the element | string | 

### SQL 插入
<a name="utility-functions-insert"></a>

`insert` 公用程式提供使用 `INSERT`操作在資料庫中插入單一資料列項目的直接方式。

**單一項目插入**

若要插入項目，請指定 資料表，然後傳入值的物件。物件索引鍵會映射到您的資料表資料欄。資料欄名稱會自動逸出，並使用變數映射將值傳送至資料庫：

```
import { insert, createMySQLStatement } from '@aws-appsync/utils/rds';

export function request(ctx) {
    const { input: values } = ctx.args;
    const insertStatement = insert({ table: 'persons', values });
    
    // Generates statement:
    // INSERT INTO `persons`(`name`)
    // VALUES(:NAME)
    return createMySQLStatement(insertStatement)
}
```

**MySQL 使用案例**

您可以結合 `insert`後接 `select`來擷取插入的資料列：

```
import { insert, select, createMySQLStatement } from '@aws-appsync/utils/rds';

export function request(ctx) {
    const { input: values } = ctx.args;
    const insertStatement = insert({  table: 'persons', values });
    const selectStatement = select({
        table: 'persons',
        columns: '*',
        where: { id: { eq: values.id } },
        limit: 1,
    });
    
    // Generates statement:
    // INSERT INTO `persons`(`name`)
    // VALUES(:NAME)
    // and
    // SELECT *
    // FROM `persons`
    // WHERE `id` = :ID
    return createMySQLStatement(insertStatement, selectStatement)
}
```

**Postgres 使用案例**

透過 Postgres，您可以使用 從您插入的資料列[https://www.postgresql.org/docs/current/dml-returning.html](https://www.postgresql.org/docs/current/dml-returning.html)取得資料。它接受 `*`或一組資料欄名稱：

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

export function request(ctx) {
    const { input: values } = ctx.args;
    const insertStatement = insert({
        table: 'persons',
        values,
        returning: '*'
    });

    // Generates statement:
    // INSERT INTO "persons"("name")
    // VALUES(:NAME)
    // RETURNING *
    return createPgStatement(insertStatement)
}
```

### SQL 更新
<a name="utility-functions-update"></a>

`update` 公用程式可讓您更新現有的資料列。您可以使用 條件物件，將變更套用至滿足條件的所有資料列中的指定資料欄。例如，假設我們有一個結構描述，允許我們進行此變動。我們希望以 `Person` `id`的值更新 `name` 的 ，`3`但前提是我們從 開始知道它們 (`known_since`)`2000`：

```
mutation Update {
    updatePerson(
        input: {id: 3, name: "Jon"},
        condition: {known_since: {ge: "2000"}}
    ) {
    id
    name
  }
}
```

我們的更新解析程式如下所示：

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

export function request(ctx) {
    const { input: { id, ...values }, condition } = ctx.args;
    const where = {
        ...condition,
        id: { eq: id },
    };
    const updateStatement = update({
        table: 'persons',
        values,
        where,
        returning: ['id', 'name'],
    });

    // Generates statement:
    // UPDATE "persons"
    // SET "name" = :NAME, "birthday" = :BDAY, "country" = :COUNTRY
    // WHERE "id" = :ID
    // RETURNING "id", "name"
    return createPgStatement(updateStatement)
}
```

我們可以將檢查新增至條件，以確保只會更新主索引鍵`id`等於 `3` 的資料列。同樣地，對於 Postgres `inserts`，您可以使用 `returning` 來傳回修改後的資料。

### SQL 刪除
<a name="utility-functions-delete"></a>

`remove` 公用程式可讓您刪除現有的資料列。您可以在滿足條件的所有資料列上使用條件物件。請注意， `delete` 是 JavaScript 中的預留關鍵字。 `remove` 應該改用：

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

export function request(ctx) {
    const { input: { id }, condition } = ctx.args;
    const where = { ...condition, id: { eq: id } };
    const deleteStatement = remove({
        table: 'persons',
        where,
        returning: ['id', 'name'],
    });

    // Generates statement:
    // DELETE "persons"
    // WHERE "id" = :ID
    // RETURNING "id", "name"
    return createPgStatement(updateStatement)
}
```

## 轉換
<a name="casting"></a>

在某些情況下，您可能希望在陳述式中使用有關正確物件類型的更具體性。您可以使用提供的類型提示來指定參數的類型。 AWS AppSync 支援與資料 API [相同的類型提示](https://docs.aws.amazon.com//rdsdataservice/latest/APIReference/API_SqlParameter.html#rdsdtataservice-Type-SqlParameter-typeHint)。您可以使用 模組的 `typeHint`函數來 AWS AppSync `rds`轉換參數。

下列範例可讓您將陣列做為值傳送，該值會轉換為 JSON 物件。我們使用 `->`運算子來擷取 JSON 陣列`index``2`中 的 元素：

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

export function request(ctx) {
    const arr = ctx.args.list_of_ids
    const statement = sql`select ${typeHint.JSON(arr)}->2 as value`
    return createPgStatement(statement)
}

export function response(ctx) {
    return toJsonObject(ctx.result)[0][0].value
}
```

在處理和比較 `DATE`、 和 時`TIME`，投射也很有用`TIMESTAMP`：

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

export function request(ctx) {
    const when = ctx.args.when
    const statement = select({
        table: 'persons',
        where: { createdAt : { gt: typeHint.DATETIME(when) } }
    })
    return createPgStatement(statement)
}
```

以下另一個範例示範如何傳送目前的日期和時間：

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

export function request(ctx) {
    const now = util.time.nowFormatted('YYYY-MM-dd HH:mm:ss')
    return createPgStatement(sql`select ${typeHint.TIMESTAMP(now)}`)
}
```

**可用的類型提示**
+ `typeHint.DATE` - 對應的參數會以 `DATE`類型的物件傳送至資料庫。接受的格式為 `YYYY-MM-DD`。
+ `typeHint.DECIMAL` - 對應的參數會以 `DECIMAL`類型的物件傳送至資料庫。
+ `typeHint.JSON` - 對應的參數會以 `JSON`類型的物件傳送至資料庫。
+ `typeHint.TIME` - 對應的字串參數值會以 `TIME`類型的物件傳送至資料庫。接受的格式為 `HH:MM:SS[.FFF]`。
+ `typeHint.TIMESTAMP` - 對應的字串參數值會以 `TIMESTAMP`類型的物件傳送至資料庫。接受的格式為 `YYYY-MM-DD HH:MM:SS[.FFF]`。
+ `typeHint.UUID` - 對應的字串參數值會以 `UUID`類型的物件傳送至資料庫。