help@rskworld.in +91 93305 39277
RSK World
  • Home
  • Development
    • Web Development
    • Mobile Apps
    • Software
    • Games
    • Project
  • Technologies
    • Data Science
    • AI Development
    • Cloud Development
    • Blockchain
    • Cyber Security
    • Dev Tools
    • Testing Tools
  • Blog
  • About
  • Contact

Theme Settings

Color Scheme
Display Options
Font Size
100%
Back to Project
RSK World
go-rest-api
/
internal
/
handlers
RSK World
go-rest-api
Go REST API - Enterprise-grade REST API with JWT Authentication + PostgreSQL + Redis Caching + Docker + Comprehensive Testing + Educational Design
handlers
  • api_handler.go1.4 KB
  • auth_handler.go4.4 KB
  • auth_handler_test.go4.6 KB
  • category_handler.go5.2 KB
  • product_handler.go8.8 KB
  • upload_handler.go5 KB
requirements.txtBOARD_PAPERS_SUMMARY.mdauth_handler_test.goREADME.mdauth_handler.go
internal/handlers/auth_handler_test.go
Raw Download
Find: Go to:
/*
* Author: RSK World
* Email: help@rskworld.in / support@rskworld.in
* Website: https://rskworld.in/contact.php
* Year: 2026
*/

package handlers

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/rskworld/go-rest-api/internal/config"
	"github.com/rskworld/go-rest-api/internal/database"
	"github.com/rskworld/go-rest-api/internal/models"
	"github.com/stretchr/testify/assert"
	"golang.org/x/crypto/bcrypt"
)

func setupAuthTest(t *testing.T) (*gin.Engine, *config.Config) {
	gin.SetMode(gin.TestMode)

	// Setup test database
	cfg := &config.Config{
		DBHost:     "localhost",
		DBUser:     "postgres",
		DBPassword: "postgres",
		DBName:     "testdb",
		DBPort:     "5432",
		JWTSecret:  "testsecret",
	}

	// For testing, we'll use an in-memory database approach
	// In a real scenario, you'd set up a test database

	r := gin.New()
	return r, cfg
}

func TestRegister(t *testing.T) {
	r, cfg := setupAuthTest(t)

	authHandler := NewAuthHandler(cfg)

	r.POST("/register", authHandler.Register)

	tests := []struct {
		name           string
		requestBody    RegisterRequest
		expectedStatus int
		expectError    bool
	}{
		{
			name: "Valid registration",
			requestBody: RegisterRequest{
				Name:     "Test User",
				Email:    "test@example.com",
				Password: "password123",
			},
			expectedStatus: http.StatusCreated,
			expectError:    false,
		},
		{
			name: "Invalid email",
			requestBody: RegisterRequest{
				Name:     "Test User",
				Email:    "invalid-email",
				Password: "password123",
			},
			expectedStatus: http.StatusBadRequest,
			expectError:    true,
		},
		{
			name: "Password too short",
			requestBody: RegisterRequest{
				Name:     "Test User",
				Email:    "test@example.com",
				Password: "123",
			},
			expectedStatus: http.StatusBadRequest,
			expectError:    true,
		},
		{
			name: "Missing name",
			requestBody: RegisterRequest{
				Email:    "test@example.com",
				Password: "password123",
			},
			expectedStatus: http.StatusBadRequest,
			expectError:    true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			jsonBody, _ := json.Marshal(tt.requestBody)
			req, _ := http.NewRequest("POST", "/register", bytes.NewBuffer(jsonBody))
			req.Header.Set("Content-Type", "application/json")

			w := httptest.NewRecorder()
			r.ServeHTTP(w, req)

			assert.Equal(t, tt.expectedStatus, w.Code)

			var response map[string]interface{}
			json.Unmarshal(w.Body.Bytes(), &response)

			if tt.expectError {
				assert.Equal(t, "error", response["status"])
			} else {
				assert.Equal(t, "success", response["status"])
			}
		})
	}
}

