Overview
Social Graph Service manages the relationships between players: friends, invitations, blocks and the queries that sit on top of them. Social features look simple from the outside and are deceptively awkward underneath, because almost every question a game asks is a graph traversal under a latency budget. "Which of my friends are online?" touches every edge a player has, and it gets asked on every screen.
The service is built in Golang on PostgreSQL (Neon) with Redis caching the queries that run constantly — friend lists, online status, pending invitations. Relationships are bidirectional and stored once rather than duplicated per direction, which avoids the classic bug where two players disagree about whether they are friends because one side of the write succeeded and the other did not.
It runs as a monolith with AWS Lambda handling event-driven work, so invitation notifications and status fan-out scale independently of the request path. Blocking is treated as a first-class relationship rather than an afterthought, which means a blocked player disappears from suggestions, search and chat consistently instead of in whichever places happened to be wired up.
Built for Impact
Advanced features that power your digital infrastructure.
Bidirectional Friendships
Relationships are stored once rather than duplicated per direction, so the two sides can never disagree about whether a friendship exists.
Invitations and Requests
Pending, accepted, declined and expired states handled explicitly, with the edge cases — mutual requests, re-requests after a decline — resolved rather than left to the client.
Blocking as a First-Class Relationship
A blocked player is removed from suggestions, search, chat and presence consistently, instead of only in the places that remembered to check.
Presence and Online Status
Cached friend presence so the friends list opens instantly rather than fanning out a status query across every edge on every render.
Friend Suggestions
Mutual-connection and shared-activity suggestions computed from the graph, giving players a reason to expand their network without manual search.
Event-Driven Notifications
Invitations and status changes fan out through Lambda, so notification volume scales independently of the request path serving the app.
Specs
Architecture
Monolith
Language
Golang
Database
Postgres (Neon)
Cache
Redis
Deployment
AWS Lambda / Docker
Use Case
Social Connections
How It Works
Send an invitation
A player invites another by id or handle. The service records a pending edge and handles the awkward cases explicitly: a mutual invitation resolves straight to a friendship rather than leaving two pending requests, and an invitation to someone who has blocked you fails quietly rather than revealing the block.
Accept and materialise
On acceptance the pending edge becomes a friendship, stored once and readable from either side. Because there is a single row rather than one per direction, there is no window in which the two players disagree about their relationship.
Query the graph
Friend lists, mutual connections and presence are served from Redis where possible, falling back to Postgres on a miss. This is the path that runs most often — the friends list is on almost every screen — so it is the one that gets cached hardest.
Fan out events
Invitations, acceptances and presence changes are emitted as events handled by Lambda. Notification work therefore scales on its own rather than competing with the requests players are waiting on.
Common Use Cases
Multiplayer social layers
Friends, parties and invitations for a game where playing with people you know is the retention mechanic. Presence caching is what keeps the friends list from becoming the slowest screen in the product.
Community platforms
Following, connections and blocking for a social product. The graph primitives are the same whether the edges are called friends or follows; only the directionality rules differ.
Cross-game friend lists
A shared social graph across a publisher's catalogue, so a friendship made in one title carries into the next. Far easier to establish once than to reconcile across live games later.
What to Decide Early
Decide whether relationships are symmetric
Friends are mutual and require acceptance; follows are one-directional and do not. The two produce different privacy expectations, different notification volumes and different graph shapes. Picking one deliberately matters, because retrofitting the other later means reinterpreting every existing edge and explaining the change to users who understood the original model.
Set limits before someone finds them
Friend list caps, invitation rate limits and request expiry all exist to prevent abuse rather than to inconvenience players. Without them, invitation spam becomes a support problem and unbounded lists become a performance one. Choosing the numbers early is easy; lowering a limit after players have already exceeded it is not.
Work out what blocking actually hides
Blocking is simple to store and subtle to apply. Whether a blocked player disappears from leaderboards, whether they can still see public profiles, and whether the block is visible to them are all product decisions with real consequences. Treating blocking as a stored relationship rather than a UI filter is what makes applying it consistently possible.
Frequently Asked Questions
Still have questions? Talk to our team
Scale Your Infrastructure
Ready to handle millions of users? Our backend experts are here to build the high-concurrency, secure architecture your mission-critical project demands.
Request Custom Solution