Skip to content

Latest commit

 

History

History
560 lines (439 loc) · 11 KB

README.md

File metadata and controls

560 lines (439 loc) · 11 KB

Building APIs and Web Apps with Golang

General Commands

make help
make run

Installing HTTP Router

go get github.com/julienschmidt/httprouter@v1

Run API

go run ./cmd/api
go run ./cmd/api -help

Handlers Requests

curl -i localhost:4000/v1/healthcheck 
curl -i -X OPTIONS localhost:4000/v1/healthcheck

Show all Movies

curl -i -X POST localhost:4000/v1/movies

Show a Movie

curl -i localhost:4000/v1/movies/123

Supported Go types to JSON types

  • bool ⇒ JSON boolean
  • string ⇒ JSON string
  • int*, uint*, float*, rune ⇒ JSON number
  • array, slice ⇒ JSON array
  • struct, map ⇒ JSON object
  • slice ofstucts ⇒ array of objects
  • nil pointers, interface values, slices, maps, etc. ⇒ JSON null
  • chan, func, complex* ⇒ Not supported
  • time.Time ⇒ RFC3339-format JSON string
  • []byte ⇒ Base64-encoded JSON string
  • Encoding of nested objects is supported. So, for example, if you have a slice of structs in Go that will encode to an array of objects in JSON.
  • Any pointer values will encode as the value pointed to.
  • Channels, functions and complex number types cannot be encoded. If you try to do so you’ll get a json.UnsupportedTypeError error at runtime.

Creating a Movie

BODY='{"title":"Moana","year":2016,"runtime":107, "genres":["animation","adventure"]}'
curl -i -d "$BODY" localhost:4000/v1/movies

JSON type to Supported Go types

  • JSON boolean ⇒ bool
  • JSON string ⇒ string
  • JSON number ⇒ int*, uint*, float*, rune
  • JSON array ⇒ array, slice
  • JSON object ⇒ struct, map

Installing Postgres

sudo apt install postgresql
psql --version
sudo -u postgres psql
psql --host=localhost --dbname=greenlight --username=greenlight
export GREENLIGHT_DB_DSN='postgres://greenlight:pa55word@localhost:5432/greenlight?sslmode=disable'
echo $GREENLIGHT_DB_DSN
SELECT current_user

Install Database Driver

go get github.com/lib/pq@v1

SQL Migrations

curl -s https://packagecloud.io/install/repositories/golang-migrate/migrate/script.deb.sh | sudo bash
$ apt-get update
$ apt-get install -y migrate
migrate -version
migrate create -seq -ext=.sql -dir=./migrations create_movies_table
migrate create -seq -ext=.sql -dir=./migrations add_movies_check_constraints
migrate -path=./migrations -database=$GREENLIGHT_DB_DSN up
migrate -path=./migrations -database=$EXAMPLE_DSN version
migrate -path=./migrations -database=$EXAMPLE_DSN goto 1
migrate -path=./migrations -database=$EXAMPLE_DSN up
migrate -path=./migrations -database=$EXAMPLE_DSN down
\dt
SELECT * FROM schema_migrations;
\d movies

Database Insert A Movie

BODY='{"title":"Moana","year":2016,"runtime":107, "genres":["animation","adventure"]}'
curl -i -d "$BODY" localhost:4000/v1/movies
BODY='{"title":"Black Panther","year":2018,"runtime":134,"genres":["action","adventure"]}'
curl -i -d "$BODY" localhost:4000/v1/movies
BODY='{"title":"Deadpool","year":2016, "runtime":108,"genres":["action","comedy"]}'
curl -i -d "$BODY" localhost:4000/v1/movies
BODY='{"title":"The Breakfast Club","year":1986, "runtime":96,"genres":["drama"]}'
curl -i -d "$BODY" localhost:4000/v1/movies
SELECT * FROM movies;

Database Fetch A Movie