func TestLogin(t *testing.T) {
	r, cfg := setupAuthTest(t)

	// Create a test user in the database
	hashedPassword, _ := bcrypt.GenerateFromPassword([]byte("password123"), bcrypt.DefaultCost)
	testUser := models.User{
		Name:     "Test User",
		Email:    "test@example.com",
		Password: string(hashedPassword),
		Role:     "user",
		IsActive: true,
	}

	// This would normally save to database
	// For this test, we'll mock the database interaction

	authHandler := NewAuthHandler(cfg)

	r.POST("/login", authHandler.Login)

	tests := []struct {
		name           string
		requestBody    AuthRequest
		expectedStatus int
		expectError    bool
	}{
		{
			name: "Valid login",
			requestBody: AuthRequest{
				Email:    "test@example.com",
				Password: "password123",
			},
			expectedStatus: http.StatusOK,
			expectError:    false,
		},
		{
			name: "Invalid email",
			requestBody: AuthRequest{
				Email:    "invalid@example.com",
				Password: "password123",
			},
			expectedStatus: http.StatusUnauthorized,
			expectError:    true,
		},
		{
			name: "Wrong password",
			requestBody: AuthRequest{
				Email:    "test@example.com",
				Password: "wrongpassword",
			},
			expectedStatus: http.StatusUnauthorized,
			expectError:    true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			jsonBody, _ := json.Marshal(tt.requestBody)
			req, _ := http.NewRequest("POST", "/login", bytes.NewBuffer(jsonBody))
			req.Header.Set("Content-Type", "application/json")

			w := httptest.NewRecorder()
			r.ServeHTTP(w, req)

			assert.Equal(t, tt.expectedStatus, w.Code)

			var response map[string]interface{}
			json.Unmarshal(w.Body.Bytes(), &response)

			if tt.expectError {
				assert.Equal(t, "error", response["status"])
			} else {
				assert.Equal(t, "success", response["status"])
				assert.Contains(t, response["data"], "token")
			}
		})
	}
}
199 lines•4.6 KB
go
README.md
Raw Download

README.md

# Go REST API
**ID**: 23
**Category**: Go Projects
**Difficulty**: Advanced

## Description
A comprehensive RESTful API service built with Go programming language. Features advanced authentication, role-based access control, product management with categories, file uploads, search and filtering, Redis caching, comprehensive testing, and API versioning. Perfect for learning enterprise-grade Go development, microservices architecture, and modern API design patterns.

## 📋 Demo & Documentation
🎯 **[View Interactive Demo](./index.html)** - Complete project overview with installation guide, API documentation, and live examples.

> **Quick Start**: Open `demo.html` in your browser for a comprehensive guide to get started immediately!

## Features
- **Authentication & Authorization**
- JWT-based authentication
- Role-based access control (Admin/User)
- Password hashing with bcrypt
- Account activation/deactivation

- **Product Management**
- CRUD operations for products
- Product categories with relationships
- Image upload functionality
- Search and advanced filtering
- Pagination support

- **API Features**
- RESTful API design
- API versioning (v1)
- Comprehensive middleware stack
- Request/response logging
- CORS support
- Rate limiting
- Input validation

- **Performance & Caching**
- Redis caching for frequently accessed data
- Database query optimization
- Efficient pagination

- **Development & Testing**
- Comprehensive unit tests
- Integration tests
- Test coverage reporting
- Docker containerization
- Makefile for common tasks

- **Documentation**
- Swagger/OpenAPI documentation
- Comprehensive API documentation
- Code documentation

## Technologies
- **Backend**: Go 1.21, Gin Framework
- **Database**: PostgreSQL with GORM ORM
- **Cache**: Redis
- **Authentication**: JWT
- **Validation**: Go Playground Validator
- **File Storage**: Local filesystem with Docker volumes
- **Containerization**: Docker & Docker Compose
- **Documentation**: Swagger/OpenAPI
- **Testing**: Go testing framework

## Prerequisites
- Docker and Docker Compose
- Go 1.21+ (for local development)
- Make (optional, for using Makefile commands)

## Quick Start

### Using Docker (Recommended)
```bash
# Clone the repository
git clone <repository-url>
cd go-rest-api

# Start all services
docker-compose up --build

# The API will be available at http://localhost:8080
# Swagger documentation at http://localhost:8080/swagger/index.html
```

### Local Development
```bash
# Install dependencies
go mod download

# Set up environment variables (copy and modify .env)
cp .env.example .env

# Run database migrations
make seed

# Run the application
make run

# Run tests
make test

# Generate Swagger docs
make swagger-gen
```

## Environment Variables
```bash
# Database
DB_HOST=localhost
DB_USER=postgres
DB_PASSWORD=postgres
DB_NAME=gorestapi
DB_PORT=5432

# Authentication
JWT_SECRET=your-super-secret-jwt-key

# Server
PORT=8080

# File Upload
UPLOAD_PATH=./uploads
MAX_FILE_SIZE=5242880 # 5MB

# Redis Cache
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
CACHE_ENABLED=true
```

