Skip to content

Alijeyrad/go-chat

Repository files navigation

Author Information

👤 Ali Julaee Rad

GitHub followers


SnappChat!

This is SnappChat! but not the one you're thinking of ;) It's a chat application project showcasing a Go backend with Fiber, GORM, Redis, NATS, and more. This document describes how to set up and run the project in different modes (development, production, test), explains the project’s architecture, lists its features and API endpoints, and provides guidance on how to extend it. At the end, you’ll also find notes on limitations and potential improvements.

  1. Overview

SnappChat! is a chat application built with a microservice-friendly design:

  • Go (Golang) as the primary language
  • Fiber for the web framework and routing
  • PostgreSQL as the main relational database (using GORM)
  • Redis for caching and presence tracking
  • NATS for messaging and presence broadcasts
  • Docker Compose for containerized deployments in dev/test/prod

The server implements standard CRUD functionalities for user management and chat rooms, featuring registration, login, chatroom creation, listing, and joining.

  1. Key Features

    1. User Management:
      • Registration, login
    2. Chatroom Management:
      • Create new chatrooms
      • List all chatrooms (with pagination)
      • Join a chatroom
    3. Token-based Authentication:
      • JWT-based authentication (via Fiber middleware)
    4. Caching Layer:
      • Redis-based caching for users
      • Rate-limiting using Redis
    5. Messaging/Presence Tracking:
      • NATS for presence handling and event-driven architecture
    6. Configurable:
      • Environment-based configuration (dev, prod, test)
      • Docker Compose setups for each environment
  2. Folder Structure & Architecture

Below is the high-level directory layout relevant to the server side of SnappChat!:

snappchat
 ┣ client/ ...
 ┗ server
   ┣ api
   ┃ ┣ common
   ┃ ┣ handler
   ┃ ┃ ┣ chat
   ┃ ┃ ┗ user
   ┃ ┣ middleware
   ┃ ┗ router
   ┣ app
   ┣ cmd
   ┣ config
   ┣ docs
   ┣ internal
   ┃ ┣ adapter
   ┃ ┃ ┣ chat
   ┃ ┃ ┗ user
   ┃ ┣ domain
   ┃ ┃ ┣ chat
   ┃ ┃ ┣ user
   ┃ ┃ ┗ entity
   ┃ ┗ dto
   ┣ pkg
   ┣ test
   ┣ Dockerfile
   ┣ Dockerfile.dev
   ┣ Dockerfile.tests
   ┣ go.mod
   ┣ go.sum
   ┗ nats.conf

High-Level Architecture

SnappChat! uses a layered approach:

  1. API Layer (Fiber)
    • Receives HTTP requests
    • Routes them through router
    • Uses handlers in api/handler
  2. Domain Layer
    • Business logic for User and Chat
    • Services and interfaces in internal/domain/<entity>
  3. Data Access Layer (Adapters)
    • Repositories in internal/adapter/<entity>
    • These implement the repository interfaces from the domain layer using GORM for PostgreSQL
  4. Infrastructure / Support
    • pkg includes:
    • logger (Zap-based)
    • jwt (JWT token creation)
    • redis (Redis client)
    • nats (NATS connection)
    • postgres (PostgreSQL GORM config)
  5. App Container
    • app/container.go initializes all dependencies and the Fiber app, injects services, and runs migrations.

Folder Breakdown

  • api:
    • common: Common structs for HTTP responses (Response, AppError)
    • handler: Handlers for each resource (e.g. user, chat)
    • middleware: Additional middlewares (e.g. JWT authentication)
    • router: Where routes are set up and middlewares are applied
  • app:
    • container.go: Creates the main container with DB, Redis, NATS, JWT config, etc.
    • services.go: Helper functions to inject domain services
  • cmd:
    • main.go: Entry point for the server. Loads config, initializes the app, sets up routes, and starts Fiber.
  • config:
    • config.go: Structs defining all configuration (server, DB, NATS, Redis)
    • read.go: Reading environment variables using envconfig
  • docs:
    • Contains documentation or Postman collections, etc.
  • internal:
    • adapter: Data access layer (repositories) bridging GORM/Redis to domain interfaces
    • domain: Business logic, domain services, entity definitions, and migrations
    • dto: Data Transfer Objects (e.g. for user login/registration, chat creation, etc.)
  • pkg:
    • jwt, logger, nats, postgres, redis, validate: Common packages shared across the application
  • test:
    • Contains integration/functional tests.
    • main_test.go & subfolders for grouping tests (e.g. user tests).
    • common/: Utility to spin up test containers, run the server in test mode, and tear down.
  1. How to Run the Project