curl -i localhost:4000/v1/movies/2

Database PUT Update A Movie

BODY='{"title":"Black Panther","year":2018,"runtime":134,"genres":["sci-fi","action","adventure"]}'
curl -X PUT -d "$BODY" localhost:4000/v1/movies/2

Database PATCH Update A Movie

curl -X PATCH -d '{"year": 1985}' localhost:4000/v1/movies/4

Database Concurrent Update A Movie

xargs -I % -P8 curl -X PATCH -d '{"runtime":97}' "localhost:4000/v1/movies/4" < <(printf '%s\n' {1..8})

Database Delete A Movie

curl -X DELETE localhost:4000/v1/movies/3

Database Filter Request

  • When using curl to send a request containing more than one query string parameter, you must wrap the URL in quotes for it to work correctly.
curl "localhost:4000/v1/movies?title=moana&genres=animation,adventure&page=1&page_size=5&sort=year"

Database Listing Data

curl localhost:4000/v1/movies

Database Filtering Lists

curl "localhost:4000/v1/movies?title=black+panther"
curl "localhost:4000/v1/movies?genres=adventure"
curl "localhost:4000/v1/movies?title=moana&genres=animation,adventure"

Database Full Text Search

curl "localhost:4000/v1/movies?title=panther"
curl "localhost:4000/v1/movies?title=the+club"

Database GIN Indexes

migrate create -seq -ext .sql -dir ./migrations add_movies_indexes'
migrate -path ./migrations -database $GREENLIGHT_DB_DSN up

Database Sorting Data

curl "localhost:4000/v1/movies?sort=-title"
curl "localhost:4000/v1/movies?sort=-runtime"

Database Paginating Lists

curl "localhost:4000/v1/movies?page_size=2"
curl "localhost:4000/v1/movies?page_size=2&page=2"

Returning Pagination Metadata

curl "localhost:4000/v1/movies?page=1&page_size=2"
$ curl localhost:4000/v1/movies?genres=adventure

Rate Limiter

go get golang.org/x/time/rate@latest
for i in {1..6}; do curl http://localhost:4000/v1/healthcheck; done
go run ./cmd/api/ -limiter-enabled=false

User Model Setup And Regristration

migrate create -seq -ext=.sql -dir=./migrations create_users_table
migrate -path=./migrations -database=$GREENLIGHT_DB_DSN up

Encrypting User Passwords

go get golang.org/x/crypto/bcrypt@latest

Regristering New Users

BODY='{"name": "Alice Smith", "email": "[email protected]", "password": "pa55word"}'
curl -i -d "$BODY" localhost:4000/v1/users

Install Mail Package

go get github.com/go-mail/mail/v2@v2

Send Users Emails

BODY='{"name": "Bob Jones", "email": "[email protected]", "password": "pa55word"}'
curl -w '\nTime: %{time_total}\n' -d "$BODY" localhost:4000/v1/users

Send Background Emails

BODY='{"name": "Godae Hill", "email": "[email protected]", "password": "pa55word"}'
curl -w '\nTime: %{time_total}\n' -d "$BODY" localhost:4000/v1/users
BODY='{"name": "Carol Smith", "email": "[email protected]", "password": "pa55word"}'
curl -w '\nTime: %{time_total}\n' -d "$BODY" localhost:4000/v1/users
BODY='{"name": "Dave Smith", "email": "[email protected]", "password": "pa55word"}'
curl -w '\nTime: %{time_total}\n' -d "$BODY" localhost:4000/v1/users

Activate User: Set up Tokens Database Table

migrate create -seq -ext .sql -dir ./migrations create_tokens_table
migrate -path=./migrations -database=$GREENLIGHT_DB_DSN up

Register User: Send Activation Token

BODY='{"name": "Faith Smith", "email": "[email protected]", "password": "pa55word"}'
curl -w '\nTime: %{time_total}\n' -d "$BODY" localhost:4000/v1/users

