AWS AppSync over websockets for query, mutation, and subscription (no REST)? - websocket

I've been successful with Apollo Server + graphql-ws for using a websocket as the entire network transport (all queries, mutations, and subscriptions). Is this possible to do with AWS AppSync? Or does AWS AppSync only allow use of the websocket for subscriptions and all queries/mutations go via REST requests?
Allowing queries and mutations to go over the websocket (which is already connected for subscriptions) opens up a lot of doors for very quick two way communication between web and mobile clients.

Related

Unable to establish a websocket connection for GraphQL subscription

I am trying to implement a GraphQL WebSocket-based #subscription on a server (using NestJS #subscription). The server is hosted on an AWS ECS and is behind an ALB.
We currently have an AWS API GW connection via VPC-link to our ALB.
I tried to build a dedicated Websocket API GW with the same VPC link we use in the HTTP API GW.
I also tried to spin up a new NLB (Network Load Balancer) over our ECS and a new REST VPC link to be used in the dedicated Websocket API GW.
The client and server are communicating over a graphql-transport-ws sub-protocol using graphql-ws library and the communication is working fine on a localhost setup.
When running the following command on our local host I am able to establish a web socket connection:
wscat -c ws://localhost:3000/graphql -s graphql-transport-ws
When running the same against the WebSocket API GW URL
wscat -c wss://*****.execute-api.*****.amazonaws.com/**** -s graphql-transport-ws
I’m getting this:
error: Server sent no subprotocol
The error indicates a problem with the sub-protocol so when removing the sub-protocol a connection is established and I am getting a prompt:
Connected (press CTRL+C to quit)
>
However, there’s no indication of reaching the server and it seems like the connection is only made with the WebSocket API GW itself.
When I circumvent the gateway and directly connect an internet-facing NLB I'm able to establish a WebSocket connection.
I am not a super Websocket expert, but I understand WebSocket connections will be terminated by the API Gateway and cannot be used as a connection pass-through. You can forward web socket events using AWS_PROXY integration to a graphQL server backend, BUT it's not a maintained direct connection - API Gateway terminates and events towards the backend integration and will not return the integration response to the WebSocket since it is event-driven and not a connection-oriented service - hence the “error: Server sent no subprotocol” you are seeing.
So to use API GW as the WebSocket layer, you would need to build out connection management functionality somewhere to manage the event-based nature of the APIGW and send out data to the APIGW connections or adjust the integration mechanism within the graphql server to utilise the #connection functionality to send responses/notifications to WebSocket consumers.
Integrating Backend Service documentation:
https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-routes-integrations.html
Sending responses to a connected client:
https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-how-to-call-websocket-api-connections.html
API GW Websockets are great for building custom solutions but take some effort since you will be configuring the setup for the events.
For a GraphQL API on AWS - I would recommend taking a look at AppSync, which is an AWS Managed GraphQL service - it handles GraphQL subscriptions via WebSockets natively and with zero additional code and its highly scalable out of the box and would simplify the GraphQL hosting burden of an ECS based solution.
I suspect there may be a lot of other reasons for the need to build out using existing GraphQL on ECS, so understand it's not always possible to pivot to something like AppSync. I feel the NLB solution you tried is okay within the existing ECS backend landscape and, as you have noted, is connection-oriented (via NLB), so will achieve the outcome you are after.

Websocket connection using AWS Lambda + API Gateway

I have a react application and I would like to setup a websocket connection to my backend for some realtime updates. I was going to deploy an EC2 or ECS-cluster to host websocket connections. Then I stumbled into some articles showing how websocket connection can be setup in a serverless manner.
One example: https://medium.com/#likhita507/real-time-chat-application-using-webscockets-in-apigateway-e3ed759c4740
However, I can't seem to figure out how this works for a few reasons.
Lambda has a max runtime of 15 min
How does the backend establish a connection when no lambda is running and the backend wants to invoke a message to the frontend
Does this entail that I have to keep a lambda alive all the time, if so, it no longer feels like a good idea. In the above example, what I can't grasp is that when creating that chat application, can each chat room only exist for 15 min? And if a user disconnects from the room, how will that user be updated on new messages.
Does anyone have any experience with this kind of solution?
It's the API Gateway that keeps the websocket connection alive. The browser (or whatever your client is) is connecting to the Gateway, not the lambda function.
The gateway triggers the Lambda function. You hook this up by selecting LAMBDA_PROXY from Integration Request. You can connect each route to a separate function, or have them all dealt with by one, whichever you prefer. Unless you're doing something very complicated in the function, it should only be executing for a few ms.
Communicating from the function to the original client is done through the gateway too - with APIGatewayManagementAPI.postToConnection (or you could roll your own http version using the connection URL I guess).