SnappChat! is containerized. You can run it locally (Docker + Docker Compose) or directly on your machine if you set up the environment variables and dependencies. We have three main Compose files:

  • docker-compose.yaml for production
  • docker-compose.dev.yaml for development
  • docker-compose.test.yaml for test

Environment Variables

All environment variables live in one of these .env files:

  • server/.env (used for production or dev, depending on your setup)
  • server/.env.example (template)
  • server/.env.test (specific for test environment)

The following variables are especially important:

POSTGRES_HOST=<database-host> POSTGRES_PORT=<database-port> POSTGRES_USER=<database-username> POSTGRES_PASSWORD=<database-password> POSTGRES_DB=<database-dbname> POSTGRES_SSL_MODE=<ssl-mode>

SERVER_ENV=<dev|prod|test> SERVER_PORT=<port> SERVER_SECRET=<jwt-secret> SERVER_EXP_MIN=<jwt-expiration-in-minutes>

NATS_HOST=<nats-host> NATS_PORT=<nats-port> NATS_AUTH_TOKEN=<nats-token>

REDIS_HOST=<redis-host> REDIS_PORT=<redis-port>

Production Mode

  1. Build & Run:

docker-compose up -d

This command uses the default docker-compose.yaml. It will spin up postgres, redis, nats, and the server container. Once finished:

  • The server listens on SERVER_PORT (exposed to your machine as well).
  • The logs can be viewed via docker logs -f snappchat-server.
  1. Stopping:

docker-compose down

Development Mode

  1. Spin Up:

docker-compose -f docker-compose.dev.yaml up -d

This runs the containers in dev mode. Notable differences:

  • The server container uses Dockerfile.dev which includes Air for live reloading.
  • Changes in the /server directory will automatically rebuild and restart the Go process.
  1. Air config: The air tool is initialized with .air.toml or similar config in the server folder (see RUN air init in the Dockerfile.dev).
  2. Stopping:

docker-compose -f docker-compose.dev.yaml down

Test Mode

  1. Spin Up:

docker-compose -f docker-compose.test.yaml up -d

This will bring up the testing environment (postgres, redis, nats, and server with Dockerfile.tests). The containers may map to the same ports as dev—be mindful of conflicts or use environment overrides.

  1. Running Tests: Enter the running server container:

docker exec -it snappchat-server-test sh

Then within the container:

go test ./test/... -v

or

go test ./...

  1. Stopping:

docker-compose -f docker-compose.test.yaml down

  1. API Description

Base URL

All endpoints are prefixed with:

/api/v1

