home.social

#appsync — Public Fediverse posts

Live and recent posts from across the Fediverse tagged #appsync, aggregated by home.social.

  1. AppSync subscriptions: waiting for start_ack can still result in missing events

    It seems like that when AppSync returns a `start_ack` message in response to a subscription `start` it won't necessarily mean that all future events will be delivered.

    Subscriptions are the mechanism to deliver real-time events from AppSync. It is based on WebSockets and its protocol is documented [here](docs.aws.amazon.com/appsync/la).

    In the protocol, a client needs to send a `start` message with the GraphQL query to start receiving updates. Then AppSync responds with a `start_ack` if everything is OK and then sends `data` events whenever an update happens.

    Reading the documentation my impression was that `start_ack` is the moment when the subscription is live and all future events will be delivered. But what I'm seeing is that **it's not the case**. Even when the event is strictly triggered after the `start_ack` is received sometimes it is not delivered to the client.

    Why is it a problem?

    A common pattern for APIs with real-time updates is to subscribe to updates first then query the current state. This way there is no "temporal dead zone" when updates are lost. But that requires a definitive *point in time* when the subscription is live. Without that, it's only best-effort and messages will be lost every now and then especially in cases when the subscription is made just before the event, common in tests and some async workflows.

    Real-time updates, especially in AppSync, is a complex topic and it's easy to get wrong. I've [written about it before](advancedweb.hu/shorts/apollos-), it has a [separate section in my book](graphql-on-aws-appsync-book.co), and I even [made a library](github.com/sashee/appsync-subs) because I wasn't particularly happy with the AWS-provided one.

    I noticed tests using subscriptions timeouting for a long time now, but i wrote it off as "something complex is happening" and added some retries to handle it. A message is published to IoT Core that triggers a Lambda, that writes to DynamoDB then it triggers the subscription. A lot can go wrong so it's realistic that the 10-ish seconds sometimes pass.

    But I then started working on a simpler setup and still noticed that some events seemingly never arrive. This time I could pinpoint the issue because if a parallel subscription is opened before then the event is delivered there. So the problem must be that the subscription is not live even though AppSync says it is.

    Hopefully, it will get fixed soon.

    Bug report opened [in the AppSync repo](github.com/aws/aws-appsync-com).

    #aws #appsync

    Originally published [on my blog](advancedweb.hu/shorts/appsync-)

  2. AppSync subscriptions: waiting for start_ack can still result in missing events

    It seems like that when AppSync returns a `start_ack` message in response to a subscription `start` it won't necessarily mean that all future events will be delivered.

    Subscriptions are the mechanism to deliver real-time events from AppSync. It is based on WebSockets and its protocol is documented [here](docs.aws.amazon.com/appsync/la).

    In the protocol, a client needs to send a `start` message with the GraphQL query to start receiving updates. Then AppSync responds with a `start_ack` if everything is OK and then sends `data` events whenever an update happens.

    Reading the documentation my impression was that `start_ack` is the moment when the subscription is live and all future events will be delivered. But what I'm seeing is that **it's not the case**. Even when the event is strictly triggered after the `start_ack` is received sometimes it is not delivered to the client.

    Why is it a problem?

    A common pattern for APIs with real-time updates is to subscribe to updates first then query the current state. This way there is no "temporal dead zone" when updates are lost. But that requires a definitive *point in time* when the subscription is live. Without that, it's only best-effort and messages will be lost every now and then especially in cases when the subscription is made just before the event, common in tests and some async workflows.

    Real-time updates, especially in AppSync, is a complex topic and it's easy to get wrong. I've [written about it before](advancedweb.hu/shorts/apollos-), it has a [separate section in my book](graphql-on-aws-appsync-book.co), and I even [made a library](github.com/sashee/appsync-subs) because I wasn't particularly happy with the AWS-provided one.

    I noticed tests using subscriptions timeouting for a long time now, but i wrote it off as "something complex is happening" and added some retries to handle it. A message is published to IoT Core that triggers a Lambda, that writes to DynamoDB then it triggers the subscription. A lot can go wrong so it's realistic that the 10-ish seconds sometimes pass.

    But I then started working on a simpler setup and still noticed that some events seemingly never arrive. This time I could pinpoint the issue because if a parallel subscription is opened before then the event is delivered there. So the problem must be that the subscription is not live even though AppSync says it is.

    Hopefully, it will get fixed soon.

    Bug report opened [in the AppSync repo](github.com/aws/aws-appsync-com).

    #aws #appsync

    Originally published [on my blog](advancedweb.hu/shorts/appsync-)

  3. GraphQL tip: Have the subscription types contain all filterable fields on the top-level.

    For example, a Todo event that should allow filtering by user, group, severity, and id, should have these as well as the item itself:

    ```graphql
    type TodoEvent {
    userId: ID!
    groupId: ID!
    todoId: ID!
    severity: Severity!
    todo: Todo
    }
    ```

    Originally, it was the only way for filtering in AppSync, but that changed when it [started supporting filters on nested fields](docs.aws.amazon.com/appsync/la). So, why it's still a best practice?

    The primary reason is deletion: this structure makes it easy to support sending an event when the item is deleted and all the filters will work.

    Then an additional benefit is when there is a filter that is not present in the entity itself. For example, if a Todo item's `userId` can be changed it is useful to notify the original owner. In that case, a `prevUserId` field can be added to the subscription allowing users to subscribe to events when an item is removed from them.

    #graphql #tips #appsync

    Originally published [on my blog](advancedweb.hu/shorts/graphql-)

  4. GraphQL tip: Have the subscription types contain all filterable fields on the top-level.

    For example, a Todo event that should allow filtering by user, group, severity, and id, should have these as well as the item itself:

    ```graphql
    type TodoEvent {
    userId: ID!
    groupId: ID!
    todoId: ID!
    severity: Severity!
    todo: Todo
    }
    ```

    Originally, it was the only way for filtering in AppSync, but that changed when it [started supporting filters on nested fields](docs.aws.amazon.com/appsync/la). So, why it's still a best practice?

    The primary reason is deletion: this structure makes it easy to support sending an event when the item is deleted and all the filters will work.

    Then an additional benefit is when there is a filter that is not present in the entity itself. For example, if a Todo item's `userId` can be changed it is useful to notify the original owner. In that case, a `prevUserId` field can be added to the subscription allowing users to subscribe to events when an item is removed from them.

    #graphql #tips #appsync

    Originally published [on my blog](advancedweb.hu/shorts/graphql-)

  5. GraphQL tip: Only make fields required if they are present for all users

    In GraphQL it's possible to mark a field as required with the `!`. For example, a `User` always have a `Project` according to this schema:

    ```graphql
    type User {
    name: String!
    email: String!
    project: Project!
    }
    ```

    Marking fields required is a powerful tool in the GraphQL toolbox as clients won't need handle the case when the field is null. But it can also lead to a cascading null effect.

    For example, let's say the Project has a `client` field that also can't be null:

    ```graphql
    type User {
    name: String!
    email: String!
    project: Project!
    tickets: [Ticket!]!
    }

    type Project {
    name: String!
    description: String!
    client: String!
    }

    // + Ticket
    ```

    Let's say the `Project.client` is considered sensitive information and will be removed for non-admin users.

    Then a query that gets the `User`, its `Project`, and and then the `client` field will fail to return any data:

    ```graphql
    query {
    user(id: "user1") {
    name
    email
    project {
    name
    description
    client
    }
    tickets: {
    id
    }
    }
    }
    ```

    The result:

    ```json
    {
    "data": null,
    "errors": ...
    }
    ```

    Because of this, be mindful which fields you mark as required. If a field can be null for some users during normal operations, such as when you have different access levels, it should be optional. A field should be marked as mandatory only when it returns a value in every case.

    #appsync #graphql

    Originally published [on my blog](advancedweb.hu/shorts/graphql-)

  6. GraphQL tip: Only make fields required if they are present for all users

    In GraphQL it's possible to mark a field as required with the `!`. For example, a `User` always have a `Project` according to this schema:

    ```graphql
    type User {
    name: String!
    email: String!
    project: Project!
    }
    ```

    Marking fields required is a powerful tool in the GraphQL toolbox as clients won't need handle the case when the field is null. But it can also lead to a cascading null effect.

    For example, let's say the Project has a `client` field that also can't be null:

    ```graphql
    type User {
    name: String!
    email: String!
    project: Project!
    tickets: [Ticket!]!
    }

    type Project {
    name: String!
    description: String!
    client: String!
    }

    // + Ticket
    ```

    Let's say the `Project.client` is considered sensitive information and will be removed for non-admin users.

    Then a query that gets the `User`, its `Project`, and and then the `client` field will fail to return any data:

    ```graphql
    query {
    user(id: "user1") {
    name
    email
    project {
    name
    description
    client
    }
    tickets: {
    id
    }
    }
    }
    ```

    The result:

    ```json
    {
    "data": null,
    "errors": ...
    }
    ```

    Because of this, be mindful which fields you mark as required. If a field can be null for some users during normal operations, such as when you have different access levels, it should be optional. A field should be marked as mandatory only when it returns a value in every case.

    #appsync #graphql

    Originally published [on my blog](advancedweb.hu/shorts/graphql-)

  7. AppSync footgun: util.error does not work if called with non-string arguments

    The `util.error` function terminates the current resolver with the error provided in the argument, similar to how an exception would work. Its signature is:

    ```
    util.error(String, String?, Object?, Object?)
    ```

    But what happens when the provided arguments don't match this signature?

    Consider this code:

    ```js
    if (ctx.result.statusCode < 200 || ctx.result.statusCode >= 300) {
    util.error(ctx.result.statusCode);
    }
    return JSON.parse(ctx.result.body);
    ```

    The expectation here is that if the `statusCode` is outside the 2xx range then it will result in an error.

    But what happens instead is that the `util.error` is silently ignored. There is no error, no warning, it behaves as it was never called.

    All these lines are no-ops:

    ```js
    util.error(25);
    util.error(true);
    util.error("abc", 25);
    ```

    Issue opened at [github.com/aws/aws-appsync-com.

    #aws #appsync

    Originally published [on my blog](advancedweb.hu/shorts/appsync-)

  8. AppSync footgun: util.error does not work if called with non-string arguments

    The `util.error` function terminates the current resolver with the error provided in the argument, similar to how an exception would work. Its signature is:

    ```
    util.error(String, String?, Object?, Object?)
    ```

    But what happens when the provided arguments don't match this signature?

    Consider this code:

    ```js
    if (ctx.result.statusCode < 200 || ctx.result.statusCode >= 300) {
    util.error(ctx.result.statusCode);
    }
    return JSON.parse(ctx.result.body);
    ```

    The expectation here is that if the `statusCode` is outside the 2xx range then it will result in an error.

    But what happens instead is that the `util.error` is silently ignored. There is no error, no warning, it behaves as it was never called.

    All these lines are no-ops:

    ```js
    util.error(25);
    util.error(true);
    util.error("abc", 25);
    ```

    Issue opened at [github.com/aws/aws-appsync-com.

    #aws #appsync

    Originally published [on my blog](advancedweb.hu/shorts/appsync-)

  9. New AWS::AppSync::ChannelNamespace

    Use the AWS::AppSync::ChannelNamespace resource to creates a channel namespace associated with an Api.
    docs.aws.amazon.com/AWSCloudFo #appsync #cloudformation

  10. New AWS::AppSync::ChannelNamespace

    Use the AWS::AppSync::ChannelNamespace resource to creates a channel namespace associated with an Api.
    docs.aws.amazon.com/AWSCloudFo #appsync #cloudformation

  11. New AWS::AppSync::ChannelNamespace

    Use the AWS::AppSync::ChannelNamespace resource to creates a channel namespace associated with an Api.
    docs.aws.amazon.com/AWSCloudFo #appsync #cloudformation

  12. New AWS::AppSync::ChannelNamespace

    Use the AWS::AppSync::ChannelNamespace resource to creates a channel namespace associated with an Api.
    docs.aws.amazon.com/AWSCloudFo #appsync #cloudformation

  13. New AWS::AppSync::ChannelNamespace

    Use the AWS::AppSync::ChannelNamespace resource to creates a channel namespace associated with an Api.
    docs.aws.amazon.com/AWSCloudFo #appsync #cloudformation

  14. New AWS::AppSync::Api

    Use the AWS::AppSync::Api resource to create an AWS AppSync API that you can use for an AWS AppSync API with your preferred configuration,.
    docs.aws.amazon.com/AWSCloudFo #appsync #cloudformation

  15. New AWS::AppSync::Api

    Use the AWS::AppSync::Api resource to create an AWS AppSync API that you can use for an AWS AppSync API with your preferred configuration,.
    docs.aws.amazon.com/AWSCloudFo #appsync #cloudformation

  16. New AWS::AppSync::Api

    Use the AWS::AppSync::Api resource to create an AWS AppSync API that you can use for an AWS AppSync API with your preferred configuration,.
    docs.aws.amazon.com/AWSCloudFo #appsync #cloudformation

  17. New AWS::AppSync::Api

    Use the AWS::AppSync::Api resource to create an AWS AppSync API that you can use for an AWS AppSync API with your preferred configuration,.
    docs.aws.amazon.com/AWSCloudFo #appsync #cloudformation

  18. New AWS::AppSync::Api

    Use the AWS::AppSync::Api resource to create an AWS AppSync API that you can use for an AWS AppSync API with your preferred configuration,.
    docs.aws.amazon.com/AWSCloudFo #appsync #cloudformation

  19. #AppSync to #DynamoDB with JS resolvers seems like a really nice idea but I just keep hitting walls with it.

    #AWS

  20. #AppSync to #DynamoDB with JS resolvers seems like a really nice idea but I just keep hitting walls with it.

    #AWS

  21. #AppSync to #DynamoDB with JS resolvers seems like a really nice idea but I just keep hitting walls with it.

    #AWS

  22. #AppSync to #DynamoDB with JS resolvers seems like a really nice idea but I just keep hitting walls with it.

    #AWS

  23. #AppSync to #DynamoDB with JS resolvers seems like a really nice idea but I just keep hitting walls with it.

    #AWS

  24. The main problem I'm working through is my primary key can change, so I'm trying to make sure I can still update it in the future by having a static guid as an attribute in the tables.

    I'm amazed there's no nice way for #AppSync to query a table with multiple IDs. The get batch item support isn't great as it requires the table name to be hard coded in the resolved, which you can't do if you want dev and prod envs.

    #AWS

  25. The main problem I'm working through is my primary key can change, so I'm trying to make sure I can still update it in the future by having a static guid as an attribute in the tables.

    I'm amazed there's no nice way for #AppSync to query a table with multiple IDs. The get batch item support isn't great as it requires the table name to be hard coded in the resolved, which you can't do if you want dev and prod envs.

    #AWS

  26. The main problem I'm working through is my primary key can change, so I'm trying to make sure I can still update it in the future by having a static guid as an attribute in the tables.

    I'm amazed there's no nice way for #AppSync to query a table with multiple IDs. The get batch item support isn't great as it requires the table name to be hard coded in the resolved, which you can't do if you want dev and prod envs.

    #AWS

  27. The main problem I'm working through is my primary key can change, so I'm trying to make sure I can still update it in the future by having a static guid as an attribute in the tables.

    I'm amazed there's no nice way for #AppSync to query a table with multiple IDs. The get batch item support isn't great as it requires the table name to be hard coded in the resolved, which you can't do if you want dev and prod envs.

    #AWS

  28. The main problem I'm working through is my primary key can change, so I'm trying to make sure I can still update it in the future by having a static guid as an attribute in the tables.

    I'm amazed there's no nice way for #AppSync to query a table with multiple IDs. The get batch item support isn't great as it requires the table name to be hard coded in the resolved, which you can't do if you want dev and prod envs.

    #AWS

  29. I have an #AppSync API that I use to put and store user generated data that is authenticated using #Cognito. As part of the JWT payload there is a device ID that is unique to the signed in user on that device.

  30. I have an #AppSync API that I use to put and store user generated data that is authenticated using #Cognito. As part of the JWT payload there is a device ID that is unique to the signed in user on that device.

  31. I have an #AppSync API that I use to put and store user generated data that is authenticated using #Cognito. As part of the JWT payload there is a device ID that is unique to the signed in user on that device.

  32. AppSync supports long running events with asynchronous Lambda function invocations
    aws.amazon.com/about-aws/whats
    Previously, customers could only invoke Lambda functions synchronously from AppSync, which meant that the GraphQL API would wait for the Lambda function to complete before returning a response. With support for Event mode, AppSync can now trigger Lambda functions asynchronously, decoupling the API response from the Lambda execution.
    #AWS #AppSync

  33. #AWS just revealed that Amazon EventBridge Event Bus supports AWS AppSync as an Event Bus's target.

    Developers can now stream real-time updates like sports scores from their apps to frontend applications, whether on mobile or desktop: bit.ly/3HtzZo0

    #InfoQ #EventBridge #AppSync #CloudComputing #EventDrivenArchitecture

  34. #AWS just revealed that Amazon EventBridge Event Bus supports AWS AppSync as an Event Bus's target.

    Developers can now stream real-time updates like sports scores from their apps to frontend applications, whether on mobile or desktop: bit.ly/3HtzZo0

    #InfoQ #EventBridge #AppSync #CloudComputing #EventDrivenArchitecture

  35. #AWS just revealed that Amazon EventBridge Event Bus supports AWS AppSync as an Event Bus's target.

    Developers can now stream real-time updates like sports scores from their apps to frontend applications, whether on mobile or desktop: bit.ly/3HtzZo0

    #InfoQ #EventBridge #AppSync #CloudComputing #EventDrivenArchitecture

  36. #AWS just revealed that Amazon EventBridge Event Bus supports AWS AppSync as an Event Bus's target.

    Developers can now stream real-time updates like sports scores from their apps to frontend applications, whether on mobile or desktop: bit.ly/3HtzZo0

    #InfoQ #EventBridge #AppSync #CloudComputing #EventDrivenArchitecture