skip to content

What is @ConnectMapping in Spring RSocket, how does it differ from @MessageMapping, and what are its constraints?

level: seniorimportance: should knowfreq 40%

answer

  1. Runs once on SETUP frame, before requests
  2. void / Mono<Void> only — no data reply
  3. error → connection rejected (auth gate)
  4. capture client RSocketRequester for push
  5. routed or unrouted

basics

~20 s

@ConnectMapping handles the RSocket SETUP frame once, when a client connects — good for auth, registration, or reading setup metadata. Unlike @MessageMapping (per-request, any interaction model), it runs once per connection and cannot return data; it returns void/Mono<Void>.

solid answer

~40 s

@ConnectMapping is a responder-side hook invoked once per connection when the client's SETUP frame arrives, before any @MessageMapping requests. It receives the setup payload and/or connection metadata and is where you authenticate, register the client, or capture the client's RSocketRequester for later server-initiated calls. Key constraints: it must return void or Mono<Void> — it cannot send a response payload back to the client (the setup handshake has no reply channel). Throwing or returning an error Mono rejects the connection. It can be routed: @ConnectMapping("route") matches setup routing metadata, or unrouted to handle every connection. By contrast, @MessageMapping handles individual requests after connect and supports all four interaction models via its return/parameter types. A common pattern is capturing RSocketRequester in @ConnectMapping to push messages to that specific client afterward.

code

java · 16 lines
java
@Controller
class ConnectionController {

    private final ClientRegistry registry;
    ConnectionController(ClientRegistry registry) { this.registry = registry; }

    @ConnectMapping("client.connect")
    Mono<Void> onConnect(RSocketRequester clientRequester, @Payload ClientInfo info) {
        if (!info.hasValidToken()) {
            return Mono.error(new RejectedSetupException("unauthenticated"));
        }
        // capture the client's requester so the server can push to it later
        registry.register(info.id(), clientRequester);
        return Mono.empty(); // completes -> connection accepted
    }
}

go deeper

for a junior

Know it handles the connection setup once, unlike per-request @MessageMapping.

for a middle

Explain the void/Mono<Void> constraint and typical uses (auth, registration).

for a senior

Describe error-rejects-connection semantics, routed vs unrouted, and capturing the client requester for push.

for a principal

Discuss connection-level vs request-level security, latency impact of connect-time work, and server-initiated interaction architecture.

In RSocket, a connection begins with a **SETUP frame** the client sends once during the handshake — it can carry a setup *payload* and *metadata*. Spring exposes this moment through **`@ConnectMapping`**, which is distinct from the per-request `@MessageMapping`. **What @ConnectMapping is for.** It is invoked exactly **once per connection**, at connect time, *before* any request frames are handled. Typical uses: - **Authentication/authorization of the connection** — read credentials from setup metadata and reject if invalid. - **Client registration** — record that a client connected; often capture its `RSocketRequester` so the server can later initiate calls *to* that client (server-to-client streaming/push). - **Reading setup context** — a client id, protocol version, or feature flags sent in the setup payload. **Signatures & arguments.** A `@ConnectMapping` method can accept the setup payload (decoded to a type), `@Header`/metadata values, and — importantly — an injected `RSocketRequester` representing the *connecting client*, which you can stash in a registry for later use. It may be **routed** (`@ConnectMapping("client.connect")` matches the setup frame's routing metadata) or **unrouted** (matches every connection). **Hard constraints.** - It **must return `void` or `Mono<Void>`**. The setup handshake has no application reply channel, so you cannot send a payload back to the client from here. Attempting to return data is a design/usage error. - **Errors reject the connection.** If the method throws or returns `Mono.error(...)`, the connection setup fails and the client is disconnected — this is precisely how you enforce connection-level auth. - Because it returns `Mono<Void>`, any async work (e.g. a DB lookup for auth) must be composed into that Mono; the connection is considered established when it completes successfully. **Contrast with @MessageMapping.** | Aspect | `@ConnectMapping` | `@MessageMapping` | |---|---|---| | When | Once, at SETUP | Per request, after connect | | Return | `void`/`Mono<Void>` only | `Mono`/`Flux`/`void` — picks interaction model | | Can reply with data | No | Yes | | Purpose | Auth, registration, setup context | The four interaction models | **The capture-the-requester pattern.** Because a `@ConnectMapping` (or even a `@MessageMapping`) handler can inject the client's `RSocketRequester`, servers implement push by storing that requester keyed by client id, then later calling `requester.route(...).data(...).send()` or `.retrieveFlux(...)` against the client's own responder. This is how RSocket enables true server-initiated interactions over the same multiplexed connection. **Gotchas.** - Forgetting that `@ConnectMapping` can't return data leads people to try request-response semantics at connect — instead do the exchange over a normal `@MessageMapping` after connect, or pass needed info in the setup payload. - Long/blocking work in `@ConnectMapping` delays *every* connection's establishment; keep it reactive and fast. - Security: connection-level auth in `@ConnectMapping` complements per-request auth; Spring Security's RSocket support integrates with both setup and request metadata. **When to use.** Reach for `@ConnectMapping` whenever you need per-connection setup logic — authenticating the handshake, registering clients for later push, or extracting connection-scoped context — and use `@MessageMapping` for everything request-scoped.

  • How can a server send messages to a specific connected client later?
    Capture that client's RSocketRequester in @ConnectMapping (or a @MessageMapping) and store it keyed by client id; later call route()...send()/retrieveFlux() on it to push over the same connection.
  • Why can't @ConnectMapping return a payload to the client?
    The RSocket setup handshake has no application-level reply channel; Spring restricts it to void/Mono<Void>. Do request-response exchanges via @MessageMapping after connect instead.

saying these in an interview costs you the question

  • Expecting @ConnectMapping to return a response payload to the client
  • Thinking @ConnectMapping runs per request rather than once per connection
  • Not realizing an error from @ConnectMapping rejects the whole connection
  • Doing heavy blocking work in the connect handler

context