## API Endpoints

### Authentication
- `POST /api/v1/register` - Register a new user
- `POST /api/v1/login` - Login and receive JWT token

### Public Endpoints
- `GET /api/v1/health` - Health check
- `GET /api/v1/categories` - Get all categories
- `GET /api/v1/categories/{id}` - Get category by ID
- `GET /api/v1/products` - Get products with filtering and pagination
- `GET /api/v1/products/{id}` - Get product by ID

### Protected Endpoints (Requires Authentication)
- `GET /api/v1/profile` - Get user profile

### Admin Only Endpoints (Requires Admin Role)
- `POST /api/v1/categories` - Create category
- `PUT /api/v1/categories/{id}` - Update category
- `DELETE /api/v1/categories/{id}` - Delete category
- `POST /api/v1/products` - Create product
- `PUT /api/v1/products/{id}` - Update product
- `DELETE /api/v1/products/{id}` - Delete product
- `POST /api/v1/products/{id}/upload` - Upload product image
- `DELETE /api/v1/products/{id}/image` - Delete product image

## API Usage Examples

### Register User
```bash
curl -X POST http://localhost:8080/api/v1/register \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"email": "john@example.com",
"password": "password123"
}'
```

### Login
```bash
curl -X POST http://localhost:8080/api/v1/login \
-H "Content-Type: application/json" \
-d '{
"email": "john@example.com",
"password": "password123"
}'
```

### Get Products with Filtering
```bash
# Get products with search and pagination
curl "http://localhost:8080/api/v1/products?search=laptop&page=1&limit=10"

# Filter by category and price range
curl "http://localhost:8080/api/v1/products?category_id=1&min_price=100&max_price=1000"

# Get only in-stock products
curl "http://localhost:8080/api/v1/products?in_stock=true"
```

### Create Product (Admin Only)
```bash
curl -X POST http://localhost:8080/api/v1/products \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "New Product",
"description": "Product description",
"price": 99.99,
"stock": 50,
"category_id": 1
}'
```

### Upload Product Image (Admin Only)
```bash
curl -X POST http://localhost:8080/api/v1/products/1/upload \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-F "image=@/path/to/image.jpg"
```

## Database Schema

### Users
- id (Primary Key)
- email (Unique)
- password (Hashed)
- name
- role (admin/user)
- is_active
- last_login
- timestamps

### Categories
- id (Primary Key)
- name (Unique)
- description
- timestamps

### Products
- id (Primary Key)
- name
- description
- price
- stock
- category_id (Foreign Key)
- image (File path)
- timestamps

## Testing

```bash
# Run all tests
make test

# Run tests with verbose output
make test-verbose

# Run tests with coverage
make test-coverage

# View coverage report
open coverage.html
```

## Development Commands

```bash
# Run the application
make run

# Build the application
make build

# Run database seeder
make seed

# Generate Swagger documentation
make swagger-gen

# Clean build artifacts
make clean

# Docker commands
make docker-up # Start services
make docker-down # Stop services
```

## Project Structure
```
├── cmd/
│ ├── api/ # Main application entry point
│ └── seeder/ # Database seeder
├── internal/
│ ├── cache/ # Redis caching layer
│ ├── config/ # Configuration management
│ ├── database/ # Database connection and setup
│ ├── handlers/ # HTTP request handlers
│ ├── middleware/ # Custom middleware
│ ├── models/ # Database models
│ ├── response/ # Response utilities
│ ├── routes/ # Route definitions
│ └── validation/ # Input validation
├── tests/ # Integration tests
├── docs/ # Swagger documentation
├── docker-compose.yml
├── Dockerfile
├── Makefile
├── go.mod
└── README.md
```

## API Documentation
Complete API documentation is available via Swagger UI at:
`http://localhost:8080/swagger/index.html`

## Contributing
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests for new functionality
5. Ensure all tests pass
6. Submit a pull request

## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Support
- **Author**: RSK World
- **Website**: [RSK World](https://rskworld.in/contact.php)
- **Email**: help@rskworld.in / support@rskworld.in
- **Year**: 2026

## Source
[Download Source](./go-rest-api/go-rest-api.zip)
internal/handlers/auth_handler.go
Raw Download
Find: Go to:
/*
* Author: RSK World
* Email: help@rskworld.in / support@rskworld.in
* Website: https://rskworld.in/contact.php
* Year: 2026
*/

package handlers

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"github.com/rskworld/go-rest-api/internal/config"
	"github.com/rskworld/go-rest-api/internal/database"
	"github.com/rskworld/go-rest-api/internal/models"
	"github.com/rskworld/go-rest-api/internal/response"
	"github.com/rskworld/go-rest-api/internal/validation"
	"golang.org/x/crypto/bcrypt"
)