Apollo-Server GraphQL Subscription on External WebSocket Server

So I have a bit of a question since I'm having a hard time wrapping my head around it. Currently I have a GraphQL API Server created using Apollo-Server and persisted using a local sqlite database. I have the queries and mutations working correctly.
I also have an external WebSocket server that constantly has messages (that match my GraphQL/Database schema) produced to it at say ws://localhost:8000/websocket. Is it possible to have my GraphQL Server subscribe to that websocket address and constantly parse those messages and use the appropriate mutation to insert into the backend database?
I would then have a Vue frontend that would constantly display the results (via Vue Apollo Clients WS subscription maybe?)
to have my GraphQL Server subscribe to that websocket address and constantly parse those messages
Typically no, its the other way around - you can make ws server to call graphql server to do the mutations. That is if you want to use WS as the primary transport layer for everything - queries, mutations & subscriptions.
But usually architecture separates queries & mutations because they are more stateless and critical from subscriptions which are more stateful (persisted connection)
client -> queries & mutations -> graphql server --> redis pubsub -+
|
client <--> subscriptions <-- graphql subscription server <-------+
(in simpler cases when you don't need high load, you can combine both servers to use in-memory pubsub)
BUT, if you very much want to, ofc you can write custom code to connect graphql server -> listen ws server in the background. See https://github.com/enisdenjo/graphql-ws#node-client for example
The problem can appear if you have some user context. You would need to either have custom connection where changes of all users happen. Or have a dedicated connection for every user
Yes , it can be done quite easily , just write a service worker or a worker thread that constantly checks for new messages
Can be done using worker_threads in node js
And if you need to implement it realtime
Make sure your worker thread starts a socket connection and is constantly connected to the port where you are publishing your messages
You can do it using socket.io library

With GraphQL is it possible to replace the websocket used for subscription with a message-based approach (e.g. MQ)

Whereas the corporate environment I am working in accepts the use of http(s) based request response patterns, which is OK for GraphQL Query and Mutation, they have issues with the use of websockets as needed for GraphQL Subscription and would prefer that the subscription is routed via IBM MQ.
Does anyone have any experience with this? I am thinking of using Apollo Server to serve up the GraphQL interface. Perhaps there is a front-end subscription solution that can be plugged in using IBM MQ? The back end data sources are Oracle databases.
Message queues are usually used to communicate between services while web sockets are how browsers can communicate with the server over a constant socket. This allows the server to send data to the client when a new event of a subscription arrived (classically browsers only supported "pull" and could only receive data when they asked for it). Browsers don't implement the MQ protocols you would need to directly subscribe to the MQ itself. I am not an expert on MQs but what is usually done is there is a subscription server that connects to the client via web socket. The subscription service then itself subscribes to the message queue and notifies relevant clients about their subscribed events. You can easily scale the subscription servers horizontally when you need additional resources.

Will each Graphql Subscription from same browser create one websocket connection?

I know what is graphql subscription.
My question is if each subscription will create one websocket connection?
Or all the subscription from each browser is combined to one websocket connection?
I couldn't find answer anywhere in document.
GraphQL itself purposefully does not specify a transport layer in the specification. Therfore the answer depends on the implementation that you are using but for the implementations it makes sense to have only one connection. In Apollo you can use apollo-link-ws to connect to the server. This link then creates (an keeps alive) a single socket to the server using subscriptions-transport-ws. It can also handle all GraphQL methods (not only subscriptions) using the web socket.

Resources