This example shows how to build a secure, production-ready RESTful API in Go using the gorest framework. You’ll learn project structure, configuration, database migrations, routing, middleware, and how to add your own resources step by step.
- Features
- Prerequisites
- Installation & Setup
- Environment Configuration
- Project Structure
- Running the Application
- Database Migrations
- API Reference & Examples
- Authentication & Security
- Best Practices
- Contributing
- License
- Multi-database support: MySQL/Postgres/SQLite, Redis, MongoDB
- Flexible authentication: Basic Auth, JWT, 2FA
- Security: CORS, IP firewall, rate limiting, secure headers
- Clean layers: Models → Repositories → Services → Handlers
- Auto migrations, graceful shutdown, performance tracing
- Email verification & password recovery (Postmark)
- Go 1.23+ installed
- One or more databases:
- RDBMS (MySQL, PostgreSQL, or SQLite)
- Redis (optional)
- MongoDB (optional)
- Git & a terminal
Note: For a new project, you do not need to clone this project. You only need to import the packages you need in your own project.
But for this example, clone this repo and enter the example2 directory.
# 1. Clone repo and enter example2
git clone https://github.com/pilinux/gorest.git
cd gorest/example2
# 2. Copy sample env and edit values
cd cmd/app
cp .env.sample .env
# open .env in your editor and configure
# 3. Fetch dependencies
cd ../..
go mod tidyAll settings live in cmd/app/.env. Key sections:
- APP_*: Name, host, port, env
- DB: Activate RDBMS & credentials
- REDIS / MONGO: optional cache & NoSQL
- AUTH: Basic Auth, JWT, 2FA flags & keys
- SECURITY: CORS, firewall, rate limiter
- EMAIL: Postmark settings for verification & recovery (optional)
example2/
├── cmd/
│ └── app/
│ ├── main.go # Application entry
│ └── .env.sample # Env template
└── internal/
├── database/
│ ├── migrate/ # Auto migrations
│ └── model/ # GORM, Mongo and Redis models
├── handler/ # Gin HTTP handlers
├── repo/ # Data access layer
├── router/ # Route definitions & middleware
└── service/ # Business logic
- main.go: boots config, DB/Redis/Mongo clients, migrations, router, graceful shutdown
- model: data structures + validation
- repo: DB/Redis/Mongo operations using ORMs
- service: higher-level business flows
- handler: HTTP parsing, response formatting
- router: groups, middleware, versioning
# From project root:
cd cmd/app
go build -o app
./appVisit http://localhost:8999 and explore /api/v1/... endpoints.
Migrations run automatically at startup if ACTIVATE_RDBMS=yes.
To customize tables:
// internal/database/migrate/migrate.go
func StartMigration(configure gconfig.Configuration) error {
db := gdb.GetDB()
configureDB := configure.Database.RDBMS
driver := configureDB.Env.Driver
if err := db.AutoMigrate(
&auth{},
&twoFA{},
&twoFABackup{},
&tempEmail{},
&user{},
&post{},
&hobby{},
// add &model.YourNewModel{} here
)
}All routes are mounted under the base path /api/v1 and defined in internal/router/router.go.
Base URL: http://localhost:8999/api/v1
Register a new user.
Body:
{
"email": "jon@example.com",
"password": "your_password"
}curl -X POST http://localhost:8999/api/v1/register \
-H "Content-Type: application/json" \
-d '{"email":"jon@example.com","password":"your_password"}'Login with email and password to receive JWT.
Body:
{
"email": "jon@example.com",
"password": "your_password"
}curl -X POST http://localhost:8999/api/v1/login \
-H "Content-Type: application/json" \
-d '{"email":"jon@example.com","password":"your_password"}'Retrieve all users, their posts and hobbies.
curl http://localhost:8999/api/v1/usersRetrieve a single user by userID.
curl http://localhost:8999/api/v1/users/123Add the user profile. (Requires JWT)
Body:
{
"firstName": "Jon",
"lastName": "Doe"
}curl -X POST http://localhost:8999/api/v1/users \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"firstName":"Jon","lastName":"Doe"}'Update authenticated user’s profile. (Requires JWT)
Body:
{
"firstName": "Jon",
"lastName": "Smith"
}Delete authenticated user account and all related posts/hobbies. (Requires JWT)
Get all posts with pagination support.
Query parameters:
page(optional): Page number, default: 1pageSize(optional): Number of posts per page, default: 10, max: 100
# Get first page with default page size (10)
curl http://localhost:8999/api/v1/posts
# Get page 2 with 1 post per page
curl "http://localhost:8999/api/v1/posts?page=2&pageSize=1"Response:
{
"hasNext": true,
"hasPrevious": true,
"page": 2,
"pageSize": 1,
"posts": [
{
"postID": 8,
"createdAt": 1771575386,
"updatedAt": 1771575386,
"title": "My First Post",
"body": "Hello, gorest!"
}
],
"total": 9
}Get a post by postID.
curl http://localhost:8999/api/v1/posts/456Create a new post. (Requires JWT)
Body:
{
"title": "My First Post",
"body": "Hello, gorest!"
}curl -X POST http://localhost:8999/api/v1/posts \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"title":"My First Post","body":"Hello, gorest!"}'Update a post you own. (Requires JWT)
Body:
{
"title": "Updated Title",
"body": "Updated content"
}Delete a single post. (Requires JWT)
Delete all your posts. (Requires JWT)
List all hobbies.
curl http://localhost:8999/api/v1/hobbiesGet hobby by hobbyID.
curl http://localhost:8999/api/v1/hobbies/7List your hobbies. (Requires JWT)
Add a hobby to your profile. (Requires JWT)
Body:
{
"hobby": "painting"
}Remove a hobby from your profile. (Requires JWT)
Set a key/value.
Body:
{
"key": "theme",
"value": "dark"
}Get value by key.
curl http://localhost:8999/api/v1/kv/themeDelete a key/value pair.
curl -X DELETE http://localhost:8999/api/v1/kv/themeAdd a new address.
Body (Geocoding model):
{
"formattedAddress": "1600 Amphitheatre Pkwy, Mountain View, CA",
"city": "Mountain View",
"state": "California",
"country": "United States of America",
"countryCode": "USA",
"latitude": 37.422,
"longitude": -122.0841
}List all saved addresses.
curl http://localhost:8999/api/v1/addressesGet one address by Mongo _id.
curl http://localhost:8999/api/v1/addresses/60d5f4832f8fb814c8a1e7d4- Search by fields. (exclude-address-id: omit
_idin filter) - Body same as above, just send the fields you want to match.
- Response: an array of one or more matching address objects.
If no matches are found, the API returns HTTP 404 with message
"address not found".
- Update an existing address.
- Body: full Geocoding object with
_idand fields to update.
Delete an address by Mongo _id.
Feel free to experiment with these endpoints via curl, Postman, or your preferred HTTP client.
- Basic Auth: set
ACTIVATE_BASIC_AUTH=yes - JWT: set
ACTIVATE_JWT=yes, configure keys & TTLs - 2FA: set
ACTIVATE_2FA=yes - CORS/firewall/rate limiter: toggle
ACTIVATE_CORS,ACTIVATE_FIREWALL,RATE_LIMIT - Middleware is wired in internal/router.
- Validate inputs early (handler & model)
- Use context.Context for cancellation & timeouts
- Structure code in layers for testability
- Log errors and HTTP events
- Keep secrets out of code (.env)
- Write unit & integration tests
- Fork & clone
- Create feature branch
- Write tests & update docs
- Submit a PR
MIT © pilinux