

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

# Verwenden von Aurora PostgreSQL mit Daten-API in AWS AppSync
<a name="aurora-serverless-tutorial-js"></a>

 

Erfahren Sie, wie Sie Ihre GraphQL-API mit Aurora PostgreSQL-Datenbanken verbinden können. AWS AppSync Diese Integration ermöglicht es Ihnen, skalierbare, datengesteuerte Anwendungen zu erstellen, indem Sie SQL-Abfragen und -Mutationen über GraphQL-Operationen ausführen. AWS AppSync bietet eine Datenquelle für die Ausführung von SQL-Anweisungen für Amazon Aurora Aurora-Cluster, die mit einer Daten-API aktiviert sind. Sie können AWS AppSync Resolver verwenden, um SQL-Anweisungen für die Daten-API mit GraphQL-Abfragen, -Mutationen und Abonnements auszuführen.

Bevor Sie mit diesem Tutorial beginnen, sollten Sie über grundlegende Kenntnisse von AWS Diensten und GraphQL-Konzepten verfügen.

**Anmerkung**  
Für dieses Tutorial wird die Region `US-EAST-1` verwendet. 

**Topics**
+ [Richten Sie Ihre Aurora PostgreSQL-Datenbank ein](#creating-clusters)
+ [Datenbank und Tabelle erstellen](#creating-db-table)
+ [Erstellen eines GraphQL-Schemas](#rds-graphql-schema)
+ [Resolver für RDS](#rds-resolvers)
+ [Löschen Sie Ihren Cluster](#rds-delete-cluster)

## Richten Sie Ihre Aurora PostgreSQL-Datenbank ein
<a name="creating-clusters"></a>

Bevor Sie eine Amazon RDS-Datenquelle hinzufügen AWS AppSync, gehen Sie wie folgt vor.

1. Aktivieren Sie eine Daten-API auf einem Aurora Serverless v2-Cluster.

1. Konfigurieren Sie ein Geheimnis mit AWS Secrets Manager

1. Erstellen Sie den Cluster mit dem folgenden AWS CLI Befehl.

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

Dadurch wird ein ARN für den Cluster zurückgegeben. Nachdem Sie einen Cluster erstellt haben, müssen Sie mit dem folgenden AWS CLI Befehl eine Serverless v2-Instanz hinzufügen.

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

**Anmerkung**  
Es dauert einige Zeit, bis diese Endpunkte aktiviert werden. Sie können ihren Status in der RDS-Konsole auf der Registerkarte **Konnektivität und Sicherheit** für den Cluster überprüfen.

Überprüfen Sie den Clusterstatus mit dem folgenden AWS CLI Befehl.

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

Erstellen Sie ein Secret über die AWS Secrets Manager Konsole oder AWS CLI mit einer Eingabedatei wie der folgenden, indem Sie die `USERNAME` und `COMPLEX_PASSWORD` aus dem vorherigen Schritt verwenden:

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

Übergeben Sie dies als Parameter an AWS CLI:

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

Dadurch wird ein ARN für das Secret zurückgegeben. **Notieren Sie sich** den ARN Ihres Aurora Serverless v2-Clusters und Secret für später, wenn Sie eine Datenquelle in der AWS AppSync Konsole erstellen. 

## Datenbank und Tabelle erstellen
<a name="creating-db-table"></a>

Erstellen Sie zunächst eine Datenbank mit dem Namen`TESTDB`. In PostgreSQL ist eine Datenbank ein Container, der Tabellen und andere SQL-Objekte enthält. Stellen Sie sicher, dass Ihr Aurora Serverless v2-Cluster korrekt konfiguriert ist, bevor Sie ihn zu Ihrer AWS AppSync API hinzufügen. Erstellen Sie zunächst eine *TESTDB-Datenbank* mit dem folgenden `--sql` Parameter.

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

 Wenn das ohne Fehler läuft, fügen Sie zwei Tabellen mit dem `create table` Befehl hinzu:

```
 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);'
```

Wenn dies erfolgreich ist, fügen Sie den Cluster als Datenquelle in Ihre API ein.

## Erstellen eines GraphQL-Schemas
<a name="rds-graphql-schema"></a>

Jetzt, da Ihre Aurora Serverless v2 Data API mit konfigurierten Tabellen läuft, erstellen wir ein GraphQL-Schema. Sie können Ihre API schnell erstellen, indem Sie mit dem API-Erstellungsassistenten Tabellenkonfigurationen aus einer vorhandenen Datenbank importieren.

Um zu beginnen: 

1. Wählen Sie in der AWS AppSync Konsole **Create API** und dann **Start with a Amazon Aurora Cluster** aus. 

1. Geben Sie API-Details wie den **API-Namen** an und wählen Sie dann Ihre Datenbank aus, um die API zu generieren.

1. Wählen Sie Ihre Datenbank aus. Aktualisieren Sie bei Bedarf die Region und wählen Sie dann Ihren Aurora-Cluster und Ihre *TESTDB-Datenbank* aus. 

1. Wählen Sie Ihr Secret und dann **Import**. 

1. Sobald die Tabellen erkannt wurden, aktualisieren Sie die Typnamen. Wechseln Sie `Todos` zu `Todo` und `Tasks` zu`Task`. 

1. Zeigen Sie eine Vorschau des generierten Schemas an, indem Sie **Schemavorschau** wählen. Ihr Schema wird ungefähr so aussehen: 

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

1. Für die Rolle können Sie entweder eine neue Rolle AWS AppSync erstellen lassen oder eine mit einer Richtlinie erstellen, die der folgenden ähnelt:

------
#### [ 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:*"
               ]
           }
       ]
   }
   ```

------

   Beachten Sie, dass diese Richtlinie zwei Aussagen enthält, für die Sie Rollenzugriff gewähren. Die erste Ressource ist Ihr Aurora-Cluster und die zweite ist Ihr AWS Secrets Manager ARN. 

   Wählen Sie **Weiter**, überprüfen Sie die Konfigurationsdetails und wählen Sie dann **Create API**. Sie haben jetzt eine voll funktionsfähige API. Sie können die vollständigen Details Ihrer API auf der **Schema-Seite** überprüfen. 

## Resolver für RDS
<a name="rds-resolvers"></a>

Der API-Erstellungsablauf hat automatisch die Resolver für die Interaktion mit unseren Typen erstellt. Wenn Sie sich die **Schema-Seite** ansehen, finden Sie Resolver einiger der folgenden Resolver.
+ Erstellen Sie eine über das Feld`todo`. `Mutation.createTodo`
+ Aktualisieren Sie ein `todo` über das `Mutation.updateTodo` Feld.
+ Löschen Sie ein `todo` über das `Mutation.deleteTodo` Feld.
+ Holen Sie sich eine Single `todo` über das `Query.getTodo` Feld.
+ Liste alle `todos` über das `Query.listTodos` Feld auf.

Sie werden ähnliche Felder und Resolver für den `Task` Typ finden. Schauen wir uns einige der Resolver genauer an. 

### Mutation.CreateToDo
<a name="createtodo"></a>

Wählen Sie im Schema-Editor in der AWS AppSync Konsole auf der rechten Seite die Option neben. `testdb` `createTodo(...): Todo` Der Resolver-Code verwendet die `insert` Funktion aus dem `rds` Modul, um dynamisch eine Insert-Anweisung zu erstellen, die der `todos` Tabelle Daten hinzufügt. Da wir mit Postgres arbeiten, können wir die `returning` Anweisung nutzen, um die eingefügten Daten zurückzuholen.

Aktualisieren Sie den folgenden Resolver, um den `DATE` Feldtyp korrekt anzugeben. `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]
}
```

Speichern Sie den Resolver. Der Typhinweis markiert das in unserem Eingabeobjekt `due` korrekt als `DATE` Typ. Dadurch kann die Postgres-Engine den Wert richtig interpretieren. Aktualisieren Sie als Nächstes Ihr Schema, um das `id` aus der `CreateTodo` Eingabe zu entfernen. Da unsere Postgres-Datenbank die generierte ID zurückgeben kann, können Sie sich darauf verlassen, dass sie erstellt und das Ergebnis wie folgt in einer einzigen Anfrage zurückgibt.

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

Nehmen Sie die Änderung vor und aktualisieren Sie Ihr Schema. Gehen Sie zum **Abfrage-Editor**, um der Datenbank wie folgt ein Element hinzuzufügen.

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

Sie erhalten das folgende Ergebnis.

```
{
  "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>

Wählen Sie im Schema-Editor in der Konsole auf der rechten Seite die Option `testdb` neben. `listTodos(id: ID!): Todo` Der Request-Handler verwendet die Select Utility-Funktion, um eine Anforderung zur Laufzeit dynamisch zu erstellen.

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

Wir möchten `todos` nach dem `due` Datum filtern. Lassen Sie uns den Resolver aktualisieren, in den `due` Werte umgewandelt werden sollen`DATE`. Aktualisieren Sie die Liste der Importe und den Request-Handler wie folgt.

```
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 };
}
```

Gehen Sie im **Query-Editor** wie folgt vor.

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

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

Sie können auch ein. `update` `Todo` Lassen Sie uns im **Query-Editor** unser erstes `Todo` Element von aktualisieren `id``1`.

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

Beachten Sie, dass Sie das `id` Element angeben müssen, das Sie aktualisieren möchten. Sie können auch eine Bedingung angeben, um nur ein Element zu aktualisieren, das bestimmte Bedingungen erfüllt. Beispielsweise möchten wir das Element möglicherweise nur bearbeiten, wenn die Beschreibung `edits` wie folgt beginnt.

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

Genau wie wir mit unseren `list` Operationen `create` und umgegangen sind, können wir unseren Resolver so aktualisieren, dass er das `due` Feld in a `DATE` umwandelt. Speichern Sie diese Änderungen `updateTodo` wie folgt.

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

Versuchen Sie jetzt ein Update mit einer Bedingung:

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

### Mutation.DeleteTodo
<a name="deletetodo"></a>

Du kannst `delete` mit der Mutation ein. `Todo` `deleteTodo` Dies funktioniert wie die `updateTodo` Mutation, und Sie müssen das `id` Element, das Sie löschen möchten, wie folgt angeben.

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

### Schreiben von benutzerdefinierten Abfragen
<a name="writing-custom-queries"></a>

Wir haben die `rds` Modul-Dienstprogramme verwendet, um unsere SQL-Anweisungen zu erstellen. Wir können auch unsere eigene benutzerdefinierte statische Anweisung schreiben, um mit unserer Datenbank zu interagieren. Aktualisieren Sie zunächst das Schema, um das `id` Feld aus der `CreateTask` Eingabe zu entfernen.

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

Erstellen Sie als Nächstes einige Aufgaben. Eine Aufgabe hat `Todo` wie folgt eine Fremdschlüsselbeziehung.

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

Erstellen Sie ein neues Feld Ihres `Query` Typs mit dem folgenden Namen`getTodoAndTasks`.

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

Fügen Sie dem `Todo` Typ wie folgt ein `tasks` Feld hinzu.

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

Speichern Sie das Schema. Wählen Sie im Schema-Editor in der Konsole auf der rechten Seite **Attach Resolver** for `getTodosAndTasks(id: Int!): Todo` aus. Wählen Sie Ihre Amazon RDS-Datenquelle. Aktualisieren Sie Ihren Resolver mit dem folgenden Code.

```
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;
}
```

In diesem Code verwenden wir die `sql` Tag-Vorlage, um eine SQL-Anweisung zu schreiben, an die wir zur Laufzeit sicher einen dynamischen Wert übergeben können. `createPgStatement`kann bis zu zwei SQL-Anfragen gleichzeitig annehmen. Wir verwenden das, um eine Anfrage für uns `todo` und eine weitere für unsere zu senden`tasks`. Sie hätten dies mit einer `JOIN` Anweisung oder einer anderen Methode tun können. Die Idee ist, Ihre eigene SQL-Anweisung schreiben zu können, um Ihre Geschäftslogik zu implementieren. Gehen Sie wie folgt vor, um die **Abfrage** im Query-Editor zu verwenden.

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

## Löschen Sie Ihren Cluster
<a name="rds-delete-cluster"></a>

**Wichtig**  
Das Löschen eines Clusters ist dauerhaft. Überprüfen Sie Ihr Projekt gründlich, bevor Sie diese Aktion ausführen.

Um Ihren Cluster zu löschen:

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