A complete, production-ready REST API service built with GoServe framework, MongoDB, Redis, JWT authentication, and role-based authorization.
This project is a fully production-ready blog service demonstrating best practices for building performant and secure backend REST API services with MongoDB. It showcases the application of the GoServe framework with clean architecture, feature separation, comprehensive testing, and production grade security.
- GoServe Framework - Built on the production-ready GoServe v2 framework
- Clean Architecture - Well-structured, maintainable codebase with clear separation of concerns
- MongoDB Integration - Full MongoDB support with query builder and document validation
- Redis Caching - High-performance caching layer for frequently accessed data
- JWT Authentication - Secure token-based authentication with refresh tokens
- Role-Based Authorization - Fine-grained access control with role management
- API Key Support - Additional security layer for API access control
- Request Validation - Comprehensive input validation using validator v10
- Testing Suite - Extensive unit and integration test coverage
- Docker Ready - Complete Docker Compose setup for easy deployment
- Auto-Generated APIs - CLI tool for scaffolding new API endpoints
- Type-Safe DTOs - Structured data transfer objects for all requests/responses
- Language: Go 1.21+
- Framework: GoServe v2
- Web Framework: Gin
- Database: MongoDB (mongo-driver)
- Cache: Redis (go-redis)
- Authentication: JWT tokens
- Validation: validator
- Configuration: Viper
- Testing: Testify
- Docker & Docker Compose (Installation Guide)
- Go 1.21+ (for local development)
1. Clone the Repository
git clone https://github.com/afteracademy/goserve-example-api-server-mongo.git
cd goserve-example-api-server-mongo2. Generate RSA Keys
go run .tools/rsa/keygen.go3. Create Environment Files
go run .tools/copy/envs.go 4. Start with Docker Compose
docker compose up --buildThe API server will be available at: http://localhost:8080
5. Health Check
docker inspect --format='{{.State.Health.Status}}' goserver-mongo6. Run Tests
docker exec -t goserver-mongo go test -v ./...If you encounter issues:
- Ensure port 8080 is available (change
SERVER_PORTin.envif needed) - Ensure port 27017 is available (change
DB_PORTin.envif needed) - Ensure port 6379 is available (change
REDIS_PORTin.envif needed)
For local development without Docker:
go mod tidyKeep Docker containers for mongo and redis running, but stop the goserver-mongo container.
Update the following in .env and .test.env:
DB_HOST=localhost
REDIS_HOST=localhostRun the application:
go run cmd/main.goOr use VS Code: Use the Run and Debug panel for an enhanced development experience.
The architecture is designed to make each API independent while sharing services among them. This promotes:
- Code Reusability - Shared services across multiple endpoints
- Team Collaboration - Reduced conflicts when working in teams
- Feature Isolation - Easier testing and maintenance
Startup Flow:
cmd/main → startup/server → module, mongo, redis, router → api/[feature]/middlewares → api/[feature]/controller → api/[feature]/service → authentication, authorization → handlers → response
Sample API
├── dto/
│ └── create_sample.go # Data Transfer Objects
├── model/
│ └── sample.go # MongoDB Collection Model
├── middleware/ # (Optional) Feature-specific middleware
│ └── custom.go
├── controller.go # Route definitions & handlers
└── service.go # Business logic & data operations
Key Components:
- DTOs - Request/response body definitions in
dto/directory - Models - MongoDB collection schemas in
model/directory - Controller - Defines endpoints and handles HTTP requests
- Service - Contains business logic and data operations
- Middleware - Authentication, authorization, and custom middleware
| Directory | Purpose |
|---|---|
| api/ | Feature-based API implementations |
| cmd/ | Application entry point (main.go) |
| common/ | Shared code across all APIs |
| config/ | Environment variable configuration |
| keys/ | RSA keys for JWT token signing |
| startup/ | Server initialization, DB, Redis, routing |
| tests/ | Integration test suites |
| utils/ | Utility functions |
Helper Directories:
- .extra/ - MongoDB initialization scripts, assets, documentation
- .github/ - CI/CD workflows
- .tools/ - Code generators, key generation utilities
- .vscode/ - Editor configuration and debug settings
Scaffold a new API endpoint with a single command:
go run .tools/apigen.go sampleThis creates the complete structure under api/sample/ with:
- Model definitions
- DTO templates
- Controller skeleton
- Service interface
api/sample/model/sample.go
package model
import (
"context"
"time"
"github.com/go-playground/validator/v10"
"github.com/afteracademy/goserve/v2/mongo"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
mongod "go.mongodb.org/mongo-driver/mongo"
)
const CollectionName = "samples"
type Sample struct {
ID primitive.ObjectID `bson:"_id,omitempty" validate:"-"`
Field string `bson:"field" validate:"required"`
Status bool `bson:"status" validate:"required"`
CreatedAt time.Time `bson:"createdAt" validate:"required"`
UpdatedAt time.Time `bson:"updatedAt" validate:"required"`
}
func NewSample(field string) (*Sample, error) {
time := time.Now()
doc := Sample{
Field: field,
Status: true,
CreatedAt: time,
UpdatedAt: time,
}
if err := doc.Validate(); err != nil {
return nil, err
}
return &doc, nil
}
}
func (doc *Sample) GetValue() *Sample {
return doc
}
func (doc *Sample) Validate() error {
validate := validator.New()
return validate.Struct(doc)
}
func (*Sample) EnsureIndexes(db mongo.Database) {
indexes := []mongod.IndexModel{
{
Keys: bson.D{
{Key: "_id", Value: 1},
{Key: "status", Value: 1},
},
},
}
mongo.NewQueryBuilder[Sample](db, CollectionName).Query(context.Background()).CreateIndexes(indexes)
}Model Interface: Implements github.com/afteracademy/goserve/v2/mongo/database.Document[T]
type Document[T any] interface {
EnsureIndexes(Database)
GetValue() *T
Validate() error
}api/sample/dto/create_sample.go
package dto
import (
"time"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type InfoSample struct {
ID primitive.ObjectID `json:"_id" binding:"required"`
Field string `json:"field" binding:"required"`
CreatedAt time.Time `json:"createdAt" binding:"required"`
}api/sample/service.go
package sample
import (
"github.com/afteracademy/goserve-example-api-server-mongo/api/sample/dto"
"github.com/afteracademy/goserve-example-api-server-mongo/api/sample/model"
"github.com/afteracademy/goserve/v2/mongo"
"github.com/afteracademy/goserve/v2/redis"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type Service interface {
FindSample(id primitive.ObjectID) (*model.Sample, error)
}
type service struct {
sampleQueryBuilder mongo.QueryBuilder[model.Sample]
infoSampleCache redis.Cache[dto.InfoSample]
}
func NewService(db mongo.Database, store redis.Store) Service {
return &service{
sampleQueryBuilder: mongo.NewQueryBuilder[model.Sample](db, model.CollectionName),
infoSampleCache: redis.NewCache[dto.InfoSample](store),
}
}
func (s *service) FindSample(id primitive.ObjectID) (*model.Sample, error) {
filter := bson.M{"_id": id}
msg, err := s.sampleQueryBuilder.SingleQuery().FindOne(filter, nil)
if err != nil {
return nil, err
}
return msg, nil
}Key Features:
- Database Query:
mongo.QueryBuilder[model.Sample]provides type-safe MongoDB operations - Redis Cache:
redis.Cache[dto.InfoSample]provides type-safe caching operations
api/sample/controller.go
package sample
import (
"github.com/gin-gonic/gin"
"github.com/afteracademy/goserve-example-api-server-mongo/api/sample/dto"
"github.com/afteracademy/goserve-example-api-server-mongo/common"
coredto "github.com/afteracademy/goserve/v2/dto"
"github.com/afteracademy/goserve/v2/network"
"github.com/afteracademy/goserve-example-api-server-mongo/utils"
)
type controller struct {
network.Controller
common.ContextPayload
service Service
}
func NewController(
authMFunc network.AuthenticationProvider,
authorizeMFunc network.AuthorizationProvider,
service Service,
) network.Controller {
return &controller{
Controller: network.NewController("/sample", authMFunc, authorizeMFunc),
ContextPayload: common.NewContextPayload(),
service: service,
}
}
func (c *controller) MountRoutes(group *gin.RouterGroup) {
group.GET("/id/:id", c.getSampleHandler)
}
func (c *controller) getSampleHandler(ctx *gin.Context) {
mongoId, err := network.ReqParams[coredto.MongoId](ctx)
if err != nil {
network.SendBadRequestError(ctx, err.Error(), err)
return
}
sample, err := c.service.FindSample(mongoId.ID)
if err != nil {
network.SendNotFoundError(ctx, "sample not found", err)
return
}
data, err := utils.MapTo[dto.InfoSample](sample)
if err != nil {
network.SendInternalServerError(ctx, "something went wrong", err)
return
}
network.SendSuccessDataResponse(ctx, "success", data)
}Controller Interface: Implements github.com/afteracademy/goserve/v2/network.Controller
type Controller interface {
BaseController
MountRoutes(group *gin.RouterGroup)
}
type BaseController interface {
ResponseSender
Path() string
Authentication() gin.HandlerFunc
Authorization(role string) gin.HandlerFunc
}startup/module.go
import (
...
"github.com/afteracademy/goserve-example-api-server-mongo/api/sample"
)
...
func (m *module) Controllers() []network.Controller {
return []network.Controller{
...
sample.NewController(m.AuthenticationProvider(), m.AuthorizationProvider(), sample.NewService(m.DB, m.Store)),
}
}startup/indexes.go
import (
...
sample "github.com/afteracademy/goserve-example-api-server-mongo/api/sample/model"
)
func EnsureDbIndexes(db mongo.Database) {
go mongo.Document[sample.Sample](&sample.Sample{}).EnsureIndexes(db)
...
}Explore other GoServe example implementations:
-
GoServe Framework
Core framework with PostgreSQL, MongoDB, Redis, and NATS support -
PostgreSQL API Server
Complete REST API with PostgreSQL and clean architecture -
Microservices Example
NATS-based microservices communication patterns
Use the GoServeGen CLI to generate a starter project:
# Install GoServeGen CLI
go install github.com/afteracademy/goservegen@latest
# Generate a new project
goservegen create my-project --db=mongoOr download the starter project directly:
- How to Architect Good Go Backend REST API Services
- How to Create Microservices — A Practical Guide Using Go
- Implement JSON Web Token (JWT) Authentication using AccessToken and RefreshToken
We welcome contributions! Please feel free to:
- Fork the repository
- Open issues for bugs or feature requests
- Submit pull requests with improvements
- Share your feedback and suggestions
Subscribe to AfterAcademy on YouTube for in-depth tutorials and concept explanations:
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
If you find this project useful, please consider:
- Starring ⭐ this repository
- Sharing with the community
- Contributing improvements
- Reporting bugs and issues
Built with love by AfterAcademy
