Skip to content
moncef-anPublic

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

18 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Blog API

A RESTful Blog API built with Go, Fiber v3, GORM, and PostgreSQL.

The project is organized using a layered architecture to keep HTTP handlers, business logic, database access, and data models separated.

Features

  • User registration and login
  • JWT-based authentication
  • Access and refresh tokens
  • Password hashing with bcrypt
  • Create, update, read, and delete blog posts
  • Blog ownership checks
  • Blog likes
  • Comments on blog posts
  • Update and delete comments with ownership checks
  • Pagination for comments
  • PostgreSQL persistence with GORM
  • Automatic database migrations
  • Environment-based configuration
  • Layered architecture using handlers, services, and repositories

Tech Stack

Technology Purpose
Go Backend language
Fiber v3 HTTP web framework
GORM ORM
PostgreSQL Database
JWT Authentication
bcrypt Password hashing
UUID Entity identifiers
godotenv Environment configuration

Architecture

The API follows a simple layered architecture:

Client
  │
  ▼
Routes
  │
  ▼
Handlers
  │
  ▼
Services
  │
  ▼
Repositories
  │
  ▼
PostgreSQL

Project structure

.
├── cmd/
│   └── main.go
│
├── internal/
│   ├── Auth/
│   │   └── Auth.go
│   │
│   ├── database/
│   │   └── connection.go
│   │
│   ├── dto/
│   │   └── dto.go
│   │
│   ├── handlers/
│   │   ├── blogHandlers.go
│   │   ├── commenthandler.go
│   │   ├── likeHandler.go
│   │   └── userHandler.go
│   │
│   ├── middleware/
│   │   └── middleware.go
│   │
│   ├── models/
│   │   └── models.go
│   │
│   ├── repository/
│   │   ├── blogrepo.go
│   │   ├── commentrepo.go
│   │   ├── Likerepo.go
│   │   └── userrepo.go
│   │
│   ├── routes/
│   │   └── routes.go
│   │
│   └── service/
│       ├── blogservice.go
│       ├── commentservice.go
│       ├── LikeService.go
│       └── Userservice.go
│
├── .env
├── .gitignore
├── go.mod
├── go.sum
├── makefile
└── README.md

Database Models

The API currently contains four main entities:

User

  • id
  • username
  • email
  • password
  • bio
  • avatar_url
  • timestamps

Passwords are stored as bcrypt hashes and are never returned in JSON responses.

Blog

  • id
  • title
  • content
  • category
  • user_id
  • like_num
  • timestamps

A blog belongs to a user and can have comments and likes.

Comment

  • id
  • content
  • user_id
  • blog_id
  • timestamps

Comments belong to both a user and a blog.

Like

  • id
  • user_id
  • blog_id

A like associates a user with a blog.

Authentication

Authentication uses JWT.

When a user registers or logs in, the API returns:

{
  "access_token": "ACCESS_TOKEN",
  "refresh_token": "REFRESH_TOKEN"
}

The access token is valid for 15 minutes and the refresh token is valid for 7 days.

Protected routes require:

Authorization: Bearer <access_token>

The middleware validates the token and stores the authenticated user's ID in the Fiber context.

API Endpoints

Base URL:

http://localhost:3000

Authentication

Register

POST /register

Request:

{
  "UserName": "moncef",
  "email": "moncef@example.com",
  "password": "password123",
  "Bio": "Backend developer"
}

Response:

{
  "access_token": "ACCESS_TOKEN",
  "refresh_token": "REFRESH_TOKEN"
}

Login

POST /login

Request:

{
  "Email": "moncef@example.com",
  "Password": "password123"
}

Blogs

Get a blog

GET /blogs/:id

Example:

GET /blogs/550e8400-e29b-41d4-a716-446655440000

Create a blog

Authentication required

POST /blogs
Authorization: Bearer <access_token>

Request:

{
  "Title": "My first blog",
  "Content": "This is my first blog post.",
  "Category": "Programming"
}

Update a blog

Authentication required

PUT /blogs/:id
Authorization: Bearer <access_token>

Request:

{
  "title": "Updated title",
  "content": "Updated content.",
  "password": "password123"
}

The current implementation verifies the user's password and ensures that the authenticated user owns the blog before updating it.

Delete a blog

Authentication required

DELETE /blogs/:id
Authorization: Bearer <access_token>

Request:

{
  "Password": "password123"
}

The API verifies both the user's password and blog ownership before deletion.


Likes

Authentication required

Add a like

POST /like
Authorization: Bearer <access_token>

Request:

{
  "UserID": "USER_UUID",
  "BlogID": "BLOG_UUID"
}

Remove a like

DELETE /like
Authorization: Bearer <access_token>

Request:

{
  "ID": "LIKE_UUID",
  "UserID": "USER_UUID",
  "BlogID": "BLOG_UUID"
}

Comments

Create a comment

POST /posts/:postID/comments/
Authorization: Bearer <access_token>

