A simple example chess website using Go, React, Websockets, and MongoDB Change Streams
This is very simple chess website built using React, Go, and MongoDB. Players will be matched with another player when one is available. They will then be able to play chess in realtime. The frontend is built with React and Redux. It uses the chess.js and chessboardjsx libraries which are used by the likes of chess.com and lichess.org. The underlying databases is MongoDB, using the new MongoDB Driver for Go. Clients communicate with the server through websockets. When a player makes a move, the game is updated in the database. A change stream takes any changes to games and sends them to clients via websockets.
Running the React application:
cd frontend
npm i
npm run dev
This runs webpack dev server. If you build the frontend and serve it with the go app, the chess board will flicker when moving pieces. I haven't figured out the cause of this yet, so best results are when using npm run dev
to run the react app separatey from the go app.
Running the Go backend:
cd ..
go run .
localhost:8080
in two browser windows side by side.1. e4 e5 2. Bc4 Nc6 3. Qh5 Nf6?? 4. Qxf7#
There are 4 files:
main.go
: main()
does three things:
chess.games
collection,The server handles two routes:
/ws
: The websocket which the frontend connects to. The frontend will send moves made to this websocket./start
: Clients will POST here to start a game.mongo.go
:
connectMongo()
: Connects to MongoDB using mongo.Connect()
.watchForChanges()
: Will watch a MongoDB collection and will send the change events to a channel.websockets.go
makeHandleWebsockets()
: Returns the handler function for the /ws
endpoint. It creates a websocket using the gorilla
websocket library. A couple of things happen here:
readMessages
goroutine, we wait for any messages from the client. The client sends moves. These are added to the game document in the database.find_game.go
makeHandleStart()
: Returns the handler function for the /start
endpoint. This does the matchmaking. The matchmaking is very simple. If there's a document in the games collection where black
is an empty string, that means there's no black player, and the player is added to that game as black. If there isn't a document where black
is an empty string, a new game is made where the player is set to be white. They will then have to wait for another player to come along and join their game as black.It's worth just showing off a couple of things:
Show the functionality of the chess app, by playing a few moves.
Show mongo.go
. This shows how to connect to the database in connectMongo()
and how it watches for changes in watchForChanges()
You can then show how in websockets.go
, and changes to a game is sent to the players via websockets. The key code block is:
for event := range eventChan {
fmt.Println("Event received: ", event)
for _, client := range clients[event.FullDocument.ID.Hex()] {
err := client.WriteJSON(event.FullDocument)
if err != nil {
log.Printf("error: %v", err)
client.Close()
}
}
}