Skip to content

Commit 43436da

Browse files
committed
feat: migration using sql files, boostrap dependencies, updated readme.
1 parent 36d1d34 commit 43436da

File tree

17 files changed

+448
-172
lines changed

17 files changed

+448
-172
lines changed

.env.example

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ POSTGRES_HOST=0.0.0.0
33
POSTGRES_USER=
44
POSTGRES_NAME=testdb
55
POSTGRES_PORT=15432
6-
PORT=3006
6+
PORT=2024
77

88

99
JWT_SCECRET=E24R43F34FC32345XZC

README.md

+147-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,151 @@
1-
# Gin Framework API boilerplate
1+
# Gin Boilerplate
22

3-
This is a microservice for the application, which is responsible for handling user-related functionality. It is written in Go and uses a PostgreSQL database for data storage.
3+
A production-ready boilerplate for building REST APIs with Go and Gin framework. This boilerplate includes essential features like database integration, API documentation, logging, error handling, and containerization support.
44

5-
## Installation
5+
## 🚀 Features
66

7-
To run this microservice locally, you will need to have Docker and Go installed on your system. Once you have these dependencies, you can follow the steps below:
7+
- [x] [Gin Framework](https://github.com/gin-gonic/gin) for routing and middleware
8+
- [x] PostgreSQL integration with migration support
9+
- [x] Swagger API documentation
10+
- [x] API monitoring with APIToolkit
11+
- [x] Custom error handling and logging
12+
- [x] CORS configuration
13+
- [x] Docker and Docker Compose support
14+
- [x] OTP management system
15+
- [x] Static file serving
16+
- [x] Environment configuration
17+
- [x] Hot reload during development
18+
- [x] Code security scanning with gosec
819

9-
1. Clone the repository to your local machine:
20+
## 📋 Prerequisites
21+
22+
- Go 1.x
23+
- Docker and Docker Compose
24+
- PostgreSQL (if running locally)
25+
- Make
26+
27+
## 🛠️ Installation
28+
29+
1. Clone the repository
30+
```bash
31+
git clone https://github.com/CeoFred/gin-boilerplate.git
32+
cd gin-boilerplate
33+
```
34+
35+
2. Copy the example environment file
36+
```bash
37+
cp .env.example .env
38+
```
39+
40+
3. Install dependencies
41+
```bash
42+
make requirements
43+
```
44+
45+
## 🔧 Configuration
46+
47+
Update the `.env` file with your configuration:
48+
49+
```env
50+
PORT=8080
51+
DB_HOST=localhost
52+
DB_PORT=5432
53+
DB_USER=postgres
54+
DB_PASSWORD=yourpassword
55+
DB_NAME=yourdatabase
56+
API_TOOLKIT_KEY=your-api-toolkit-key
57+
```
58+
59+
## 🚀 Running the Application
60+
61+
### Local Development
62+
63+
```bash
64+
# Run the application with hot reload
65+
make run-local
66+
```
67+
68+
### Using Docker
69+
70+
```bash
71+
# Build the Docker image
72+
make build
73+
74+
# Start all services using Docker Compose
75+
make service-start
76+
```
77+
78+
## 📚 API Documentation
79+
80+
Swagger documentation is available at:
81+
```
82+
http://localhost:2024/swagger/index.html
83+
```
84+
85+
To regenerate Swagger documentation:
86+
```bash
87+
make docs-generate
88+
```
89+
90+
## 🛠️ Available Make Commands
91+
92+
- `make run-local` - Run the application locally with hot reload
93+
- `make docs-generate` - Generate Swagger documentation
94+
- `make requirements` - Install/update dependencies
95+
- `make clean-packages` - Clean Go module cache
96+
- `make build` - Build Docker image
97+
- `make start-postgres` - Start PostgreSQL container
98+
- `make stop-postgres` - Stop PostgreSQL container
99+
- `make start` - Start application with Docker
100+
- `make build-no-cache` - Build Docker image without cache
101+
- `make service-stop` - Stop all Docker Compose services
102+
- `make service-start` - Start all Docker Compose services
103+
104+
## 📁 Project Structure
105+
106+
```
107+
.
108+
├── constants/ # Application constants and configuration
109+
├── database/ # Database connection and migrations
110+
├── docs/ # Swagger documentation
111+
├── internal/
112+
│ ├── bootstrap/ # Application bootstrapping
113+
│ ├── helpers/ # Helper functions
114+
│ ├── otp/ # OTP management
115+
│ ├── repository/ # Repository management
116+
│ └── routes/ # API routes
117+
├── static/ # Static files
118+
├── templates/ # Template files
119+
├── main.go # Application entry point
120+
├── Dockerfile # Docker configuration
121+
├── docker-compose.yml # Docker Compose configuration
122+
└── Makefile # Build and development commands
123+
```
124+
125+
## 🔒 Security
126+
127+
The project includes security measures:
128+
- Custom recovery middleware
129+
- CORS configuration
130+
- Request logging
131+
- Security scanning with gosec
132+
133+
## 🤝 Contributing
134+
135+
1. Fork the repository
136+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
137+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
138+
4. Push to the branch (`git push origin feature/amazing-feature`)
139+
5. Open a Pull Request
140+
141+
## 📝 License
142+
143+
This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE) file for details.
144+
145+
## 👤 Contact
146+
147+
Johnson Awah Alfred - [johnsonmessilo19@gmail.com](mailto:johnsonmessilo19@gmail.com)
148+
149+
## ⭐️ Show your support
150+
151+
Give a ⭐️ if this project helped you!

constants/env.go

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type Config struct {
3232
APIToolkitKey string
3333
SendFromEmail string
3434
SendFromName string
35+
SSLMode string
3536
}
3637

