Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ CORS_MAX_AGE=300

DB_HOST=db
DB_PORT=5432
DB_USER=myapp_user
DB_PASS=myapp_pass
DB_NAME=myapp_db
DB_USER=book-service-user
DB_PASS=book-service-pass
DB_NAME=book-service-db
DB_DEBUG=true
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ RUN go mod download
COPY . .

RUN go build -ldflags '-w -s' -a -o ./bin/app ./cmd/app \
&& go build -ldflags '-w -s' -a -o ./bin/migrate ./cmd/migrate
&& go build -tags=embed -ldflags '-w -s' -a -o ./bin/migration ./cmd/migration

CMD ["/myapp/bin/app"]
EXPOSE 8080
4 changes: 2 additions & 2 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ app:
go run ./cmd/app

# Run DB migration CLI (defaults to up)
migrate cmd="up":
migration *cmd="up":
@export $(grep -v '^#' .env | xargs) && \
DB_HOST={{ db_host }} \
go run ./cmd/migrate {{ cmd }}
go run ./cmd/migration -dir={{ justfile_directory() }}/cmd/migration/migrations {{ cmd }}

# Run docker compose build
build:
Expand Down
27 changes: 14 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,18 +107,18 @@ To keep this simple, we use only a single database table named `books`.

```just
MYAPP
help # List available commands
install # Install development tools
app # Run server app
migrate cmd="up" # Run DB migration CLI (defaults to up)
build # Run docker compose build
up cmd="" # Run docker compose up
down # Run docker compose down
lint # Run lints using gofumpt, go vet, staticcheck and govulncheck
test # Run tests
gen # Run go generate for all packages
apidoc # Generate openapi.yaml
repos # Generate gorm repositories using gorm cli
help # List available commands
install # Install development tools
app # Run server app
migration cmd="up" # Run DB migration CLI (defaults to up)
build # Run docker compose build
up cmd="" # Run docker compose up
down # Run docker compose down
lint # Run lints using gofumpt, go vet, staticcheck and govulncheck
test # Run tests
gen # Run go generate for all packages
apidoc # Generate openapi.yaml
repos # Generate gorm repositories using gorm cli
```