type AuthRequest struct {
	Email    string `json:"email" binding:"required,email" validate:"required,email"`
	Password string `json:"password" binding:"required,min=6" validate:"required,min=6,max=128"`
}

type RegisterRequest struct {
	Name     string `json:"name" binding:"required,min=2,max=100" validate:"required,min=2,max=100"`
	Email    string `json:"email" binding:"required,email" validate:"required,email"`
	Password string `json:"password" binding:"required,min=6,max=128" validate:"required,min=6,max=128"`
}

type AuthHandler struct {
	Config *config.Config
}

func NewAuthHandler(cfg *config.Config) *AuthHandler {
	return &AuthHandler{Config: cfg}
}

// Register godoc
// @Summary Register a new user
// @Description Register a new user with name, email and password
// @Tags auth
// @Accept  json
// @Produce  json
// @Param user body handlers.RegisterRequest true "Register user"
// @Success 201 {object} response.Response
// @Failure 400 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /register [post]
func (h *AuthHandler) Register(c *gin.Context) {
	var req RegisterRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.Error(c, http.StatusBadRequest, err.Error())
		return
	}

	// Additional validation using our validation package
	if err := validation.ValidateStruct(req); err != nil {
		response.ValidationError(c, err.Error())
		return
	}

	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
	if err != nil {
		response.InternalServerError(c, "Failed to hash password")
		return
	}

	user := models.User{
		Name:     req.Name,
		Email:    req.Email,
		Password: string(hashedPassword),
		Role:     "user", // Default role
		IsActive: true,   // Default active status
	}

	if result := database.DB.Create(&user); result.Error != nil {
		response.Error(c, http.StatusBadRequest, "Email already exists")
		return
	}

	response.Success(c, http.StatusCreated, "User registered successfully", gin.H{
		"id":    user.ID,
		"name":  user.Name,
		"email": user.Email,
		"role":  user.Role,
	})
}

// Login godoc
// @Summary Login user
// @Description Login user and generate JWT token
// @Tags auth
// @Accept  json
// @Produce  json
// @Param user body handlers.AuthRequest true "Login user"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.Response
// @Failure 401 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /login [post]
func (h *AuthHandler) Login(c *gin.Context) {
	var req AuthRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.Error(c, http.StatusBadRequest, err.Error())
		return
	}

	// Validate input
	if err := validation.ValidateStruct(req); err != nil {
		response.Error(c, http.StatusBadRequest, err.Error())
		return
	}

	var user models.User
	if result := database.DB.Where("email = ?", req.Email).First(&user); result.Error != nil {
		response.Error(c, http.StatusUnauthorized, "Invalid credentials")
		return
	}

	if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password)); err != nil {
		response.Unauthorized(c, "Invalid credentials")
		return
	}

	// Check if user is active
	if !user.IsActive {
		response.Unauthorized(c, "Account is deactivated")
		return
	}

	// Update last login time
	now := time.Now()
	database.DB.Model(&user).Update("last_login", now)

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"user_id": user.ID,
		"role":    user.Role,
		"exp":     time.Now().Add(time.Hour * 24).Unix(),
	})

	tokenString, err := token.SignedString([]byte(h.Config.JWTSecret))
	if err != nil {
		response.Error(c, http.StatusInternalServerError, "Failed to generate token")
		return
	}

	response.Success(c, http.StatusOK, "Login successful", gin.H{"token": tokenString})
}

155 lines•4.4 KB
go
🚀 Support RSK World

Subscribe to our YouTube channel for latest tutorials & updates!



Click subscribe & support our work ❤️

About RSK World

Founded by Molla Samser, with Designer & Tester Rima Khatun, RSK World is your one-stop destination for free programming resources, source code, and development tools.

Founder: Molla Samser
Designer & Tester: Rima Khatun

Development

  • Game Development
  • Web Development
  • Mobile Development
  • AI Development
  • Development Tools

Legal

  • Terms & Conditions
  • Privacy Policy
  • Disclaimer

Contact Info

Nutanhat, Mongolkote
Purba Burdwan, West Bengal
India, 713147

+91 93305 39277

hello@rskworld.in
support@rskworld.in

© 2026 RSK World. All rights reserved.

Content used for educational purposes only. View Disclaimer