This monorepo bootstraps the infrastructure required to build a server authoritative Tic-Tac-Toe experience powered by Nakama.
The repository combines an Expo React Native client, Go plugins for Nakama, and deployment automation for Google Cloud Platform.
.
├── frontend/ # Expo React Native application
├── backend/ # Go Nakama plugin source and tooling
├── scripts/ # Shared development helpers
├── package.json # Root workspace configuration
├── pnpm-workspace.yaml
└── turbo.json # Task orchestration via Turborepo
- Node.js 20 LTS
- pnpm 9 LTS
- Go 1.25+
- Docker
- golangci-lint
- goimports
- Optional but recommended: direnv or similar for
.envmanagement
-
Install dependencies
pnpm install
-
Install the Go developer tooling (golangci-lint & goimports)
./scripts/install-go-tools.sh
Add Go's tool bin to your shell path if it's not already there:
export PATH="$HOME/go/bin:$PATH"
-
Prepare Husky git hooks
pnpm prepare
-
Start the backend services using Docker Compose (recommended)
For development (simplified, no SSL/nginx):
docker compose -f backend/docker-compose.dev.yml up -d
For production (with nginx and SSL):
docker compose -f backend/docker-compose.yml up -d
Both options start PostgreSQL and Nakama with the compiled Go plugin.
-
(Optional) For native development, download the Nakama macOS binary
./scripts/setup-nakama-native-macos.sh
-
(Optional) For native development, start PostgreSQL (Docker) and run Nakama locally
./scripts/run-nakama-native-local.sh
The script rebuilds the Go plugin for the host architecture into
backend/build/darwin-<arch>/and then tails the Nakama process. SetNAKAMA_SKIP_PLUGIN=1if you want to launch Nakama without loading the custom module. PressCtrl+Cto stop the server. -
Verify the backend is running
-
Verify the backend is running
./scripts/check-backend-health.sh
-
Stop the backend services
For development:
docker compose -f backend/docker-compose.dev.yml down
For production:
docker compose -f backend/docker-compose.yml down
-
Launch the Expo development server
pnpm --filter frontend start
Common scripts are orchestrated with Turborepo.
pnpm lint # Runs linting for all workspaces
pnpm test # Executes test suites
pnpm build # Builds the Go plugin and prepares frontend artifacts
pnpm format # Checks formatting across the repo
pnpm typecheck # Runs TypeScript and Go vet checksThe multiplayer experience relies on Nakama's realtime socket API once a user has authenticated with the backend:
- WebSocket connection – The Expo client creates a socket and calls
socket.connect(session, true)so the player appears online and can enter the matchmaking pool. SeenakamaService.connectSocket()for the concrete implementation. - Matchmaking updates – While queued, the client listens for
socket.onmatchmakermatchedto receive the authoritativematch_idreturned by the Go plugin when it callsnk.MatchCreate. - Sending moves – After joining a match, the client submits moves via
socket.sendMatchState(matchId, opcode, payload); the player screen wraps this to send the tapped cell index as JSON. - Receiving state – Every broadcast from the server arrives through
socket.onmatchdata, where the UI inspects the opcode and updates the board, turn indicator, and end-of-game messaging.
The authoritative match handler in backend/match.go validates moves, tracks the
board, and broadcasts the canonical state after each turn, ensuring all clients
stay in sync.
pnpm --filter frontend lint– ESLint + Prettierpnpm --filter frontend test– Jest (jest-expo preset)pnpm --filter frontend typecheck– TypeScript--noEmitpnpm --filter frontend start– Expo metro bundler
pnpm --filter backend build– Builds the Nakama plugin (Linux target for CI/CD)pnpm --filter backend test– Runs Go testspnpm --filter backend lint–golangci-lintpnpm --filter backend typecheck–go vet./scripts/run-nakama-native-local.sh– macOS development server (Postgres via Docker, Nakama native binary)
docker compose -f backend/docker-compose.dev.yml up -d– Start local PostgreSQL and Nakama services (development)docker compose -f backend/docker-compose.yml up -d– Start local PostgreSQL, Nakama, Nginx, and Certbot services (production)docker compose -f backend/docker-compose.dev.yml up -d postgres– Start local Postgres only (development)
Copy .env.example to .env and adjust credentials for local development or CI.
cp .env.example .envFor production deployment with SSL termination and domain setup, see the comprehensive Production Deployment Guide.
The deployment process involves:
- Server Setup: Use
scripts/gcp-vm-setup.sh(for GCP) orscripts/server-deploy-setup.sh(for any Ubuntu server) - SSL Configuration: Run
backend/setup-ssl.shto configure Let's Encrypt certificates forapi.tictactoe.kauntalha.dev - Service Management: Use Docker Compose commands to manage the Nakama backend with Nginx reverse proxy
Quick production setup:
# On your production server
curl -sSL https://raw.githubusercontent.com/rogdex24/tictactoe-game/main/scripts/server-deploy-setup.sh | bash
cd ~/tictactoe-game/backend
./setup-ssl.shYour backend will be accessible at https://api.tictactoe.kauntalha.dev with automatic SSL certificate management.
GitHub Actions workflow (.github/workflows/ci.yml) runs linting, tests, builds the Go plugin, and optionally pushes a Docker image to Artifact Registry when pushing to main.
- Ensure
GCP_ARTIFACT_REPOSITORYand GCP credentials are configured in CI for automated pushes. - Provision Nakama and PostgreSQL using GKE Autopilot or Cloud Run Jobs + Cloud SQL depending on scaling needs.
- Commit messages must follow the Conventional Commits spec. A Husky
commit-msghook runs Commitlint to enforce the format and prevent malformed commit messages from landing in the repository. - Pre-commit hooks format JavaScript/TypeScript and Go sources via
lint-staged, ensuring ESLint/Prettier andgoimports+golangci-lintrun automatically on staged changes. - Pre-push hooks run the full lint and test suites.
- Always run
pnpm testbefore opening a pull request.