Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

README.md

Example2: Building a RESTful API with gorest

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.

Table of Contents

  1. Features
  2. Prerequisites
  3. Installation & Setup
  4. Environment Configuration
  5. Project Structure
  6. Running the Application
  7. Database Migrations
  8. API Reference & Examples
  9. Authentication & Security
  10. Best Practices
  11. Contributing
  12. License

Features

  • 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)

Prerequisites

  • Go 1.23+ installed
  • One or more databases:
    • RDBMS (MySQL, PostgreSQL, or SQLite)
    • Redis (optional)
    • MongoDB (optional)
  • Git & a terminal

Installation & Setup

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 tidy

Environment Configuration

All 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)

Project Structure

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

Running the Application

# From project root:
cd cmd/app
go build -o app
./app

Visit http://localhost:8999 and explore /api/v1/... endpoints.

Database Migrations

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
    )
}

API Reference & Examples

All routes are mounted under the base path /api/v1 and defined in internal/router/router.go.

Base URL: http://localhost:8999/api/v1


🔐 Authentication

POST /register

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"}'

POST /login

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"}'

🧑‍💻 Users

GET /users

Retrieve all users, their posts and hobbies.

curl http://localhost:8999/api/v1/users

GET /users/:id

Retrieve a single user by userID.

curl http://localhost:8999/api/v1/users/123

POST /users

Add 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"}'

PUT /users

Update authenticated user’s profile. (Requires JWT)

Body:

{
  "firstName": "Jon",
  "lastName": "Smith"
}

DELETE /users

Delete authenticated user account and all related posts/hobbies. (Requires JWT)


📝 Posts

GET /posts

Get all posts with pagination support.

Query parameters:

  • page (optional): Page number, default: 1
  • pageSize (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 /posts/:id

Get a post by postID.

curl http://localhost:8999/api/v1/posts/456

POST /posts

Create 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!"}'

PUT /posts/:id

Update a post you own. (Requires JWT)

Body:

{
  "title": "Updated Title",
  "body": "Updated content"
}

DELETE /posts/:id

Delete a single post. (Requires JWT)

DELETE /posts/all

Delete all your posts. (Requires JWT)


🎯 Hobbies

GET /hobbies

List all hobbies.

curl http://localhost:8999/api/v1/hobbies

GET /hobbies/:id

Get hobby by hobbyID.

curl http://localhost:8999/api/v1/hobbies/7

GET /hobbies/me

List your hobbies. (Requires JWT)

POST /hobbies

Add a hobby to your profile. (Requires JWT)

Body:

{
  "hobby": "painting"
}

DELETE /hobbies/:id

Remove a hobby from your profile. (Requires JWT)


🗝️ Key-Value (Redis)

POST /kv

Set a key/value.

Body:

{
  "key": "theme",
  "value": "dark"
}

GET /kv/:key

Get value by key.

curl http://localhost:8999/api/v1/kv/theme

DELETE /kv/:key

Delete a key/value pair.

curl -X DELETE http://localhost:8999/api/v1/kv/theme

📍 Addresses (MongoDB)

POST /addresses

Add 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
}

GET /addresses

List all saved addresses.

curl http://localhost:8999/api/v1/addresses

GET /addresses/:id

Get one address by Mongo _id.

curl http://localhost:8999/api/v1/addresses/60d5f4832f8fb814c8a1e7d4

POST /addresses/filter?exclude-address-id=<true|false>

  • Search by fields. (exclude-address-id: omit _id in 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".

PUT /addresses

  • Update an existing address.
  • Body: full Geocoding object with _id and fields to update.

DELETE /addresses/:id

Delete an address by Mongo _id.


Feel free to experiment with these endpoints via curl, Postman, or your preferred HTTP client.

Authentication & Security

  • 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.

Best Practices

  • 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

Contributing

  • Fork & clone
  • Create feature branch
  • Write tests & update docs
  • Submit a PR

License

MIT © pilinux