Why Sending a WebSocket Reply From Serverless Feels Weird
Cortlet is a software brand specailizing in: Lightweight, secure-first, no-bloat, problem-solving, and balance of opensource and closed source software.
With a normal HTTP API, the response path is obvious.
A client sends a request.
The server processes it.
The server returns a response.
That mental model is so familiar that developers barely think about it.
WebSockets on serverless infrastructure can feel very different.
A client sends a message over a WebSocket, the gateway triggers a serverless function, and then the developer discovers something strange:
Returning JSON from the function does not always mean that JSON goes back to the WebSocket client.
That is where the architecture starts feeling less intuitive.
HTTP has a natural request-response flow
A normal HTTP interaction looks like this:
Client → HTTP Request → Serverless Function → HTTP Response → Client
The response already knows where it needs to go.
The function returns data, the platform sends it back to the caller, and the interaction is finished.
WebSockets are different because the connection often lives somewhere outside the serverless function itself.
A simplified architecture might look like:
Client → WebSocket Gateway → Serverless Function
The gateway owns the persistent socket.
The function only receives an event.
That means the function does not necessarily own the response channel.
The return value may not be the socket response
Imagine a client sends:
{"action":"ping"}
The gateway forwards that event to a serverless function.
The function processes it and returns:
{"message":"pong"}
It would be reasonable to expect that response to travel back to the same client.
But in many serverless WebSocket setups, that is not the default behavior.
The function return value may simply indicate that the backend invocation completed successfully.
To actually send data back over the WebSocket, the backend may need to make a separate call to the gateway.
Conceptually, the flow becomes:
Client → WebSocket Gateway → Function
and then:
Function → Gateway Management API → Client
That is very different from ordinary HTTP.
Now the backend needs to know the connection
To send that second request, the backend usually needs some form of connection identifier.
That means the function may need to know:
which connection sent the original message
whether that connection is still active
which gateway endpoint should receive the callback
how to authenticate the callback request
what to do if the connection disappeared before the response arrives
The response path has now become its own piece of infrastructure.
A simple:
receive message → process → return response
can turn into:
receive event → extract connection ID → process event → initialize gateway client → send callback → handle stale connection
None of those steps are impossible.
They are just additional machinery.
Why serverless makes this more noticeable
On a traditional WebSocket server, the active socket object is often already available in memory.
Conceptually, the application can do something like:
socket.send(response)
The same process that received the message still owns the connection.
With serverless, that assumption disappears.
The function processing the event may have no direct access to the underlying WebSocket at all.
The connection belongs to another layer.
That separation is useful for scaling, but it changes the developer experience.
Async workloads make it even more complicated
The response path becomes even more important when the work cannot finish immediately.
Suppose a WebSocket message starts a longer-running job.
The initial function might place the work onto a queue and exit.
Later, another worker finishes the job.
Now that worker needs to send the result back to the original client.
The architecture might become:
Client → WebSocket Gateway → Function → Queue → Worker
followed by:
Worker → Gateway API → WebSocket Client
At that point, the application needs some way to preserve the relationship between the original connection and the eventual result.
Again, the application starts managing transport infrastructure alongside its actual business logic.
What if the webhook response could become the socket response?
There is another possible model.
Imagine this:
Client → WebSocket → Gateway → HTTP Webhook
The backend receives an ordinary HTTP request.
Then it simply returns:
200 OK
with:
{"message":"pong"}
The gateway takes that HTTP response and automatically sends it back over the originating WebSocket connection.
The developer gets a familiar programming model:
WebSocket message → HTTP request → HTTP response → WebSocket message
The persistent connection still exists.
But the backend does not need a special SDK or a second callback request for a basic response.
This is one of the ideas behind Cortlet
With Cortlet, we are exploring whether WebSocket infrastructure can feel more like ordinary HTTP infrastructure.
The gateway would own the persistent connection.
The backend would receive standard HTTP webhooks.
For simple request-response interactions, the HTTP response could potentially become the WebSocket response automatically.
More complex asynchronous flows could still use explicit outbound APIs when needed.
The goal is not to hide how WebSockets work.
It is to remove infrastructure steps that the application should not need to repeat for every message.
Familiar interfaces matter
Developers already understand HTTP extremely well.
Requests come in.
Responses go out.
Serverless platforms are designed around that model.
If a WebSocket gateway can translate persistent connections into that familiar request-response flow, realtime applications may require less WebSocket-specific backend code.
And that leads to a useful question:
If the backend already knows how to return an HTTP response, why should sending a basic WebSocket reply require an entirely separate code path?