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.