Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mutation to create relations on AWS AppSync

I have been trying to run a mutation to create relations to two separate Types with not much success.

** SCHEMA **

(I have used "Create Resources" to create tables in DynamoDB)

type Comment {
    eventId: ID!
    commentId: String!
    content: String
}

type CommentConnection {
    items: [Comment]
    nextToken: String
}

input CreateCommentInput {
    eventId: ID!
    commentId: String!
    content: String
}

input CreateEventInput {
    id: ID!
    name: String
    where: String
    when: String
    description: String
}

input DeleteCommentInput {
    eventId: ID!
}

input DeleteEventInput {
    id: ID!
}

type Event {
    id: ID!
    name: String
    where: String
    when: String
    description: String
    comments(limit: Int, nextToken: String): CommentConnection
}

type EventConnection {
    items: [Event]
    nextToken: String
}

type Mutation {
    createEvent(input: CreateEventInput!): Event
    updateEvent(input: UpdateEventInput!): Event
    deleteEvent(input: DeleteEventInput!): Event
    createComment(input: CreateCommentInput!): Comment
    updateComment(input: UpdateCommentInput!): Comment
    deleteComment(input: DeleteCommentInput!): Comment
    commentOnEvent(input: commentOnEventInput!): Comment
}

type Query {
    fetchEvent(id: ID!): Event
    getEvent(id: ID!): Event
    listEvents(first: Int, after: String): EventConnection
    getComment(eventId: ID!): Comment
    listComments(first: Int, after: String): CommentConnection
}

type Subscription {
    onCreateEvent(
        id: ID,
        name: String,
        where: String,
        when: String,
        description: String
    ): Event
        @aws_subscribe(mutations: ["createEvent"])
    onUpdateEvent(
        id: ID,
        name: String,
        where: String,
        when: String,
        description: String
    ): Event
        @aws_subscribe(mutations: ["updateEvent"])
    onDeleteEvent(
        id: ID,
        name: String,
        where: String,
        when: String,
        description: String
    ): Event
        @aws_subscribe(mutations: ["deleteEvent"])
    onCreateComment(eventId: ID, commentId: String, content: String): Comment
        @aws_subscribe(mutations: ["createComment"])
    onUpdateComment(eventId: ID, commentId: String, content: String): Comment
        @aws_subscribe(mutations: ["updateComment"])
    onDeleteComment(eventId: ID, commentId: String, content: String): Comment
        @aws_subscribe(mutations: ["deleteComment"])
}

input UpdateCommentInput {
    eventId: ID!
    commentId: String
    content: String
}

input UpdateEventInput {
    id: ID!
    name: String
    where: String
    when: String
    description: String
}

input commentOnEventInput {
    eventId: ID!
    content: String
}

schema {
    query: Query
    mutation: Mutation
    subscription: Subscription
}

** MUTATIONS **

mutation #1:

mutation {
    createEvent(input: {
    id: "id8888"
        name: "some event"
    where: "Tokyo"
        when: "tomorrow"
        description: "desc for event"
  })
  {
    id
        name
    }
}

mutation #1 gives:

{
  "data": {
    "createEvent": {
      "id": "id8888",
      "name": "some event"
    }
  }
}

mutation #2:

mutation {
  commentOnEvent(input : {
    eventId: "id8888"
    commentId: "id2222"
    content: "some content"
  })
  {
    commentId
    content
  }
}

mutation #2 gives:

{
  "data": {
    "commentOnEvent": null
  }
}

In the React sample created by AWS AppSync creates commentId automatically but I can't recreate that in manually created schema and resource.

I would like to know how I can establish relations on separate types and query them. Has anybody successfully done this??

like image 547
Takeshi Amano Avatar asked Jan 02 '23 03:01

Takeshi Amano


1 Answers

Starting from the console's “Create resources” functionality lets talk through how this works. Assume we have this schema.

type Comment {
    eventId: ID!
    commentId: String!
    content: String
}

type CommentConnection {
    items: [Comment]
    nextToken: String
}

input CreateCommentInput {
    eventId: ID!
    commentId: String!
    content: String
}

input CreateEventInput {
    id: ID!
    name: String
    where: String
    when: String
    description: String
}

input DeleteCommentInput {
    eventId: ID!
}

input DeleteEventInput {
    id: ID!
}

type Event {
    id: ID!
    name: String
    where: String
    when: String
    description: String
    comments(limit: Int, nextToken: String): CommentConnection
}

type EventConnection {
    items: [Event]
    nextToken: String
}

type Mutation {
    createEvent(input: CreateEventInput!): Event
    updateEvent(input: UpdateEventInput!): Event
    deleteEvent(input: DeleteEventInput!): Event
    createComment(input: CreateCommentInput!): Comment
    updateComment(input: UpdateCommentInput!): Comment
    deleteComment(input: DeleteCommentInput!): Comment
}