(For instance, http://localhost:8080/api/v1/users/register.)

Authentication

All non-public endpoints require a Bearer token in the Authorization header, generated on successful login or registration.

User Endpoints • POST /api/v1/users/register Request Body (JSON):

{ "username": "string", "password": "string", "repeat": "string" }

•	username must be at least 3 characters
•	password must be at least 8 characters
•	repeat must match password

Response (200 OK):

{ "status": "success", "message": "user registered", "data": { "token": "JWT_TOKEN_HERE", "user_id": 123 } }

•	POST /api/v1/users/login

Request Body (JSON):

{ "username": "string", "password": "string" }

Response (200 OK):

{ "status": "success", "message": "login success", "data": { "token": "JWT_TOKEN_HERE", "user_id": 123 } }

Chat Endpoints

All chat endpoints require Authorization: Bearer  in the header.

1.	POST /api/v1/chat/chatrooms (Create chatroom)
•	Body:

{ "name": "name_of_chatroom" }

•	Response (200 OK):

{ "status": "success", "message": "Chatroom created", "data": { "chatroom": { "ID": 1, "Name": "name_of_chatroom", ... } } }

2.	GET /api/v1/chat/chatrooms/get?name=chatroom_name (Get chatroom by name)
•	Response (200 OK):

{ "status": "success", "message": "Chatroom retrieved", "data": { "chatroom": { "ID": 1, "Name": "chatroom_name", ... } } }

3.	GET /api/v1/chat/chatrooms/get/all?page=1&limit=10 (List paginated chatrooms)
•	Response (200 OK):

{ "status": "success", "message": "chatrooms fetched", "data": { "chatrooms": [ {...}, {...} ], "page": 1, "limit": 10, "total": 12 } }

4.	GET /api/v1/chat/chatrooms/join/:room_id (Join a chatroom)
•	Response (200 OK):

{ "status": "success", "message": "allowed", "data": { "token": "NATS_AUTH_TOKEN" } }

Status & Error Handling • Successful responses:

{ "status": "success", "message": "...", "data": {...} }

•	Error responses:

{ "status": "error", "error": "Some error message", "data": null }

Typically with an HTTP status code (400, 401, 404, 500, etc.).

  1. How to Add a New Feature or Endpoint

Suppose you want to add a new resource (e.g., “Messages” or “Profiles”). The steps are:

  1. Define Domain Interface & Entities

    • In internal/domain/<new_resource>/service.go, define your interfaces and domain logic.
    • In internal/domain/entity, create the GORM model struct (if storing in PostgreSQL).
    • If needed, update domain/DoMigrations(db *gorm.DB) to include the new entity.
  2. Create Adapters (Repository)

    • In internal/adapter/<new_resource>, implement the domain’s repository interface using GORM or other DB logic.
    • Optionally add caching in Redis if needed, following the pattern in user/cached_postgres_repo.go.
  3. Add Handler

    • In api/handler/<new_resource>, add your Fiber handler that orchestrates the domain service calls.
    • Validate incoming requests using github.com/go-playground/validator/v10.
  4. Register Routes

    • In api/router/<new_resource>.go, define route functions.
    • In api/router/setup.go, create a route group or add endpoints to an existing group.
  5. Inject & Use

    • In router/<new_resource>.go, instantiate your repository and service, then pass them to your handler.
    • Use appContainer.InjectServices if you need to make your service available globally.
  6. Testing

    • Add or update tests under server/test/<new_resource> or in an existing suite.
    • Spin up the test environment and ensure everything passes.
  7. Client CLI Documentation

Design Patterns Used

The CLI uses: • Command pattern in commands/ (e.g., each command has a Run() or Execute() method). • Factory for menus in menus/factory.go.

Commands & Menus

In client/commands and client/menus: • Each command is defined in separate .go files (e.g., login_command.go, join_chat_command.go, etc.). • A central registration method (e.g., register_commands.go) ties commands together. • Menus can be created or extended by adding new MenuItem structs and hooking them into the factory.go.

  1. Limitations & Future Improvements
    1. No Persistence for Messaging • Currently, The messages are not persistent, using any database.
    2. Security Hardening • The CORS policy is wide open (AllowOrigins: "*") for development. In production, you should restrict it to known front-end origins. • Strengthen NATS token generation or switch to more advanced auth.
    3. Session Management / Token Refresh • We rely on a single JWT with an expiration time. We need to incorporate refresh tokens or session-based strategies.
    4. Testing Coverage • We need more integration tests for the chat endpoints and presence tracking.
    5. Message Entity • There is a partial Message struct in the code. A complete messaging flow with endpoints to send and retrieve messages is not yet implemented.
    6. Front-end • The user flow is currently driven by a CLI client. A web or mobile front-end would be required for a typical chat application.

Thank you for using SnappChat!

About

Chat backend using Go, Redis & NATS

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors