Skip to content

Commit 0c5c35b

Browse files
Merge pull request #21 from DoWithLogic/feat/healthcheck-and-graceful-shutdown
chore: add graceful Shutdown and healthcheck
2 parents 0ed538d + 5ae1831 commit 0c5c35b

File tree

5 files changed

+32
-9
lines changed

5 files changed

+32
-9
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ The DIP promotes high-level modules (e.g., use cases) to depend on abstractions
6565
- [x] [Go-Standards](https://github.com/golang-standards/project-layout) Project Layout
6666
- [x] Environment Variable Configuration
6767
- [x] Health-Check and Debug API
68-
- [ ] Graceful Shutdown
68+
- [x] Graceful Shutdown
6969
- Layered architecture
7070
- [x] [SOLID Principle](https://en.wikipedia.org/wiki/SOLID)
7171
- [x] Database Transaction

cmd/api/main.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,5 @@ func main() {
1919
panic(err)
2020
}
2121

22-
app := app.NewApp(context.Background(), cfg)
23-
if err := app.Start(); err != nil {
24-
panic(err)
25-
}
22+
app.NewApp(context.Background(), cfg).Run()
2623
}

internal/app/app.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import (
44
"context"
55
"fmt"
66
"os"
7+
"os/signal"
8+
"syscall"
9+
"time"
710

811
_ "github.com/go-sql-driver/mysql"
912

@@ -13,6 +16,7 @@ import (
1316
"github.com/DoWithLogic/golang-clean-architecture/pkg/otel/zerolog"
1417
"github.com/jmoiron/sqlx"
1518
"github.com/labstack/echo/v4"
19+
"github.com/labstack/gommon/log"
1620
)
1721

1822
type App struct {
@@ -36,13 +40,29 @@ func NewApp(ctx context.Context, cfg config.Config) *App {
3640
}
3741
}
3842

39-
func (app *App) Start() error {
40-
if err := app.StartService(); err != nil {
43+
func (app *App) Run() error {
44+
if err := app.startService(); err != nil {
4145
app.log.Z().Err(err).Msg("[app]StartService")
4246

4347
return err
4448
}
4549

50+
// Wait for interrupt signal to gracefully shutdown the server with a timeout of 10 seconds.
51+
// Use a buffered channel to avoid missing signals as recommended for signal.Notify
52+
quit := make(chan os.Signal, 1)
53+
signal.Notify(quit, syscall.SIGTERM)
54+
signal.Notify(quit, syscall.SIGINT)
55+
56+
go func() {
57+
<-quit
58+
log.Info("Server is shutting down...")
59+
60+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
61+
defer cancel()
62+
app.db.Close()
63+
app.echo.Shutdown(ctx)
64+
}()
65+
4666
app.echo.Debug = app.cfg.Server.Debug
4767
app.echo.Use(middleware.AppCors())
4868
app.echo.Use(middleware.CacheWithRevalidation)

internal/app/service.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
package app
22

33
import (
4+
"net/http"
5+
46
userV1 "github.com/DoWithLogic/golang-clean-architecture/internal/users/delivery/http/v1"
57
userRepository "github.com/DoWithLogic/golang-clean-architecture/internal/users/repository"
68
userUseCase "github.com/DoWithLogic/golang-clean-architecture/internal/users/usecase"
9+
"github.com/labstack/echo/v4"
710
)
811

9-
func (app *App) StartService() error {
12+
func (app *App) startService() error {
1013
userRepo := userRepository.NewRepository(app.db)
1114
userUC := userUseCase.NewUseCase(userRepo, app.cfg)
1215
userCTRL := userV1.NewHandlers(userUC)
1316

1417
domain := app.echo.Group("/api/v1/users")
18+
domain.GET("/ping", func(c echo.Context) error {
19+
return c.String(http.StatusOK, "Hello Word 👋")
20+
})
1521

1622
userCTRL.UserRoutes(domain, app.cfg)
1723

internal/users/delivery/http/v1/route.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
)
88

99
func (h *handlers) UserRoutes(domain *echo.Group, cfg config.Config) {
10-
domain.POST("", h.CreateUser)
10+
domain.POST("/", h.CreateUser)
1111
domain.POST("/login", h.Login)
1212
domain.GET("/detail", h.UserDetail, middleware.AuthorizeJWT(cfg))
1313
domain.PATCH("/update", h.UpdateUser, middleware.AuthorizeJWT(cfg))

0 commit comments

Comments
 (0)