Request:

{
  "content": "Great post!"
}

Get comments for a post

GET /posts/:postID/comments/?page=1&limit=10

Example:

GET /posts/BLOG_UUID/comments/?page=1&limit=10

Response:

{
  "comments": [
    {
      "id": "COMMENT_UUID",
      "content": "Great post!",
      "user_id": "USER_UUID",
      "blog_id": "BLOG_UUID",
      "created_at": "2026-08-10T10:00:00Z",
      "updated_at": "2026-08-10T10:00:00Z"
    }
  ],
  "meta": {
    "page": 1,
    "limit": 10,
    "total_items": 1,
    "total_pages": 1
  }
}

Pagination defaults to:

page  = 1
limit = 10

The maximum comment page size is 50.

Get a comment

GET /comments/:commentID

Update a comment

Authentication required

PUT /comments/:commentID
Authorization: Bearer <access_token>

Request:

{
  "content": "Updated comment"
}

Only the owner of the comment can update it.

Delete a comment

Authentication required

DELETE /comments/:commentID
Authorization: Bearer <access_token>

Only the owner of the comment can delete it.

Get comments written by a user

GET /users/:userID/comments/?page=1&limit=10

Environment Variables

Create a .env file in the project root:

JWT_SECRET=your-strong-secret
DSN=host=localhost user=postgres password=your-password dbname=Blogdb port=5432 sslmode=disable

Do not commit your real .env file or JWT secret to Git.

The application reads:

  • JWT_SECRET — secret used to sign and validate JWTs
  • DSN — PostgreSQL connection string

PostgreSQL Setup

Make sure PostgreSQL is installed and running.

Create the database:

CREATE DATABASE Blogdb;

You do not need to manually create the tables.

On startup, GORM runs:

db.AutoMigrate(
    &models.User{},
    &models.Blog{},
    &models.Comment{},
    &models.Like{},
)

and creates/updates the required tables.

Installation

1. Clone the repository

git clone https://github.com/moncef-an/Blog-API.git
cd Blog-API

2. Configure environment variables

Create .env:

JWT_SECRET=your-strong-secret
DSN=host=localhost user=postgres password=your-password dbname=Blogdb port=5432 sslmode=disable

3. Download dependencies

go mod download

4. Run the API

go run cmd/main.go

The server starts on:

http://localhost:3000

Makefile

The project includes a Makefile.

Run the application:

make run

Build the application:

make build

Clean the generated binary:

make clean

Testing with cURL

Register

curl -X POST http://localhost:3000/register \
  -H "Content-Type: application/json" \
  -d '{
    "UserName": "moncef",
    "email": "moncef@example.com",
    "password": "password123",
    "Bio": "Backend developer"
  }'

Login

curl -X POST http://localhost:3000/login \
  -H "Content-Type: application/json" \
  -d '{
    "Email": "moncef@example.com",
    "Password": "password123"
  }'

Then use the returned access token:

curl http://localhost:3000/blogs/BLOG_UUID \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Design Decisions

Repository Pattern

Repositories are responsible for database operations.

For example:

BlogHandler
    ↓
BlogService
    ↓
BlogRepository
    ↓
GORM
    ↓
PostgreSQL

This keeps database logic out of the HTTP handlers.

Service Layer

The service layer contains business rules such as:

  • verifying blog ownership
  • verifying passwords
  • validating comments
  • handling pagination
  • managing likes
  • creating authentication tokens

Middleware

Authentication is handled through Fiber middleware rather than repeating JWT validation inside every protected handler.

Security

The project currently implements:

  • bcrypt password hashing
  • JWT authentication
  • access/refresh token separation
  • protected routes
  • ownership checks for blog and comment modifications
  • environment variables for secrets and database credentials

For production, additional protections should be considered, such as:

  • refresh-token rotation/revocation
  • rate limiting
  • request validation
  • stricter CORS configuration
  • centralized error handling
  • database constraints for preventing duplicate likes
  • HTTPS
  • structured logging

Current Limitations / Future Improvements

The project is still evolving. Some useful next improvements would be:

  • Add a refresh-token endpoint
  • Add an endpoint to list all blogs
  • Add unique constraints for (user_id, blog_id) likes
  • Prevent a user from liking the same blog multiple times
  • Improve request validation
  • Add Swagger/OpenAPI documentation
  • Add automated unit and integration tests
  • Add Docker and Docker Compose
  • Add pagination for blogs
  • Add centralized error handling
  • Improve authentication context consistency across handlers
  • Add CI/CD with GitHub Actions

Learning Goals

This project was built to practice backend development concepts in Go, including:

  • REST API design
  • Fiber
  • PostgreSQL
  • GORM
  • JWT authentication
  • bcrypt
  • middleware
  • DTOs
  • repository pattern
  • service layer
  • dependency injection
  • pagination
  • relational data modeling

License

This project is intended for learning and portfolio purposes.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages