Building a Bingo game backend with encore.dev

First of all, let’s talk aboutencore.devwhich is a magical Go backend framework with almost 0 boilerplate,tracing out of the box, serverless deploys immediately, support for postgreSQL, auth and much more! I first heard about this framework in theTLDR Newsletterand I tried it for a little bit and I’m falling in love with it even though I barely know how to code in Go!

To start a new project you need to download and install encore, the good thing is that is available in brew for mac so we can just do the following command in our terminal:

$ brew install encoredev/tap/encore
//then run 
$ encore app create

When doing theencore app createit will ask you to login in our encore app to create a new project, after doing that it will ask you for the name of the app and a starter template, for this project I chose the Hello World template just to have a base to start with, after finishing with the prompt questions you’ll have something like this encore app create

Now if we go into that folder withcd bingoand then doencore runwe should be able to go tohttp://localhost:64675/${project-id}and see the “API Explorer” which is a nice interface that lists all our API endpoints so we can trigger the request from there with a custom body, params and all that encore api explorer There’s also a nice thing about this app that encore creates for us, we also have available an auto generated API docs which specify a little description about the API endpoint, the params types and response schema of each defined endpoint

Creating our bingo database and migration

In this post I won’t be generating all the authentication part of the app, since it’s a small project, I will be creating the following routes with it’s functionality:

  • User creation route (with a number list)
  • Randomizer route (to get a random number and compare to the number list of a specified user)

Doing the migration

So let’s start with how the migration of the user is going to look, to create the right file, we need to add a new folder calledbingoon the root of the project, then create amigrationfolder with a file on it called1_create_user_table.up.sql,Notes:

  • the number of the file means the order that the migrations are going to be executed
  • after the first underscore (_) comes the name of the migration
  • we have to add the extension.up.sqlto all migrations
CREATE TABLE client (
  email TEXT PRIMARY KEY,
  list INTEGER []
);

Defining the logic of the route

after doing this, whenever we do encore run the next time it will run our migration, but before that let’s create abingo.gofile inside the bingo folder to create our routes inside of it.

first we need to include the package name which should match the folder name (our bingo service) then after that we should define our imports and include encore sqldb handler inside of it

package bingo

import (
	"context"

	"encore.dev/storage/sqldb"
)

now we need to declare some structs for our response and request for the route to be used inside the function to cover our needs, we need a struct for the request params and a struct for the response we’re sending, that should look like the following

type UserParams struct {
	Email string
}

type RegisterResponse struct {
	Email string
	List  []int
}

then it comes the fun part, creating the function that in charge of creating the list, the user, store it in the database and returnn it, NOTE: keep in mind that we need to add the comment//encore:api publicbefore the function so encore can detect it as an API route, the following code looks something like this:

//encore:api public
func RegisterUser(ctx context.Context, params *UserParams) (*RegisterResponse, error) {
	var list = generateList(0, 100)
	var user RegisterResponse
	err := sqldb.QueryRow(ctx, `
    INSERT INTO "client" (email, list)
    VALUES ($1, $2)
	RETURNING *;
  `, params.Email, list).Scan(&user.Email, &user.List)
	if err != nil {
		return nil, err
	}
	return &user, nil
}

func generateList(min, max int) []int {
	a := make([]int, max-min+1)
	for i := range a {
		a[i] = min + i
	}
	return a
}

Verifying the database

Now after doing that we can doencore rungo to localhost:4060 and try hitting the registerUser Endpoint with the request to call the API

quick note:To check the database data, you can doencore db conn-uri ${service name}in this case service name is bingo so it should beencore db conn-uri bingo, then you can take this URL and use it in postico or some other postgreSQL UI app to see the table and the rows, also you can use the PSQL command in mac (you need to install it first with brew)

Selecting a number and updating the array

Now the next task is to get the list array from a user, then select a random number from the list, remove that number from that list, updating it in the database and return the selected number! so let’s get started, we will do this code in the same bingo.go file:

//encore:api public
func DrawFromList(ctx context.Context, params *UserParams) (*DrawResponse, error) {
	var list []int // empty list to be asssigned after the SQL query
	err := sqldb.QueryRow(ctx, `
	SELECT list from "client"
	WHERE email = ($1)`, params.Email).Scan(&list)
	if err != nil {
		return nil, err
	}
  var response DrawResponse
  // generate a random number everytime you hit the request 
	rand.Seed(time.Now().UnixNano())
	var randomIndex = rand.Intn(len(list) - list[0] + 1 + list[0])
  // assign the random number to the response selected struct
	response.Selected = list[randomIndex]
  //remove the item from the array by the index
	list = RemoveIndex(list, randomIndex)
  // update the list of the user with the new one without the selected item
	sqldb.QueryRow(ctx, `
	UPDATE "client"
	SET list = ($1)
	WHERE email = ($2)`, list, params.Email)
	response.Removed = true
	return &response, nil
}

//Function to remove an item from an array by the index
func RemoveIndex(s []int, index int) []int {
	return append(s[:index], s[index+1:]...)
}

After this we can commit what we did, and then rungit push encoreto build the project and deploy it inside encore.dev platform (which uses a free tier of aws under the hood) and if we go toencore.devand select the project you should see a build completed git push encorebuild completed

Also, to check the database in production, we can useencore db shell bingo --env=prodand from here we will be able to see our database with psql from the terminal

So TLDR:

In this tuto we created a encore.dev backend app from scratch which uses two routes to create a user based on an email and assigns a list of a 100 numbers to it, and another route to draw a random number and remove it from that user array list

you can test this API on this urlhttps://bingo-c7m2.encoreapi.com/prod/bingo.RegisterUserwith this CURL to register:

curl --location --request POST 'https://bingo-c7m2.encoreapi.com/prod/bingo.RegisterUser' \
--header 'Content-Type: application/json' \
--data-raw '{
    "Email": "your-mail@gg.com"
}'

and this CURL to draw a number

curl --location --request POST 'https://bingo-c7m2.encoreapi.com/prod/bingo.DrawFromList' \
--header 'Content-Type: application/json' \
--data-raw '{
    "Email": "your-mail@gg.com"
}'

Sources

cd …