Skip to content

Movie API with CRUD/Filtering/Listing/Pagination features

Notifications You must be signed in to change notification settings

TomiwaAribisala-git/greenlight

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

About

Movie API with CRUD/Filtering/Listing/Pagination features

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published