Amazon RDS 的 AWS AppSync JavaScript 解析器函数参考 - AWS AppSync GraphQL

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

Amazon RDS 的 AWS AppSync JavaScript 解析器函数参考

AWS AppSync RDS 函数和解析器允许开发人员使用 RDS 数据 API 向 Amazon Aurora 集群数据库发送 SQL 查询,并返回这些查询的结果。您可以使用 AWS AppSync 的 rds 模块带有 sql 标签的模板或使用 rds 模块的 selectinsertupdateremove 帮助程序函数来编写发送到数据 API 的 SQL 语句。AWS AppSync 利用 RDS 数据服务的 ExecuteStatement 操作对数据库运行 SQL 语句。

带 SQL 标签的模板

AWS AppSync 的带 sql 标签的模板使您能够使用模板表达式创建可在运行时系统中接收动态值的静态语句。AWS AppSync 根据表达式值构建变量映射,以构造发送到 Amazon Aurora Serverless 数据 API 的 SqlParameterized 查询。使用此方法,运行时传递的动态值不可能修改原始语句,这可能会导致意外执行。所有动态值都作为参数传递,不能修改原始语句,也不会由数据库执行。这使您的查询不太容易受到 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); }

借助于通过变量映射传递所有动态值,我们依靠数据库引擎来安全地处理和清理值。

创建语句

函数和解析器可以与 MySQL 和 PostgreSQL 数据库进行交互。分别使用 createMySQLStatementcreatePgStatement 来构建语句。例如,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); }
注意

createPgStatementcreateMySQLStatement 不转义或引用使用带 sql 标签的模板构建的语句。

检索数据

您执行的 SQL 语句的结果可在响应处理程序的 context.result 对象中提供。结果是一个 JSON 字符串,其中包含来自 ExecuteStatement 操作的响应元素。解析后,结果具有以下形状:

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 行或多行。如果结果值无效或不符合预期,则 toJsonObject 返回 null

实用程序函数

您可以使用 AWS AppSync RDS 模块的实用程序帮助程序与您的数据库进行交互。

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'] })); }

限制和偏移

您可以将 limitoffset 应用于查询:

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 } } ]} })); }

您也可以使用以下运算符来比较值:

运算符 描述 可能的值类型
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

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,您可以使用 returning 从插入的行中获取数据。它接受 * 或列名称数组:

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) }

update 实用程序允许您更新现有行。您可以使用条件对象将更改应用于满足条件的所有行中的指定列。例如,假设我们有一个允许我们进行这种突变的架构。我们要将 Personname 更新为 id3,但仅限我们自 2000 年开始就已知道它们 (known_since):

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 返回修改后的数据。

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) }

转换

在某些情况下,您可能希望在语句中使用更具体的正确对象类型。您可以使用提供的类型提示来指定参数的类型。AWS AppSync 支持与数据 API 相同的类型提示。您可以使用 AWS AppSync rds 模块中的 typeHint 函数来强制转换参数。

以下示例允许您将数组作为强制转换为 JSON 对象的值发送。我们使用 -> 运算符来检索 JSON 数组中 index2 的元素:

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 }

在处理和比较 DATETIMETIMESTAMP 时,强制转换也很有用:

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 类型的对象发送到数据库。