backend_master_class
Table accounts as A {
id bigserial [pk]
owner varchar [not null]
balance bigint [not null]
currency varchar [not null]
created_at timestamptz [not null, default: `now()`]
Indexes {
owner
}
}
Table entries {
id bigserial [pk]
account_id bigint [ref: > A.id, not null]
amount bigint [not null, note: 'can be negative or positive']
created_at timestamptz [not null, default: `now()`]
Indexes {
account_id
}
}
Table transfers {
id bigserial [pk]
from_account_id bigint [ref: > A.id, not null]
to_account_id bigint [ref: > A.id, not null]
amount bigint [not null, note: 'must be positive']
created_at timestamptz [not null, default: `now()`]
Indexes {
from_account_id
to_account_id
(from_account_id, to_account_id)
}
}
Then save the export to postgres
CREATE TABLE "accounts" (
"id" bigserial PRIMARY KEY,
"owner" varchar NOT NULL,
"balance" bigint NOT NULL,
"currency" varchar NOT NULL,
"created_at" timestamptz NOT NULL DEFAULT (now())
);
CREATE TABLE "entries" (
"id" bigserial PRIMARY KEY,
"account_id" bigint NOT NULL,
"amount" bigint NOT NULL,
"created_at" timestamptz NOT NULL DEFAULT (now())
);
CREATE TABLE "transfers" (
"id" bigserial PRIMARY KEY,
"from_account_id" bigint NOT NULL,
"to_account_id" bigint NOT NULL,
"amount" bigint NOT NULL,
"created_at" timestamptz NOT NULL DEFAULT (now())
);
CREATE INDEX ON "accounts" ("owner");
CREATE INDEX ON "entries" ("account_id");
CREATE INDEX ON "transfers" ("from_account_id");
CREATE INDEX ON "transfers" ("to_account_id");
CREATE INDEX ON "transfers" ("from_account_id", "to_account_id");
COMMENT ON COLUMN "entries"."amount" IS 'can be negative or positive';
COMMENT ON COLUMN "transfers"."amount" IS 'must be positive';
ALTER TABLE "entries" ADD FOREIGN KEY ("account_id") REFERENCES "accounts" ("id");
ALTER TABLE "transfers" ADD FOREIGN KEY ("from_account_id") REFERENCES "accounts" ("id");
ALTER TABLE "transfers" ADD FOREIGN KEY ("to_account_id") REFERENCES "accounts" ("id");
Golang migration
In this part we watch how to create a migration file and run it.
CLI: https://github.com/golang-migrate/migrate
- Install with brew on macos:
brew install golang-migrate
- create migrations:
migrate create -ext sql -dir db/migrations -seq <migration_name>
sqlc
This a CLI for make easy creation of models and manipulate with simple SQL for data manipulation.
url: https://docs.sqlc.dev/en/stable/tutorials/getting-started-postgresql.html
- Install the CLI:
brew install sqlc
- Test if the cli was installed:
sqlc version
- Inits the sqlc configuration file:
sqlc init
this generate a empty configuration file. Then copy and paste this config:
version: 1
packages:
- path: "./db/sqlc"
name: "db"
engine: "postgresql"
schema: "./db/migrations/"
queries: "./db/query/"
emit_json_tags: true
emit_prepared_queries: false
emit_interface: false
emit_exact_table_names: false
- path: indicate that where the cli generate the code
- name: package name
- engine: the database engine, in this case
postgresql
- schema: the folder where is the schema of the database for generate the
models.go
- queries: the folder where is at minimum one query. we have to specified the action and query like this:
-- name: GetAuthor :one // many or exec
SELECT * FROM authors
WHERE id = $1 LIMIT 1;
- Generate code with the CLI:
sqlc generate
this create the followings files:
db.go
<model>.sql.go
for example account.sql.go
models.go
from the schema create structs.
Test
use this lib: https://github.com/stretchr/testify
Popular web frameworks
Popular HTTP routers
Gin
- Install `go get -u github.com/gin-gonic/gin``
GOMOCK
Install
We use the CLI por generate the mock file
- Go to https://github.com/golang/mock for review the new form of install, at this moment is executing this:
go install github.com/golang/mock/mockgen@v1.6.0
- Generate the mock:
mockgen -package mockdb -destination db/mock/store.go github.com/wlensinas/backend_master_class/db/sqlc Store
If you execute one test function and need to watch the verbouse output:
- Edit Preferences: command+p
- Enter
> Preferences: Open Settings (JSON)
- Add this line
"go.testFlags": ["-v"]
- Save it
Executes again and them you have this kind of output:
Running tool: /usr/local/bin/go test -timeout 30s -run ^TestGetAccountAPI$ github.com/wlensinas/backend_master_class/api -v
=== RUN TestGetAccountAPI
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] POST /accounts --> github.com/wlensinas/backend_master_class/api.(*Server).createAccount-fm (3 handlers)
[GIN-debug] GET /accounts/:id --> github.com/wlensinas/backend_master_class/api.(*Server).getAccount-fm (3 handlers)
[GIN-debug] GET /accounts --> github.com/wlensinas/backend_master_class/api.(*Server).ListAccounts-fm (3 handlers)
[GIN] 2022/06/13 - 15:17:00 | 200 | 250.307µs | | GET "/accounts/27"
--- PASS: TestGetAccountAPI (0.00s)
PASS
ok github.com/wlensinas/backend_master_class/api 0.651s
Docker
Commands
- Pull postgresql image
docker pull postgres:12-alpine
- List images:
docker images
- Run docker image:
docker run --name postgres12 -p 5432:5432 -e POSTGRES_USER=root -e POSTGRES_PASSWORD=secret -d postgres:12-alpine
- Connect to the container:
docker exec -it postgres12 psql -U root
- Execute some select for testing porpouse:
select now();
and then quit \q
Tips:
- delete images with ´none´ in the name:
docker rmi $(docker images | tail -n +2 | awk '$1 == "<none>" {print $'3'}')
- Inspect container postgres12:
docker container inspect postgres12
- Inspect container app:
docker container inspect simplebank
Operation with containers dockers:
- stop container:
docker stop <name or hash>
in this case docker stop postgres12
- list containers actives and stopped:
docker ps -a
- start the container:
docker start <name or hash>
- enter to the container terminal:
docker exec -it <name or hash> /bin/sh
- create db without enter to the container:
docker exec -it <name or hash> createdb --username=root --owner=root name_db
For example: docker exec -it postgres12 createdb --username=root --owner=root simple_bank
For development in docker
- Create network:
docker network create bank-network
- Connect postgres12 container to the network:
docker network connect bank-network postgres12
- Build the image for the app:
docker build -t simplebank:latest .
- Execute the app for development:
docker run --name simplebank --network bank-network -p 8080:8080 -e DB_SOURCE="postgresql://root:secret@postgres12:5432/simple_bank?sslmode=disable" simplebank:latest
- Execute the app for test production:
docker run --name simplebank --network bank-network -p 8080:8080 -e GIN_MODE=release -e DB_SOURCE="postgresql://root:secret@postgres12:5432/simple_bank?sslmode=disable" simplebank:latest
Wait-for
https://github.com/eficode/wait-for
gRPC
Tutorial Quick start: https://grpc.io/docs/languages/go/quickstart/
Install
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
client
gRPC client: https://github.com/ktr0731/evans
Install
macOS:
brew tap ktr0731/evans
brew install evans
Random commands
create a string with 32 characters random style: openssl rand -hex 64 | head -c 32
gRPC Gateway
Source: https://github.com/grpc-ecosystem/grpc-gateway
install
- Generate a file with:
// +build tools
package tools
import (
_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway"
_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2"
_ "google.golang.org/grpc/cmd/protoc-gen-go-grpc"
_ "google.golang.org/protobuf/cmd/protoc-gen-go"
)
- Execute
go mod tidy
- Install binaries:
go install \
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \
google.golang.org/protobuf/cmd/protoc-gen-go \
google.golang.org/grpc/cmd/protoc-gen-go-grpc