type Query {
    getEvent(id: ID!): Event
    listEvents(first: Int, after: String): EventConnection
    getComment(eventId: ID!): Comment
    listComments(first: Int, after: String): CommentConnection
}

type Subscription {
    onCreateEvent(
        id: ID,
        name: String,
        where: String,
        when: String,
        description: String
    ): Event
        @aws_subscribe(mutations: ["createEvent"])
    onUpdateEvent(
        id: ID,
        name: String,
        where: String,
        when: String,
        description: String
    ): Event
        @aws_subscribe(mutations: ["updateEvent"])
    onDeleteEvent(
        id: ID,
        name: String,
        where: String,
        when: String,
        description: String
    ): Event
        @aws_subscribe(mutations: ["deleteEvent"])
    onCreateComment(eventId: ID, commentId: String, content: String): Comment
        @aws_subscribe(mutations: ["createComment"])
    onUpdateComment(eventId: ID, commentId: String, content: String): Comment
        @aws_subscribe(mutations: ["updateComment"])
    onDeleteComment(eventId: ID, commentId: String, content: String): Comment
        @aws_subscribe(mutations: ["deleteComment"])
}

input UpdateCommentInput {
    eventId: ID!
    commentId: String
    content: String
}

input UpdateEventInput {
    id: ID!
    name: String
    where: String
    when: String
    description: String
}

schema {
    query: Query
    mutation: Mutation
    subscription: Subscription
}

This is what the schema should look like after running Create Resources on the Event and Comment types. When going through the "Create Resources" flow with the Comment type you should choose the eventId as the table's hash key and the commentId as the sort key. For the Event type you can leave "id" as the single hash key. So what did that do for us?

First it created 2 DynamoDB tables to hold our objects of type Event and Comment. It then imported those tables as AppSync data sources and generated new schema parts including input objects, objects, and query and mutation fields and saved them to the schema. It also wired up resolvers specific to the new table you just defined and attached them to the newly generated query and mutation fields that implement common CRUD patterns. Unfortunately this does not yet understand relations so we have to add those ourselves. To do that let's first make the mutation to create relations as you have asked about and for completeness we will do a query as well.

As you have already done, you are going to need to add something like this to your schema

type Mutation {
  commentOnEvent(input: CommentOnEventInput!): Comment
}
input CommentOnEventInput {
  eventId: ID!
  content: String
}

Save the schema and then click "Attach" on the Mutation.commentOnEvent field to add a resolver. Select the CommentTable data source we created earlier and from the mapping template put this:

{
    "version" : "2017-02-28",
    "operation" : "PutItem",
    "key" : {
      "eventId": $util.dynamodb.toDynamoDBJson($ctx.args.input.eventId),
      "commentId": $util.dynamodb.toDynamoDBJson($util.autoId()),
    },
    "attributeValues" : $util.dynamodb.toMapValuesJson($ctx.args.input)
}

and for the response mapping template

$util.toJson($context.result)

Click save. Now you should be able to run a query like this:

mutation {
  commentOnEvent(input: { eventId: "***", content: "A comment"}) {
    eventId
    content
  }
}

Let's now add away to read data via a relation. E.G. I want to be able to run a query like this:

query {
  getEvent(id: "***") {
    id
    comments(first: 5) {
      items {
        content
      }
    }
  }
}

To do this lets first add the following parts to the schema.

type Event {
  # add this to existing fields
  comments(first: Int, after: String): CommentConnection
}

Click save then click "Attach" on the Event.comments field. Select the CommentTable data source again and then provide the following for the request mapping template.

# Event.comments.request.vtl
{
    "version" : "2017-02-28",
    "operation" : "Query",
    "query" : {
        "expression": "eventId = :eventId",
        "expressionValues" : {
            ":eventId" : {
                "S" : "${ctx.source.id}"
            }
        }
    },
    "limit": $util.defaultIfNull(${ctx.args.first}, 20),
    "nextToken": $util.toJson($util.defaultIfNullOrBlank($ctx.args.after, null))
} 

Notice the $ctx.source.id. Since we are resolving the Event.comments field, the $ctx.source is the instance of the Event type that we are resolving comments for. In effect this makes it so that anywhere we include { comments { ... } in a selection set on the Event type, only comments for the parent event will be fetched. And then you can return the paginated result object.

# Event.comments.response.vtl
# $ctx.result = { items: [...], nextToken: "..." }
$util.toJson($ctx.result)

This should do the trick. Now you can run both these queries and see the results.

mutation {
  commentOnEvent(input: { eventId: "***", content: "A comment"}) {
    eventId
    content
  }
}


query {
  getEvent(id: "***") {
    id
    comments(first: 5) {
      items {
        content
      }
    }
  }
}

Hope this helps.

like image 57
mparis Avatar answered Jan 13 '23 21:01

mparis