## Sample Request Logs
Expand Down Expand Up @@ -166,8 +166,9 @@ app-1 | {"level":"info","request_id":"d5mqjmhqvtmc73foh3dg","received_time":"20
├── cmd
│ ├── app
│ │ └── main.go
│ └── migrate
│ └── migration
│ ├── main.go
│ ├── embed.go
│ └── migrations
│ └── 00001_create_books_table.sql
Expand Down
30 changes: 15 additions & 15 deletions app/book/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ func New(validator *validator.Validate, db *gorm.DB) *Handler {
}

func (h *Handler) Register(r chi.Router) {
r.Get("/", h.List)
r.With(m.Validate[form.BookForm](h.validator)).Post("/", h.Create)
r.Get("/{id}", h.Read)
r.With(m.Validate[form.BookForm](h.validator)).Put("/{id}", h.Update)
r.Delete("/{id}", h.Delete)
r.Get("/", h.list)
r.With(m.Validate[form.BookForm](h.validator)).Post("/", h.create)
r.Get("/{id}", h.read)
r.With(m.Validate[form.BookForm](h.validator)).Put("/{id}", h.update)
r.Delete("/{id}", h.delete)
}

// List godoc
// list godoc
//
// @summary List books
// @description List books
Expand All @@ -52,7 +52,7 @@ func (h *Handler) Register(r chi.Router) {
// @success 200 {array} model.Book
// @failure 500 {object} e.Error
// @router /books [get]
func (h *Handler) List(w http.ResponseWriter, r *http.Request) {
func (h *Handler) list(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
logger := hlog.FromRequest(r)

Expand All @@ -76,7 +76,7 @@ func (h *Handler) List(w http.ResponseWriter, r *http.Request) {
}
}

// Create godoc
// create godoc
//
// @summary Create book
// @description Create book
Expand All @@ -89,7 +89,7 @@ func (h *Handler) List(w http.ResponseWriter, r *http.Request) {
// @failure 422 {object} e.Errors
// @failure 500 {object} e.Error
// @router /books [post]
func (h *Handler) Create(w http.ResponseWriter, r *http.Request) {
func (h *Handler) create(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
logger := hlog.FromRequest(r)

Expand Down Expand Up @@ -117,7 +117,7 @@ func (h *Handler) Create(w http.ResponseWriter, r *http.Request) {
logger.Info().Str("id", book.ID.String()).Msg("new book created")
}

// Read godoc
// read godoc
//
// @summary Read book
// @description Read book
Expand All @@ -130,7 +130,7 @@ func (h *Handler) Create(w http.ResponseWriter, r *http.Request) {
// @failure 404
// @failure 500 {object} e.Error
// @router /books/{id} [get]
func (h *Handler) Read(w http.ResponseWriter, r *http.Request) {
func (h *Handler) read(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
logger := hlog.FromRequest(r)

Expand Down Expand Up @@ -159,7 +159,7 @@ func (h *Handler) Read(w http.ResponseWriter, r *http.Request) {
}
}

// Update godoc
// update godoc
//
// @summary Update book
// @description Update book
Expand All @@ -174,7 +174,7 @@ func (h *Handler) Read(w http.ResponseWriter, r *http.Request) {
// @failure 422 {object} e.Errors
// @failure 500 {object} e.Error
// @router /books/{id} [put]
func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
func (h *Handler) update(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
logger := hlog.FromRequest(r)

Expand Down Expand Up @@ -212,7 +212,7 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
logger.Info().Str("id", id.String()).Msg("book updated")
}

// Delete godoc
// delete godoc
//
// @summary Delete book
// @description Delete book
Expand All @@ -225,7 +225,7 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
// @failure 404
// @failure 500 {object} e.Error
// @router /books/{id} [delete]
func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
func (h *Handler) delete(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
logger := hlog.FromRequest(r)

Expand Down
87 changes: 0 additions & 87 deletions cmd/migrate/main.go

This file was deleted.

55 changes: 55 additions & 0 deletions cmd/migration/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package main

import (
"context"
"flag"
"fmt"
"log"
"os"

_ "github.com/jackc/pgx/v5/stdlib"
"github.com/pressly/goose/v3"

"myapp/config"
)

const (
dialect = "pgx"
fmtDBString = "host=%s user=%s password=%s dbname=%s port=%d sslmode=disable"
)

var (
flags = flag.NewFlagSet("migration", flag.ExitOnError)
dir = flags.String("dir", "migrations", "directory with migration files")
)

func main() {
flags.Usage = usage
flags.Parse(os.Args[1:])

args := flags.Args()
if len(args) == 0 || args[0] == "-h" || args[0] == "--help" {
flags.Usage()
return
}

command := args[0]

c := config.NewDB()
dbString := fmt.Sprintf(fmtDBString, c.Host, c.Username, c.Password, c.DBName, c.Port)

db, err := goose.OpenDBWithDriver(dialect, dbString)
if err != nil {
log.Fatal(err.Error())
}

defer func() {
if err := db.Close(); err != nil {
log.Fatal(err.Error())
}
}()

if err := goose.RunContext(context.Background(), command, db, *dir, args[1:]...); err != nil {
log.Fatalf("migration %v: %v", command, err)
}
}
36 changes: 36 additions & 0 deletions cmd/migration/usage_embed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//go:build embed

package main

import (
"embed"
"fmt"

"github.com/pressly/goose/v3"
)

//go:embed migrations/*.sql
var migrations embed.FS

func init() {
goose.SetBaseFS(migrations)
}

func usage() {
fmt.Println(usageCommands)
}

var usageCommands = `Usage: migration COMMAND
Examples:
migration status

Commands:
up Migrate the DB to the most recent version available
up-by-one Migrate the DB up by 1
up-to VERSION Migrate the DB to a specific VERSION
down Roll back the version by 1
down-to VERSION Roll back to a specific VERSION
redo Re-run the latest migration
reset Roll back all migrations
status Dump the migration status for the current DB
version Print the current version of the database`
32 changes: 32 additions & 0 deletions cmd/migration/usage_no_embed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//go:build !embed

package main

import "fmt"

func usage() {
fmt.Println(usagePrefix)
flags.PrintDefaults()
fmt.Println(usageCommands)
}

var (
usagePrefix = `Usage: migration COMMAND
Examples:
migration create initial_tables sql
`

usageCommands = `
Commands:
create NAME [sql|go] Creates new migration file with the current timestamp
fix Apply sequential ordering to migrations
up Migrate the DB to the most recent version available
up-by-one Migrate the DB up by 1
up-to VERSION Migrate the DB to a specific VERSION
down Roll back the version by 1
down-to VERSION Roll back to a specific VERSION
redo Re-run the latest migration
reset Roll back all migrations
status Dump the migration status for the current DB
version Print the current version of the database`
)
Loading