Skip to content

Appsync Events implementation for flutter SDK #6106

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
matta-pie opened this issue Apr 4, 2025 · 6 comments
Open

Appsync Events implementation for flutter SDK #6106

matta-pie opened this issue Apr 4, 2025 · 6 comments
Labels
feature-parity A request for a feature that is fully or partially available on another platform (JS, iOS, Android) feature-request A request for a new feature or an enhancement to an existing API or category. GraphQL API Issues related to the API (GraphQL) Category

Comments

@matta-pie
Copy link

Would it be possible to add the implementation for publishing/subscribing to AWS AppSync Events endpoints?

There's already an implementation for all javascript based frameworks
javascript doc

@github-actions github-actions bot added pending-triage This issue is in the backlog of issues to triage pending-maintainer-response Pending response from a maintainer of this repository labels Apr 4, 2025
@jamilsaadeh97
Copy link
Contributor

I would also add that this would be very useful for us if it would be supported out of the box

@tyllark
Copy link
Member

tyllark commented Apr 5, 2025

Hello @matta-pie, thanks for taking the time to open this issue. We will keep this open as a feature parity request, please give your description a thumbs to help us gauge community interest.

In the meantime you might be able to use our aws_signature_v4 package to call AWS APIs Amplify doesn't covered yet.

@github-actions github-actions bot removed the pending-maintainer-response Pending response from a maintainer of this repository label Apr 5, 2025
@tyllark tyllark added feature-parity A request for a feature that is fully or partially available on another platform (JS, iOS, Android) pending-maintainer-response Pending response from a maintainer of this repository GraphQL API Issues related to the API (GraphQL) Category feature-request A request for a new feature or an enhancement to an existing API or category. and removed pending-triage This issue is in the backlog of issues to triage pending-maintainer-response Pending response from a maintainer of this repository labels Apr 5, 2025
@matta-pie
Copy link
Author