3738
func init() {
@@ -69,6 +70,7 @@ func New() *Config {
6970
APIToolkitKey: getEnv("API_TOOLKIT_KEY", ""),
7071
SendFromEmail: getEnv("SEND_FROM_EMAIL", ""),
7172
SendFromName: getEnv("SEND_FROM_NAME", ""),
73+
SSLMode: getEnv("SSL_MODE", "disable"),
7274
}
7375
}
7476

database/connect.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
"gorm.io/driver/postgres"
88
"gorm.io/gorm"
9+
"gorm.io/gorm/logger"
910
"gorm.io/gorm/schema"
1011
)
1112

@@ -15,15 +16,16 @@ type Config struct {
1516
Password string
1617
User string
1718
DBName string
19+
SSLMode string
1820
}
1921

2022
func Connect(config *Config) {
2123
var (
2224
err error
2325
port, _ = strconv.ParseUint(config.Port, 10, 32)
2426
dsn = fmt.Sprintf(
25-
"host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
26-
config.Host, port, config.User, config.Password, config.DBName,
27+
"host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
28+
config.Host, port, config.User, config.Password, config.DBName, config.SSLMode,
2729
)
2830
)
2931

@@ -34,15 +36,15 @@ func Connect(config *Config) {
3436
DisableForeignKeyConstraintWhenMigrating: true,
3537
})
3638

39+
DB.Logger.LogMode(logger.Silent)
40+
3741
if err != nil {
3842
fmt.Println(
3943
err.Error(),
4044
)
4145
panic("failed to connect database")
4246
}
4347

44-
// RunAutoMigrations()
45-
4648
fmt.Println("Connection Opened to Database")
4749
}
4850

database/migration.go

+38-38
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,52 @@
11
package database
22

33
import (
4-
"context"
5-
"gorm.io/gorm"
4+
"database/sql"
5+
"embed"
6+
"io/fs"
67
"log"
7-
"time"
8-
)
98

10-
const (
11-
dbTimeout = 30 * time.Second
9+
"github.com/golang-migrate/migrate/v4"
10+
"github.com/golang-migrate/migrate/v4/database/postgres"
11+
"github.com/golang-migrate/migrate/v4/source/iofs"
12+
_ "github.com/lib/pq"
1213
)
1314

14-
func RunManualMigration(db *gorm.DB) {
15-
16-
query1 := `CREATE TABLE IF NOT EXISTS users (
17-
id UUID NOT NULL,
18-
email VARCHAR(255) NOT NULL,
19-
password VARCHAR(255),
20-
first_name VARCHAR(255),
21-
last_name VARCHAR(255),
22-
avatar_url VARCHAR(255) DEFAULT NULL,
23-
ip VARCHAR(255) DEFAULT NULL,
24-
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
25-
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
26-
last_login VARCHAR(255) NULL,
27-
role VARCHAR(255) DEFAULT 'user',
28-
email_verified BOOLEAN DEFAULT FALSE,
29-
country VARCHAR(255) DEFAULT NULL,
30-
phone_number VARCHAR(255) DEFAULT NULL,
31-
status VARCHAR(255) DEFAULT 'Inactive'
32-
);
33-
`
34-
35-
migrationQueries := []string{
36-
query1,
15+
//go:embed migrations/*
16+
var migrationFiles embed.FS
17+
18+
func RunManualMigration(dbURL string) {
19+
// Open a connection to the database
20+
db, err := sql.Open("postgres", dbURL)
21+
if err != nil {
22+
log.Fatal("Failed to connect to database:", err)
23+
}
24+
defer db.Close()
25+
26+
// Use the embedded migration files
27+
migrations, err := fs.Sub(migrationFiles, "migrations")
28+
if err != nil {
29+
log.Fatal("Failed to create sub filesystem for migrations:", err)
3730
}
3831

39-
log.Println("running db migration :::::::::::::")
32+
d, err := iofs.New(migrations, ".")
33+
if err != nil {
34+
log.Fatal("Failed to create iofs driver:", err)
35+
}
36+
37+
driver, err := postgres.WithInstance(db, &postgres.Config{})
38+
if err != nil {
39+
log.Fatal("Failed to create postgres driver:", err)
40+
}
4041

41-
_, cancel := context.WithTimeout(context.Background(), dbTimeout)
42-
defer cancel()
42+
m, err := migrate.NewWithInstance("iofs", d, "postgres", driver)
43+
if err != nil {
44+
log.Fatal("Failed to create migrate instance:", err)
45+
}
4346

44-
for _, query := range migrationQueries {
45-
err := db.Exec(query).Error
46-
if err != nil {
47-
log.Fatal(err)
48-
}
47+
if err := m.Up(); err != nil && err != migrate.ErrNoChange {
48+
log.Fatal("Failed to run migrations:", err)
4949
}
5050

51-
log.Println("complete db migration")
51+
log.Println("Completed DB migration")
5252
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
DROP TABLE IF EXISTS users;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
2+
3+
CREATE TABLE users (
4+
id UUID NOT NULL,
5+
email VARCHAR(255) NOT NULL,
6+
password VARCHAR(255),
7+
first_name VARCHAR(255),
8+
last_name VARCHAR(255),
9+
avatar_url VARCHAR(255) DEFAULT NULL,
10+
ip VARCHAR(255) DEFAULT NULL,
11+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
12+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
13+
last_login VARCHAR(255) NULL,
14+
role VARCHAR(255) DEFAULT 'user',
15+
email_verified BOOLEAN DEFAULT FALSE,
16+
country VARCHAR(255) DEFAULT NULL,
17+
phone_number VARCHAR(255) DEFAULT NULL,
18+
status VARCHAR(255) DEFAULT 'Inactive'
19+
);

0 commit comments

Comments
 (0)