Skip to main content

How the app talks to the backend

Postman is a cloud-connected desktop app. Every collection you save, every workspace you open, every collaborator's change you see in real time — all of that involves the app talking to backend services. This page explains how that communication works.


The gateway: Bifrost

Every HTTP request from the app goes through a single gateway called Bifrost. You will see it everywhere in the codebase:

__WP_HTTP_GATEWAY_URL__ = 'https://bifrost-https-v10.gw.postman.com'

Bifrost is Postman's API gateway — a reverse proxy that sits in front of all backend microservices. The app doesn't know or care which microservice handles a request. It just talks to Bifrost, which routes it to the right place.

This design has practical benefits: the app's code doesn't need to change if the team splits a monolith into microservices, or merges two services, or changes which service handles a feature. As long as Bifrost's routing stays the same, the app works.

In the code, all HTTP communication goes through one package:

import { gatewayClient } from '@postman-app/gateway-service';

// GET request — automatically includes auth headers, handles retries
const workspace = await gatewayClient.get('/workspaces/ws-123');

// POST request
const newCollection = await gatewayClient.post('/collections', {
name: 'My Collection',
workspaceId: 'ws-123'
});

The gateway-service package handles authentication tokens, request retries, error normalization, and environment-specific base URLs. Feature teams never configure HTTP clients themselves — they use gateway-service.


The backend services the app uses

From the environment config, the app talks to these services:

ServiceURLWhat it does
Bifrost HTTPbifrost-https-v10.gw.postman.comMain API gateway — all HTTP requests
Bifrost Sync (WebSocket)bifrost-v10.getpostman.comReal-time event streaming
Postman APIapi.postman.comPublic REST API (collections, environments, etc.)
Identityidentity.getpostman.com / auth.postman.comAuthentication and user management
Scribedocumenter.getpostman.comDocumentation publishing
Monitorsmonitor.getpostman.comCollection monitoring service
Analyticsevents.getpostman.comUsage event tracking
Update serverdl.pstmn.ioDesktop app auto-updater
Elementselements.getpostman.comPublic API network / sharing

The app does not have a single backend database it talks to directly. It talks to APIs, which internally use whatever databases they need. From the app's perspective, everything is HTTP or WebSocket.


Real-time: the sync WebSocket

HTTP is fine for fetching data. But HTTP is not great for "someone else just made a change — update the UI now."

For real-time updates, postman-app uses a persistent WebSocket connection to Bifrost's sync server. This is how you see collaborators' changes appear in real time, how collection updates propagate, and how team notifications are delivered.

The WebSocket connection is managed by two packages working together:

platform-libs/websocket-proxy — Maintains the physical WebSocket connection. Handles reconnection, connection state, and the low-level message protocol.

platform-libs/realtime-pubsub — A publish/subscribe layer on top of the WebSocket. Instead of consuming the raw event stream, feature code subscribes to specific channels (like "all updates for collection X" or "all workspace membership changes").

import { createChannel } from '@postman-app/realtime-pubsub';

// Subscribe to real-time updates for a specific collection
const channel = createChannel({
room: `collection/${collectionId}`,
onEvent: (event) => {
if (event.action === 'update') {
refetch(); // Reload the collection data
}
}
});

// The channel automatically:
// - Handles reconnection if the WebSocket drops
// - Shares one server subscription across multiple UI consumers
// - Cleans up when the component unmounts

The key insight about realtime-pubsub: if ten components on screen all care about the same collection, they don't create ten separate WebSocket subscriptions. The package reference-counts subscriptions and maintains exactly one server-side room subscription regardless of how many UI consumers there are.


How a typical data flow works

Let's trace what happens when you open a workspace and see its collections:

1. User navigates to /workspace/ws-123
→ views/workspace-overview loads

2. ui-features/collections-sidebar renders
→ calls useCollectionsQuery(workspaceId) from data/collection-data

3. React Query checks its cache:
- Cache hit → render immediately, revalidate in background
- Cache miss → fetch from server

4. data/collection-data calls gatewayClient.get('/collections?workspaceId=ws-123')
→ HTTP request → Bifrost → Collections microservice → database

5. Response arrives → React Query caches it → React re-renders → UI shows collections

6. Meanwhile, realtime-pubsub subscribes to collection update events
→ If a collaborator adds a collection → WebSocket event arrives
→ React Query cache is invalidated → re-fetch → UI updates

This pattern — fetch via HTTP, keep in sync via WebSocket — is how all real-time collaborative features work in Postman.


Authentication

Every HTTP request sent through gateway-service automatically includes authentication. The gateway service reads the current user's access token from a secure store managed by the identity service and adds it as a header.

When a user logs in, the identity service (identity.getpostman.com) issues tokens. When a token expires, gateway-service handles the refresh silently in the background. Feature code never deals with tokens directly.


What this means for feature development

As a feature developer, you almost never interact with HTTP or WebSocket directly. Your stack looks like:

// In data/my-feature-data/src/queries/useMyFeatureQuery.ts

import { gatewayClient } from '@postman-app/gateway-service';
import { useQuery } from '@tanstack/react-query';

export const useMyFeatureQuery = (featureId: string) => {
return useQuery({
queryKey: ['my-feature', featureId],
queryFn: () => gatewayClient.get(`/my-feature/${featureId}`)
});
};

React Query manages caching, loading states, and background revalidation. gateway-service handles auth and routing. You just describe what data you want and how to fetch it.


References

  • platform-libs/gateway-service/ — source code for the HTTP gateway client
  • platform-libs/websocket-proxy/ — WebSocket connection management
  • platform-libs/realtime-pubsub/README.md — full docs for the pub/sub system
  • config/environments/default/desktop/base.json — all backend service URLs