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:
| Service | URL | What it does |
|---|---|---|
| Bifrost HTTP | bifrost-https-v10.gw.postman.com | Main API gateway — all HTTP requests |
| Bifrost Sync (WebSocket) | bifrost-v10.getpostman.com | Real-time event streaming |
| Postman API | api.postman.com | Public REST API (collections, environments, etc.) |
| Identity | identity.getpostman.com / auth.postman.com | Authentication and user management |
| Scribe | documenter.getpostman.com | Documentation publishing |
| Monitors | monitor.getpostman.com | Collection monitoring service |
| Analytics | events.getpostman.com | Usage event tracking |
| Update server | dl.pstmn.io | Desktop app auto-updater |
| Elements | elements.getpostman.com | Public 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 clientplatform-libs/websocket-proxy/— WebSocket connection managementplatform-libs/realtime-pubsub/README.md— full docs for the pub/sub systemconfig/environments/default/desktop/base.json— all backend service URLs