@tyllark could you explain more about the alternative solution you proposed? in particular i need to subscribe to an appsync event (namespace/channel) using JWT auth. (i guess auth type doesn't change much). from what im seeing, your proposed solution could work to send messages, not subscribing to events.

@github-actions github-actions bot added the pending-maintainer-response Pending response from a maintainer of this repository label Apr 15, 2025
@tyllark
Copy link
Member

tyllark commented Apr 16, 2025

Hello @matta-pie, I used an API key to test the following code, but you should be able to update the authorization mapping to match you preferred subprotocol format as documented here. While I used an API key for simplicity I recommend using Authorization in your production frontend code.

'authorization': {
  'x-api-key': _apiKey,
  'host': _httpDomain,
},

Your DNS Endpoints (and API key) can be found at AWS Console -> AWS AppSync -> APIs -> [Your Event API] -> Settings. I got my AWSCredentials via the aws cli command aws sts get-session-token. Finally you will need add web_socket_channel: ^3.0.2 and uuid: ^4.5.1 to your pubspec.yaml.

final String _httpDomain = '[YOUR_HTTP_DOMAIN]';
final String _realtimeDomain = '[YOUR_REALTIME_DOMAIN]';
final String _apiKey = '[YOUR_API_KEY]';

Future<void> eventApiExample() async {
  String channel = 'default/myChannel';
  final events = [
    '{"event 1" : "data 1"}',
    '{"event 2" : "data 2"}',
    '{"event 3" : "data 3"}',
    '{"event 4" : "data 4"}',
  ];

  final websocket = await _createEventApiWebsocket(channel);
  websocket.stream.listen(_onEvent);

  await _callEventApiRest(channel, events);
}

void _onEvent(dynamic event) {
  safePrint('Event Recieved: $event');
}

Future<void> _callEventApiRest(String channel, List<String> events) async {
  final signer = AWSSigV4Signer(
    credentialsProvider: AWSCredentialsProvider(
      AWSCredentials(
        AwsConstants.accessKey,
        AwsConstants.secretAccessKey,
        AwsConstants.sessionToken,
        AwsConstants.expiration,
      ),
    ),
  );

  final scope = AWSCredentialScope(
    region: AwsConstants.region,
    service: AWSService.appSync,
  );

  final jsonBody = json.encode({'channel': channel, 'events': events});
  final request = AWSHttpRequest(
    method: AWSHttpMethod.post,
    headers: {
      AWSHeaders.contentType: 'application/json',
      'x-api-key': _apiKey,
    },
    uri: Uri.https(_httpDomain, '/event'),
    body: jsonBody.codeUnits,
  );

  final signedRequest = await signer.sign(request, credentialScope: scope);

  final operation = signedRequest.send();
  final response = await operation.response;
  final body = await response.decodeBody();

  safePrint(response.statusCode);
  safePrint(response.headers);
  safePrint(body);
}

Future<WebSocketChannel> _createEventApiWebsocket(String channel) async {
  final signer = AWSSigV4Signer(
    credentialsProvider: AWSCredentialsProvider(
      AWSCredentials(
        AwsConstants.accessKey,
        AwsConstants.secretAccessKey,
        AwsConstants.sessionToken,
        AwsConstants.expiration,
      ),
    ),
  );

  final scope = AWSCredentialScope(
    region: AwsConstants.region,
    service: AWSService.appSync,
  );

  final jsonBody = json.encode({'channel': channel});
  final request = AWSHttpRequest(
    method: AWSHttpMethod.get,
    headers: {
      AWSHeaders.contentType: 'application/json',
      'x-api-key': _apiKey,
      'host': _httpDomain,
    },
    uri: Uri(scheme: 'wss', host: _realtimeDomain, path: '/event/realtime'),
    body: jsonBody.codeUnits,
  );

  final uri = await signer.presign(
    request,
    credentialScope: scope,
    expiresIn: Duration(hours: 1),
  );

  final headerJson = jsonEncode({'x-api-key': _apiKey, 'host': _httpDomain});
  var header = base64.encode(utf8.encode(headerJson));
  header = header.replaceAll('+', '-');
  header = header.replaceAll('/', '-');
  header = header.replaceAll('=', '');

  final websocket = WebSocketChannel.connect(
    uri,
    protocols: ['aws-appsync-event-ws', 'header-$header'],
  );
  await websocket.ready;

  final uuid = Uuid();
  final subscribe = {
    'type': 'subscribe',
    'id': uuid.v4(),
    'channel': channel,
    'authorization': {
      'x-api-key': _apiKey,
      'host': _httpDomain,
    },
  };

  websocket.sink.add(jsonEncode(subscribe));

  final event = {
    'type': 'publish',
    'id': uuid.v4(),
    'channel': channel,
    'events': ['{"socket event 1" : "socket data 1"}',],
    'authorization': {
      'x-api-key': _apiKey,
      'host': _httpDomain,
    },
  };

  websocket.sink.add(jsonEncode(event));

  return websocket;
}

@github-actions github-actions bot removed the pending-maintainer-response Pending response from a maintainer of this repository label Apr 16, 2025
@abarone-btf
Copy link

is correct to expose the _apiKey on "frontend" code?, is it not sensitive?. Is this the prefered method? or do you recommend another one? @tyllark

@github-actions github-actions bot added the pending-maintainer-response Pending response from a maintainer of this repository label Apr 21, 2025
@tyllark
Copy link
Member

tyllark commented Apr 21, 2025

Hello @abarone-btf, for production frontend code I recommend using authorization rather than an API key, which you can read more about here. The code snippet I provided is only intended as a proof of concept for using Appsync Events with our Sigv4 package and should not be treated as production ready.

@github-actions github-actions bot removed the pending-maintainer-response Pending response from a maintainer of this repository label Apr 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-parity A request for a feature that is fully or partially available on another platform (JS, iOS, Android) feature-request A request for a new feature or an enhancement to an existing API or category. GraphQL API Issues related to the API (GraphQL) Category
Projects
None yet
Development

No branches or pull requests

4 participants