A basic web backend framework with REST API written in Go, this application contains the following functions
- Web server wi/wo HTTPS
- Basic REST API with CURD operations
- Manipulate data in memroy (articles) and mysql database (posts)
- Authentication for
posts
resource - Dev mode supporting hot-reload with
realize
, production mode by deploying compiled binary to container - Run webapi, database and phpmyadmin in different containers
- Customized logging
- Two ways of
go test
(more tests to be added)
git clone https://github.com/yangpeng-chn/go-web-framework.git
$ vi conf.json
"EnableHTTPS": true,
-
Prepare
certs
dir$ cd go-web-framework $ mkdir certs $ cd certs
-
Genereate Private key
$ openssl genrsa -out server.key 2048
-
Generation of self-signed(x509) public key (PEM-encodings .pem|.crt) based on the private (.key)
$ openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
$ export GOFLAGS=-mod=vendor
$ export GO111MODULE=on
$ go mod init github.com/yangpeng-chn/go-web-framework (go.mod generated)
$ go mod vendor (go.mod updated, go.sum generated, packages downloaded in $GOPATH/pkg, vendor generated with packages)
Dev mode
-
Start service with
go run main.go
(without hot-reload and docker)$ go run main.go 2020-04-29 00:26:52 Listening on port 4201 ... [OK]
-
Start service with
realize
with hot-reload and without docker, database not available$ vi conf.json "UseDatabase": false, $ GO111MODULE=off go get github.com/oxequa/realize $ vi ~/.zprofile $ source ~/.zprofile --- export PATH=$PATH:$GOPATH/bin --- $ which realize $ realize start --run # also works without --run [20:45:52][API] : Watching 157 file/s 45 folder/s [20:45:52][API] : Build started [20:45:52][API] : Build completed in 0.546 s [20:45:52][API] : Running.. [20:45:53][API] : 2020-05-01 20:45:53 Listening on port 4201 ... [OK]
-
Start with docker-compose (hot-reload supported)
$ docker-compose up --build Building app Step 1/7 : FROM golang:1.14 ---> 2421885b04da Step 2/7 : RUN go get github.com/oxequa/realize ---> Using cache ---> 2131ca7f8662 Step 3/7 : ENV APP_HOME /app ---> Using cache ---> a6a9a670c9cb Step 4/7 : RUN mkdir -p $APP_HOME ---> Using cache ---> c2467a23fca1 Step 5/7 : WORKDIR $APP_HOME ---> Using cache ---> 0356ac1555ac Step 6/7 : EXPOSE 4201 ---> Using cache ---> 7879635444d2 Step 7/7 : CMD [ "realize", "start", "--run" ] ---> Using cache ---> cc1b8be1f056 Successfully built cc1b8be1f056 Successfully tagged go-web-framework_app:latest Starting db_mysql ... done Starting phpmyadmin ... done Starting full_app ... done Attaching to db_mysql, phpmyadmin, full_app ... full_app | [11:46:28][API] : Watching 157 file/s 45 folder/s full_app | [11:46:28][API] : Build started db_mysql | 2020-05-01T11:46:28.154363Z 0 [Note] Event Scheduler: Loaded 0 events db_mysql | 2020-05-01T11:46:28.156387Z 0 [Note] mysqld: ready for connections. db_mysql | Version: '5.7.29' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server (GPL) full_app | [11:46:40][API] : Build completed in 12.178 s full_app | [11:46:40][API] : Running.. full_app | [11:46:40][API] : 2020-05-01 11:46:40 Listening on port 4201 ... [OK] (stop) $ docker-compose down --remove-orphans --volumes
Production mode
$ vi docker-compose.yml
dockerfile: Dockerfile.dev -> dockerfile: Dockerfile
$ docker-compose up --build
(stop)
$ docker-compose down --remove-orphans --volumes
Articles are stored in memory while posts are stored in database
-
Get articles
curl -X GET http://localhost:4201/v1/articles [{"id":1,"title":"title1","content":"content1"},{"id":2,"title":"title2","content":"content2"},{"id":3,"title":"title3","content":"content3"}]
-
Get article
curl -X GET http://localhost:4201/v1/articles/1 {"id":1,"title":"title1","content":"content1"}
-
Add article
curl -X POST http://localhost:4201/v1/articles -d '{"id":4,"title": "title4","content":"content4"}'{"id":4,"title":"title4","content":"content4"}
-
Update articles
curl -X PUT http://localhost:4201/v1/articles/4 -d '{"id":4,"title":"updated-title","content":"updated-content"}'{"id":4,"title":"updated-title","content":"updated-content"}
-
Delete article
curl -X DELETE http://localhost:4201/v1/articles/4 {"code":200,"msg":"OK"} curl -X DELETE http://localhost:4201/v1/articles/5 {"error":"article not found"}
-
Get posts
curl -X GET http://localhost:4201/v1/posts [{"id":1,"title":"Title 1","content":"Hello world 1","author":{"id":1,"nickname":"Yang","email":"yang@gmail.com","password":"password","created_at":"2020-04-29T14:54:36Z","updated_at":"2020-04-29T14:54:36Z"},"author_id":1,"created_at":"2020-04-29T14:54:36Z","updated_at":"2020-04-29T14:54:36Z"},{"id":2,"title":"Title 2","content":"Hello world 2","author":{"id":2,"nickname":"Martin Luther","email":"luther@gmail.com","password":"password","created_at":"2020-04-29T14:54:36Z","updated_at":"2020-04-29T14:54:36Z"},"author_id":2,"created_at":"2020-04-29T14:54:36Z","updated_at":"2020-04-29T14:54:36Z"}]
-
Get post
curl -X GET http://localhost:4201/v1/posts/1 {"id":1,"title":"Title 1","content":"Hello world 1","author":{"id":1,"nickname":"Yang","email":"yang@gmail.com","password":"password","created_at":"2020-04-29T14:54:36Z","updated_at":"2020-04-29T14:54:36Z"},"author_id":1,"created_at":"2020-04-29T14:54:36Z","updated_at":"2020-04-29T14:54:36Z"}
-
Add post
curl -X POST http://localhost:4201/v1/posts -d '{"id":3,"title":"title 3","content":"content 3"} ...'
-
Update post
curl -X PUT http://localhost:4201/v1/posts/1 -d '{"id":1,"title":"updated-title","content":"updated-content"}'{"error":"Unauthorized"}
-
Delete post
curl -X DELETE http://localhost:4201/v1/posts/1 {"error":"Unauthorized"}
$ go test tests/article_selfserve_test.go
ok command-line-arguments 0.020s
$ go run main.go
$ go test tests/article_test.go
ok command-line-arguments 0.020s
OK
{
"Time": "2019-08-03 17:03:37",
"Message": "OK",
"ResponseCode": 200,
"Action": "GetArticlesHandler",
"Method": "GET",
"URI": "/v1/articles",
"RequestData": ""
}
Error
{
"Time": "2019-08-14 00:08:47",
"Message": "article not found",
"ResponseCode": 400,
"Action": "DeleteArticleHandler",
"Method": "DELETE",
"URI": "/v1/articles/5",
"RequestData": ""
}
-
Contents in container when using
Dockerfile.dev
anddocker-compose.yml
⮀ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
32e5d0bb8d85 go-web-framework_app "realize start --run" 50 seconds ago Up 49 seconds 0.0.0.0:4201->4201/tcp full_app
4360effe1a7d phpmyadmin/phpmyadmin "/docker-entrypoint.…" 4 minutes ago Up 49 seconds 0.0.0.0:9090->80/tcp phpmyadmin
555d1f9a5228 mysql:5.7 "docker-entrypoint.s…" 4 minutes ago Up 50 seconds 0.0.0.0:3306->3306/tcp, 33060/tcp db_mysql
⮀ docker exec -it 32e5d0bb8d85 sh
# pwd; ls
/build
Dockerfile Dockerfile.dev README.md certs conf.json controllers docker-compose.yml go.mod go.sum main main.go middlewares models tests utils
- Other command
# docker build -t myapp-deploy -fDockerfile .
# docker run -it -p 4201:4201 myapp-deploy