Skip to content

HarshP34/go-realtime-pubsub

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Slack Clone — Go + Socket.IO + Redis + PostgreSQL

A real-time messaging backend built in Go. Supports multiple server instances communicating across each other using Redis pub/sub — a message sent from a user on instance 1 is delivered to users on instance 2.


Architecture

Client A          Client B
   │                 │
Server 1       Server 2
   │                 │
   └──── Redis ──────┘
   (pub/sub bridge)
         │
    PostgreSQL
  (shared storage)

When a user sends a message:

  1. The receiving server publishes it to Redis channel slack:messages
  2. Every server subscribed to that channel receives it
  3. Each server broadcasts locally to its connected clients in that room

This means users connected to different server instances can talk in the same room seamlessly.


Stack

Layer Technology
Language Go 1.22+
WebSocket Socket.IO (go-socket.io v1.7.0)
Pub/Sub Redis (redigo)
Database PostgreSQL (lib/pq)
Config .env (godotenv)

Prerequisites

  • Go 1.22+
  • PostgreSQL running locally
  • Redis running locally

Setup

1. Clone and install dependencies:

git clone https://github.com/harsh-patel/slack
cd slack
go mod tidy

2. Create .env:

PORT=7001
DATABASE_URL=postgres://user:password@localhost:5432/slack?sslmode=disable
REDIS_URL=localhost:6379

3. Run:

go run ./cmd/main.go

Tables (users, rooms, room_members) are created automatically on startup.


Running Multiple Instances

# Terminal 1
PORT=7001 go run ./cmd/main.go

# Terminal 2 (PowerShell)
$env:PORT="7002"; go run ./cmd/main.go

Both instances connect to the same PostgreSQL and Redis — rooms and users are shared, and messages flow across instances.


Browser Test Client

A test UI is available at http://localhost:7001/ after starting the server. It lets you create users, create rooms, add members, and test the full Socket.IO flow without any external tooling.

Note: The test client uses Socket.IO v2.4.0. The server (go-socket.io v1.7.0) uses the engine.io v3 wire protocol, which is incompatible with Socket.IO v3/v4 clients.


HTTP API

Users

Method Endpoint Body Description
POST /users {"id","username","email"} Create user
GET /users/{id} Get user by ID
GET /users Get all users

Rooms

Method Endpoint Body Description
POST /rooms {"id","name"} Create room
GET /rooms/{id} Get room + members
POST /rooms/dm {"userID1","userID2"} Create DM room
POST /rooms/{id}/members {"userID"} Add member
DELETE /rooms/{id}/members/{userID} Remove member

Socket.IO Events

Connect with a valid userID (must exist in DB):

ws://localhost:7001/socket.io/?userID=user-1

Use a Socket.IO v2.x client — v3/v4 clients use a different wire protocol and will fail to connect.

Emit (client → server)

Event Payload Description
join "room-general" Join a room (must be a DB member)
message {"roomID":"...","text":"..."} Send message to a room
leave "room-general" Leave a room
get_members "room-general" Request live socket members

Listen (server → client)

Event Payload Description
message {"roomID","text","senderID"} Incoming message
user_joined "userID" User joined the room
user_left "userID" User left the room
room_members {"roomID","members":[...]} Response to get_members
room_error "reason" Join/message rejected

Message Flow (Multi-Instance)

%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#4a90d9', 'primaryTextColor': '#ffffff', 'primaryBorderColor': '#2c6fad', 'secondaryColor': '#e8453c', 'secondaryTextColor': '#ffffff', 'secondaryBorderColor': '#b03030', 'tertiaryColor': '#2ecc71', 'tertiaryTextColor': '#ffffff', 'tertiaryBorderColor': '#1a9e54', 'noteBkgColor': '#fff5ad', 'noteTextColor': '#333333', 'sequenceNumberColor': '#ffffff', 'actorTextColor': '#ffffff', 'actorBkg': '#4a90d9', 'actorBorder': '#2c6fad'}}}%%
sequenceDiagram
    participant u1 as u1
    participant Server1 as Server 1
    participant Redis as Redis
    participant Server2 as Server 2
    participant u2 as u2

    u1->>Server1: emit "message" {roomID, text}
    Server1->>Redis: PUBLISH slack:messages
    Redis-->>Server1: deliver
    Redis-->>Server2: deliver
    Server1->>u1: BroadcastToRoom ✓
    Server2->>u2: BroadcastToRoom ✓
Loading

Validations

  • Connection rejected if userID is missing or not found in DB
  • join rejected if room does not exist in DB or user is not a member
  • message rejected if sender has not joined the room via socket

Project Structure

slack/
├── cmd/
│   └── main.go                 # Entry point
├── internal/
│   ├── db/
│   │   └── db.go               # PostgreSQL connection + migrations
│   ├── domain/
│   │   ├── user.go
│   │   └── room.go
│   ├── repository/
│   │   ├── user_repo.go        # UserRepository interface
│   │   ├── room_repo.go        # RoomRepository interface
│   │   ├── postgres_user_repo.go
│   │   └── postgres_room_repo.go
│   ├── services/
│   │   ├── user_service.go
│   │   └── room_service.go
│   └── handler/
│       ├── http.go             # REST handlers
│       ├── ws.go               # Socket.IO server + room/connection tracking
│       └── redis.go            # Redis pub/sub
├── static/
│   └── test.html               # Browser-based test client (Socket.IO v2)
└── .env

About

A Go backend demonstrating real-time multi-server communication using Redis pub/sub and Socket.IO. Messages sent from a client on one server instance are instantly delivered to clients on other instances — bridged through a shared Redis channel.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors