A simple RESTful API with basic CRUD operations for a personal blogging platform built with Go, Gorilla Mux, and PostgreSQL.
Built as a learning project for Backend Roadmap on roadmap.sh.
- Features
 - Tech Stack
 - Prerequisites
 - Getting Started - Local Development Setup
 - API Endpoints
 - Project Structure
 - Error Handling
 
- ✅ Create new blog posts
 - ✅ Update existing blog posts
 - ✅ Delete blog posts
 - ✅ Retrieve a single blog post by ID
 - ✅ Retrieve all blog posts
 - ✅ Search blog posts by term (searches in title, content, and category)
 - ✅ Request ID tracking for debugging
 - ✅ Comprehensive logging middleware
 - ✅ Input validation with proper error responses
 
- Language: Go 1.24
 - Web Framework: Gorilla Mux
 - Database: PostgreSQL
 - Environment Management: godotenv
 - Database Driver: lib/pq
 
- Go 1.24 or higher
 - PostgreSQL 12 or higher
 - Git
 
Follow these steps to set up the project on your local machine:
- macOS:
brew install go
 - Linux (Ubuntu/Debian):
sudo apt update sudo apt install golang-go
 - Windows: Download and install from golang.org/dl
 
Verify installation:
go version- macOS:
brew install postgresql@15 brew services start postgresql@15
 - Linux (Ubuntu/Debian):
sudo apt update sudo apt install postgresql postgresql-contrib sudo systemctl start postgresql sudo systemctl enable postgresql - Windows: Download and install from postgresql.org/download
 
Verify installation:
psql --versiongit clone https://github.com/architxkumar/Blogging-Platform-API.git
cd Blogging-Platform-APIgo mod download- 
Create a PostgreSQL user (if needed):
# On macOS/Linux, switch to postgres user sudo -u postgres psql # Or connect directly psql postgres
Inside PostgreSQL prompt:
CREATE USER your_username WITH PASSWORD 'your_password'; ALTER USER your_username CREATEDB; \q
 - 
Create the database:
# Connect to PostgreSQL psql -U your_username -d postgresInside PostgreSQL prompt:
CREATE DATABASE blogs; \q
 - 
Run the schema to create tables:
psql -U your_username -d blogs -f internal/db/schema.sql
 - 
(Optional) Load sample data for testing:
psql -U your_username -d blogs -f internal/db/sample_posts_insert.sql
 
Create a .env.development file in the root directory of the project:
# Create the file
touch .env.developmentAdd the following configuration (update with your PostgreSQL credentials):
PGHOST=localhost
PGPORT=5432
PGUSER=your_username
PGPASSWORD=your_password
PGSSLNEGOTIATION=disable
PGDATABASE=blogsExample:
PGHOST=localhost
PGPORT=5432
PGUSER=architxkumar
PGPASSWORD=architxkumar
PGSSLNEGOTIATION=disable
PGDATABASE=blogsStart the server:
go run main.goYou should see output indicating the server is running. The API will be available at http://localhost:8080
Test that the API is working by making a request:
Using curl:
# Get all posts
curl http://localhost:8080/posts
# Create a new post
curl -X POST http://localhost:8080/posts \
  -H "Content-Type: application/json" \
  -d '{
    "title": "My First Blog Post",
    "content": "This is the content of my first blog post.",
    "category": "Technology",
    "tags": ["Tech", "Programming"]
  }'Using a browser:
- Open your browser and navigate to 
http://localhost:8080/posts - You should see a JSON response with all blog posts
 
Database connection errors:
- Verify PostgreSQL is running: 
pg_isready - Check your credentials in 
.env.development - Ensure the database 
blogsexists:psql -U your_username -l 
Port already in use:
- The application runs on port 8080 by default
 - Check if another application is using the port: 
lsof -i :8080(macOS/Linux) - Kill the process or modify the port in 
main.go 
Go module errors:
- Delete 
go.sumand rungo mod downloadagain - Run 
go mod tidyto clean up dependencies 
Create a new blog post.
Endpoint: POST /posts
Request Body:
{
  "title": "My First Blog Post",
  "content": "This is the content of my first blog post.",
  "category": "Technology",
  "tags": ["Tech", "Programming"]
}Validation Rules:
title: Required, maximum 255 characterscontent: Requiredcategory: Optional, maximum 30 characterstags: Optional, array of strings
Success Response (201 Created):
{
  "id": 1,
  "title": "My First Blog Post",
  "content": "This is the content of my first blog post.",
  "category": "Technology",
  "tags": ["Tech", "Programming"],
  "created_at": "2021-09-01T12:00:00Z",
  "updated_at": "2021-09-01T12:00:00Z"
}Error Responses:
400 Bad Request- Invalid JSON format or empty request body422 Unprocessable Entity- Validation errors (empty title, title too long, empty content, category too long)
Update an existing blog post.
Endpoint: PUT /posts/{id}
URL Parameters:
id(integer, required) - The ID of the blog post to update
Request Body:
{
  "title": "My Updated Blog Post",
  "content": "This is the updated content of my first blog post.",
  "category": "Technology",
  "tags": ["Tech", "Programming", "Updated"]
}Validation Rules:
- Same as Create Blog Post
 - ID must be a positive integer
 