Activate User with Email Activation Token

curl -X PUT -d '{"token": "ZYGQTPU5PKKJRY7SFOAMKXPGQY"}' localhost:4000/v1/users/activated

Generating Authentication Token

BODY='{"email": "[email protected]", "password": "pa55word"'
curl -i -d "$BODY" localhost:4000/v1/tokens/authentication

Authenticate Authentication Token with Authorization Header

curl -d '{"email": "[email protected]", "password": "pa55word"}' localhost:4000/v1/tokens/authentication
curl -i -H "Authorization: Bearer XXXXXXXXXXXXXXXXXXXXXXXXXX" localhost:4000/v1/healthcheck

Authenticate Authentication Token with Authorization Header of Activated User

SELECT email FROM users WHERE activated = true;
BODY='{"email": "[email protected]", "password": "pa55word"}'
curl -i -H "Authorization: Bearer XXXXXXXXXXXXXXXXXXXXXXXXXX" localhost:4000/v1/movies/1

Permissions SQL Migrations

migrate create -seq -ext .sql -dir ./migrations add_permissions
migrate -path ./migrations -database $GREENLIGHT_DB_DSN up

Set Read/Write Permissions for a User and give access

-- Set the activated field for [email protected] to true.
UPDATE users SET activated = true WHERE email = '[email protected]';
-- Give all users the 'movies:read' permission
INSERT INTO users_permissions
SELECT id, (SELECT id FROM permissions WHERE code = 'movies:read') FROM users;
-- Give [email protected] the 'movies:write' permission
INSERT INTO users_permissions
    VALUES (
    (SELECT id FROM users WHERE email = '[email protected]'),
    (SELECT id FROM permissions WHERE code = 'movies:write')
);
-- List all activated users and their permissions.
SELECT email, array_agg(permissions.code) as permissions
FROM permissions
INNER JOIN users_permissions ON users_permissions.permission_id = permissions.id
INNER JOIN users ON users_permissions.user_id = users.id
WHERE users.activated = true
GROUP BY email;
BODY='{"email": "[email protected]", "password": "pa55word"}'
curl -d "$BODY" localhost:4000/v1/tokens/authentication
curl -H "Authorization: Bearer XXXXXXXXXXXXXXXXXXXXXXXXXX" localhost:4000/v1/movies/1
curl -X DELETE -H "Authorization: Bearer XXXXXXXXXXXXXXXXXXXXXXXXXX" localhost:4000/v1/movies/1
BODY='{"email": "[email protected]", "password": "pa55word"}' 
curl -d "$BODY" localhost:4000/v1/tokens/authentication
curl -H "Authorization: Bearer XXXXXXXXXXXXXXXXXXXXXXXXXX" localhost:4000/v1/movies/1
curl -X DELETE -H "Authorization: Bearer XXXXXXXXXXXXXXXXXXXXXXXXXX" localhost:4000/v1/movies/1

Grant Permissions to New User which registers an account

BODY='{"name": "Grace Smith", "email": "[email protected]", "password": "pa55word"}'
curl -d "$BODY" localhost:4000/v1/users
SELECT email, code FROM users
INNER JOIN users_permissions ON users.id = users_permissions.user_id
INNER JOIN permissions ON users_permissions.permission_id = permissions.id
WHERE users.email = '[email protected]';

Activating CORS

go run ./cmd/examples/cors/simple

Makefile Quality Control

go install honnef.co/go/tools/cmd/staticcheck@latest
which staticcheck

Display binary version number

./bin/api -version

Basic Load Tests

go install github.com/rakyll/hey@latest
go run ./cmd/api/ -limiter-enabled=false
BODY='{"email": "[email protected]", "password": "pa55word"}'
hey -d "$BODY" -m "POST" http://localhost:4000/v1/tokens/authentication

Monitoring Metrics

curl http://localhost:4000/debug/vars