README
ΒΆ
RealWorld Backend - Go Implementation
A production-ready social blogging platform backend (like Medium.com) built with Go, featuring clean architecture, comprehensive testing, and optimized queries.
This project adheres to the RealWorld API specification, demonstrating modern Go development practices including JWT authentication, proper error handling, and 80%+ test coverage.
Table of Contents
- RealWorld Backend - Go Implementation
Features
β Implemented Endpoints
-
Authentication & Authorization
- User registration and login
- JWT-based authentication
- Get/Update current user profile
-
Articles
- Create, read, update, and delete articles
- List articles with filtering (by tag, author, favorited by user)
- Pagination support (limit/offset)
- Feed of articles from followed users
- Favorite/unfavorite articles
- Article slugs with automatic generation
-
Comments
- Add comments to articles
- Get all comments for an article
- Delete comments
-
User Profiles
- View user profiles
- Follow/unfollow users
-
Tags
- List all tags (with persistence)
π Key Features
- Clean Architecture: Separation of concerns with handlers, data layer, and business logic
- Optimized Queries: Efficient single-query operations for common use cases
- Comprehensive Testing: 150 unit tests + Newman API integration tests
- Input Validation: Request validation with custom validator package
- Error Handling: Structured error responses with proper HTTP status codes
- Graceful Shutdown: Clean server shutdown with context cancellation
Tech Stack
- Language: Go 1.25+
- Web Framework: chi - Lightweight, idiomatic HTTP router
- Database: PostgreSQL 15+ with pgx driver
- Authentication: JWT tokens with golang-jwt
- Query Builder: Squirrel for dynamic SQL
- Testing: Standard
testingpackage + testify - Task Runner: Task for development workflows
- API Testing: Newman (Postman CLI)
Prerequisites
- Go 1.25 or higher
- PostgreSQL 15 or higher
- Docker & Docker Compose (optional, for Postgres)
- Task (optional but recommended):
brew install go-task - Node.js (for Newman API tests):
brew install node
Quick Start
# Clone the repository
git clone https://github.com/manas-solves/realworld-backend.git
cd realworld-backend
# Install dependencies
task install:dependencies
# Start the server (automatically starts Postgres, runs migrations)
task server:start
# In another terminal, verify it's running
curl http://localhost:4000/healthcheck
The API will be available at http://localhost:4000.
Development
Project Structure
Click to expand
.
βββ cmd/api/ # Application entry point
β βββ main.go # Server initialization
β βββ routes.go # Route definitions
β βββ handlers (*.go) # HTTP handlers
β βββ middleware.go # Authentication, recovery, etc.
β βββ *_test.go # Integration tests
β βββ openapi.yml # API specification
βββ internal/
β βββ auth/ # JWT token generation & validation
β βββ data/ # Data models and operations
β β βββ articles.go # Article CRUD, favorites, feed
β β βββ users.go # User management, authentication
β β βββ comments.go # Comment operations
β β βββ tags.go # Tag management
β β βββ store.go # Store interfaces and initialization
β βββ validator/ # Input validation utilities
β βββ vcs/ # Version information
βββ migrations/ # Database schema migrations
βββ docker-compose.yml # PostgreSQL container definition
βββ Taskfile.yml # Development task definitions
Database Schema
Click to expand
The database schema consists of 6 main tables with the following relationships:
erDiagram
users ||--o{ articles : "authors"
users ||--o{ comments : "writes"
users ||--o{ favorites : "favorites"
users ||--o{ follows : "follower"
users ||--o{ follows : "followed"
articles ||--o{ comments : "has"
articles ||--o{ favorites : "favorited_by"
users {
bigserial id PK
citext username UK
citext email UK
bytea password_hash
text bio
text image
integer version
}
articles {
serial id PK
varchar slug UK
varchar title
text description
text body
text_array tag_list
timestamp created_at
timestamp updated_at
integer favorites_count
integer author_id FK
integer version
}
comments {
serial id PK
text body
integer article_id FK
bigint author_id FK
timestamp created_at
timestamp updated_at
}
favorites {
integer user_id PK_FK
integer article_id PK_FK
}
follows {
integer follower_id PK_FK
integer followed_id PK_FK
}
tags {
serial id PK
varchar tag UK
}
Key Relationships:
- users β articles: One-to-many (a user can create many articles)
- users β comments: One-to-many (a user can write many comments)
- users β users (via follows): Many-to-many (users can follow each other)
- users β articles (via favorites): Many-to-many (users can favorite many articles)
- articles β comments: One-to-many (an article can have many comments)
- tags: Standalone table for tag persistence (articles store tags in
tag_listarray)
Indexes:
- Articles:
slug,author_id,created_at,tag_list(GIN index) - Comments:
article_id,author_id,created_at - Favorites:
user_id,article_id - Tags:
tag - Follows: Composite primary key on
(follower_id, followed_id)
Running the Server
Click to expand
Quick Start (Recommended)
# Starts Postgres + runs migrations + starts server
task server:start
Manual Start
# Build the binary
task server:build
# Run with custom configuration
./bin/api \
-port 8080 \
-env production \
-db-dsn $DB_DSN \
-jwt-secret $JWT_SECRET
Configuration Options
./bin/api -help
Flags:
-port int
API server port (default 4000)
-env string
Environment (development|staging|production) (default "development")
-db-dsn string
PostgreSQL DSN (required)
-db-max-open-conns int
PostgreSQL max open connections (default 25)
-db-max-idle-time duration
PostgreSQL max connection idle time (default 15m)
-jwt-secret string
JWT secret key (required)
-jwt-issuer string
JWT issuer (default "realworld-api")
Task Runner
Click to expand
This project uses Task for managing common development workflows. All tasks are defined in Taskfile.yml.
Key Tasks
Development
# Start the server (auto-starts Postgres, runs migrations)
task server:start
# Build the binary
task server:build
# View all available command-line flags
task server:help
Database Management
# Start Postgres container
task docker:postgres:start
# Stop Postgres container
task docker:postgres:stop
# Create a new migration
task db:migrations:new -- create_some_table
# Run migrations
task db:migrations:up
# Rollback migrations
task db:migrations:down
# Setup database (create)
task db:setup
# Teardown database (drop)
task db:teardown
Testing & Quality
# Run all unit tests with coverage
task test
# Visualize coverage report
task visualize:coverage
# Run Newman API integration tests
task newman
# Run linters, formatters, and security checks
task audit
Dependencies
# Install required tools (migrate, golangci-lint, etc.)
task install:dependencies
# Update all Go dependencies
task update:dependencies
Environment Variables
All tasks automatically use environment variables defined in Taskfile.yml:
env:
DB_DSN: postgres://postgres:postgres@localhost:5432/conduit?sslmode=disable
JWT_SECRET: some-secret
JWT_ISSUER: conduit
Edit these values in Taskfile.yml to customize your local development environment.
API Documentation
Full API specification: OpenAPI YAML
Authentication Endpoints
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| POST | /users |
Register new user | No |
| POST | /users/login |
Login user | No |
| GET | /user |
Get current user | Yes |
| PUT | /user |
Update user | Yes |
Article Endpoints
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| GET | /articles |
List articles (filterable, paginated) | No |
| GET | /articles/feed |
Get feed from followed users | Yes |
| POST | /articles |
Create article | Yes |
| GET | /articles/:slug |
Get article by slug | No |
| PUT | /articles/:slug |
Update article | Yes (author only) |
| DELETE | /articles/:slug |
Delete article | Yes (author only) |
| POST | /articles/:slug/favorite |
Favorite article | Yes |
| DELETE | /articles/:slug/favorite |
Unfavorite article | Yes |
Query Parameters for List Articles:
tag- Filter by tag nameauthor- Filter by author usernamefavorited- Filter by username who favoritedlimit- Max articles to return (default: 20, max: 100)offset- Number of articles to skip (default: 0)
Comment Endpoints
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| POST | /articles/:slug/comments |
Add comment to article | Yes |
| GET | /articles/:slug/comments |
Get comments for article | No |
| DELETE | /articles/:slug/comments/:id |
Delete comment | Yes (author only) |
Profile & Tag Endpoints
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| GET | /profiles/:username |
Get user profile | No |
| POST | /profiles/:username/follow |
Follow user | Yes |
| DELETE | /profiles/:username/follow |
Unfollow user | Yes |
| GET | /tags |
Get all tags | No |
Testing
# Run all tests
task test
# Visualize code coverage (run after 'task test')
task visualize:coverage
# Run Newman API integration tests (full RealWorld spec)
task newman
# Run linting and security checks
task audit
Test Coverage
- 148 unit tests covering handlers, data layer, and authentication
- 311 Newman assertions validating full API compliance
- 77%+ code coverage across the application layer
Writing Tests
Tests use the standard Go testing package with testify for assertions:
func TestCreateArticle(t *testing.T) {
t.Parallel()
ts := newTestServer(t)
token := loginUser(t, ts, "user@example.com", "password")
// Test implementation...
}
Configuration
Environment Variables
For local development, environment variables are managed via Taskfile.yml:
env:
DB_DSN: postgres://postgres:postgres@localhost:5432/conduit?sslmode=disable
JWT_SECRET: some-secret
JWT_ISSUER: conduit
These variables are automatically applied when running tasks like task server:start or task test.
To customize local values, edit the env: section in Taskfile.yml.
Production Configuration
For production, set environment variables directly (never commit secrets):
export DB_DSN="postgres://user:pass@host:5432/conduit?sslmode=disable"
export JWT_SECRET="strong-random-secret"
export JWT_ISSUER="your-app-name"
CI/CD
GitHub Actions workflows run on every push and pull request:
Main CI Pipeline
- Linting:
golangci-lintwith strict rules - Security:
govulncheckfor vulnerability scanning - Unit Tests: Full test suite with coverage reporting
- Integration Tests: Newman tests against RealWorld API spec
- Coverage: Automatic upload to Codecov
See .github/workflows/CI.yml for details.
Secret Scanning
Automated secret detection using Gitleaks to prevent accidental exposure of sensitive data like API keys, passwords, and tokens.
See .github/workflows/secret-scanning.yml for details.
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Write tests for your changes
- Ensure all tests pass (
task test) - Run linting (
task audit) - Commit your changes with meaningful messages
- Push to your branch
- Open a Pull Request
Code Style
- Follow Effective Go guidelines
- Use
gofmtfor formatting (automatic withtask audit) - Write table-driven tests where appropriate
- Document exported functions and types
Acknowledgments
- RealWorld - API specification and inspiration
- Let's Go Further - Architecture patterns and best practices
License
This project is open source and available under the MIT License.
Built with β€οΈ using Go