Success Response (200 OK):
{
  "id": 1,
  "title": "My Updated Blog Post",
  "content": "This is the updated content of my first blog post.",
  "category": "Technology",
  "tags": ["Tech", "Programming", "Updated"],
  "created_at": "2021-09-01T12:00:00Z",
  "updated_at": "2021-09-01T12:30:00Z"
}Error Responses:
400 Bad Request- Invalid ID format or validation errors404 Not Found- Blog post with the given ID not found422 Unprocessable Entity- Validation errors
Delete an existing blog post.
Endpoint: DELETE /posts/{id}
URL Parameters:
id(integer, required) - The ID of the blog post to delete
Success Response (204 No Content): No response body
Error Responses:
400 Bad Request- Invalid ID format404 Not Found- Blog post with the given ID not found
Retrieve a single blog post by ID.
Endpoint: GET /posts/{id}
URL Parameters:
id(integer, required) - The ID of the blog post to retrieve
Success Response (200 OK):
{
  "id": 1,
  "title": "My First Blog Post",
  "content": "This is the content of my first blog post.",
  "category": "Technology",
  "tags": ["Tech", "Programming"],
  "created_at": "2021-09-01T12:00:00Z",
  "updated_at": "2021-09-01T12:00:00Z"
}Error Responses:
400 Bad Request- Invalid ID format404 Not Found- Blog post with the given ID not found
Retrieve all blog posts.
Endpoint: GET /posts
Success Response (200 OK):
[
  {
    "id": 1,
    "title": "My First Blog Post",
    "content": "This is the content of my first blog post.",
    "category": "Technology",
    "tags": ["Tech", "Programming"],
    "created_at": "2021-09-01T12:00:00Z",
    "updated_at": "2021-09-01T12:00:00Z"
  },
  {
    "id": 2,
    "title": "My Second Blog Post",
    "content": "This is the content of my second blog post.",
    "category": "Lifestyle",
    "tags": ["Life", "Tips"],
    "created_at": "2021-09-01T12:30:00Z",
    "updated_at": "2021-09-01T12:30:00Z"
  }
]Search and filter blog posts by a search term. The search is performed on the title, content, and category fields using a wildcard (LIKE) query.
Endpoint: GET /posts?term={search_term}
Query Parameters:
term(string, optional) - Search term to filter posts
Example Request:
GET /posts?term=tech
This will return all blog posts that contain "tech" (case-insensitive) in their title, content, or category.
Success Response (200 OK):
[
  {
    "id": 1,
    "title": "My First Blog Post",
    "content": "This is the content about technology.",
    "category": "Technology",
    "tags": ["Tech", "Programming"],
    "created_at": "2021-09-01T12:00:00Z",
    "updated_at": "2021-09-01T12:00:00Z"
  }
]Notes:
- If no 
termparameter is provided, all posts are returned (same as Get All Blog Posts) - The search is case-insensitive
 - The search performs a wildcard match (partial matching)
 
Blogging-Platform-API/
├── main.go                    # Application entry point
├── internal/
│   ├── handler/               # HTTP request handlers
│   │   ├── task_creation.go   # POST /posts handler
│   │   ├── task_updation.go   # PUT /posts/{id} handler
│   │   ├── deletion.go        # DELETE /posts/{id} handler
│   │   └── retrival.go        # GET /posts and /posts/{id} handlers
│   ├── middleware/            # HTTP middlewares
│   │   ├── logging.go         # Request logging
│   │   ├── request_id.go      # Request ID generation
│   │   └── validation.go      # Input validation
│   ├── model/                 # Data models
│   │   └── post.go            # Post and PostDTO structs
│   └── db/                    # Database files
│       ├── schema.sql         # Database schema
│       └── sample_posts_insert.sql  # Sample data
├── .env.development           # Environment configuration
├── go.mod                     # Go module definition
└── README.md                  # This file
The API uses standard HTTP status codes:
200 OK- Successful GET or PUT request201 Created- Successful POST request204 No Content- Successful DELETE request400 Bad Request- Invalid request format or parameters404 Not Found- Resource not found422 Unprocessable Entity- Validation errors500 Internal Server Error- Server-side errors
All error responses include a descriptive error message in the response body.
The following features are intentionally not implemented as per project requirements:
- Pagination
 - Authentication
 - Authorization
 - Rate limiting
 
License: MIT
Author: Archit Kumar
Repository: https://github.com/architxkumar/Blogging-Platform-API
Note: This README documentation was generated with AI assistance due to time constraints. However, all the API implementation logic, database schema, handlers, middleware, and core functionality were developed by Archit Kumar.