dokuzsozluk is currently under development. Features and structure may change in future versions.
A RESTful API backend for a collaborative forum platform. This project enables users to create categories, propose topics, write entries with voting mechanisms, and manage contributor roles and permissions.
Live Demo URL:
Live Demo Credentials:
- Admin:
admin/adminAdmin123* - User:
user/UserUser123* - Restricted:
restricted/restrictedRestricted123* - Banned:
banned/bannedBanned123*
- Features
- Tech Stack
- Architecture
- Prerequisites
- Installation & Setup
- Configuration
- Running the Application
- API Documentation
- Core Entities
- Authentication & Authorization
- Database Schema
- Development
- Contributing
- License
- User registration and authentication with JWT tokens
- Role-based access control (Admin, User, Restricted, Banned)
- Refresh token rotation for enhanced security
- User profiles with unique slugs
- Activity tracking (created_at, updated_at)
- Categories: Hierarchical organization of topics
- Topics: Subject areas created within categories
- Entries: Full article/contribution system for topics
- Content versioning with author tracking
- Soft deletion support
- Vote on entries (upvote/downvote concept)
- Vote count tracking per entry
- Prevent duplicate votes from same user
- Vote statistics in responses
- Full-text search across topics, entries, users, and categories
- Pagination and sorting support
- Query filtering and ordering (ascending/descending)
- User-specific content listings
- Admin Role: Full system access, user management
- User Role: Standard contribution privileges
- Restricted Role: Limited access (read-only or limited creation)
- Banned Role: Revoked access
- Granular permission system for category and content creation/updates
- JWT-based authentication with configurable expiration
- Refresh token validation and rotation
- CORS support for frontend integration
- Rate limiting (IP-based and user-based hybrid approach)
- Password hashing with bcrypt
- Bearer token authentication in headers
- Swagger/OpenAPI documentation with interactive UI
- Comprehensive error handling with structured responses
- Request validation with Go Playground validator
- Pagination for large datasets
- RESTful endpoint design
- Language: Go 1.25.5
- Framework: Gorilla/mux (HTTP router)
- Database: PostgreSQL 14+
- ORM/Query Builder: Database/sql with custom queries
- Authentication: JWT (golang-jwt/jwt)
- Password Hashing: golang.org/x/crypto (bcrypt)
- Validation: go-playground/validator
- Migration Tool: golang-migrate/migrate
- API Documentation: Swaggo (Swagger/OpenAPI)
- Configuration: godotenv (environment variables)
- CORS: rs/cors
- UUID Generation: google/uuid
- Primary DBMS: PostgreSQL
- Driver: lib/pq
- Migrations: SQL-based schema management
The project follows a layered hexagonal architecture pattern:
cmd/
├── api/
│ └── main.go # Entry point
│
internal/
├── bootstrap/
│ └── server.go # Server initialization
│
├── domain/ # Business entities
│ ├── user/
│ ├── category/
│ ├── topic/
│ ├── entry/
│ ├── entryvote/
│ ├── rolepermission/
│ ├── refreshtoken/
│ └── shared/
│
├── usecase/ # Business logic
│ ├── auth/
│ ├── user/
│ ├── category/
│ ├── topic/
│ ├── entry/
│ ├── entryvote/
│ ├── health/
│ └── ...
│
└── infrastructure/ # Technical implementation
├── config/ # Configuration management
├── database/ # Database connections
├── router/ # HTTP routing
├── middleware/ # HTTP middleware
└── repository/ # Data access layer
- Repository Pattern: Abstract data access
- Use Case Pattern: Business logic encapsulation
- Dependency Injection: Loose coupling via constructors
- Middleware Chain: Cross-cutting concerns (auth, validation, CORS)
- Go: 1.25.5 or higher
- PostgreSQL: 14 or higher
- Git: For version control
- PostgreSQL server running and accessible
- Go modules support enabled
- Port 8080 available (configurable)
git clone https://github.com/dokuzsertkol/dokuzsozluk.git
cd dokuzsozlukgo mod download
go mod tidygo install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latestCopy the .env.example file to .env and configure:
cp .env.example .envCreate a .env file in the project root with the following variables:
# Database Configuration
DB_URL=postgres://username:password@localhost:5432/dokuzsozluk?sslmode=disable
DB_USER=username
DB_PASS=password
DB_NAME=dokuzsozluk
DB_HOST=localhost
DB_PORT=5432
# Server Configuration
PORT=8080
# JWT Configuration
JWT_SECRET=your-super-secret-jwt-key-minimum-64-characters
JWT_EXPIRATION_MINS=15
REFRESH_SECRET=your-super-secret-refresh-key-minimum-64-characters
REFRESH_EXPIRATION_DAYS=7
REFRESH_EXPIRATION_DAYS_SHORT=1
# CORS Configuration
FRONTEND_URL=http://localhost:3000
# Rate Limiting
RATE_LIMIT_RATE=1
RATE_LIMIT_BURST=30
# API Documentation
SWAGGER_ENABLED=true| Variable | Description | Default | Example |
|---|---|---|---|
DB_URL |
Full PostgreSQL connection string | - | postgres://user:pass@localhost:5432/dokuzsozluk |
PORT |
Server port | 8080 |
8080 |
JWT_EXPIRATION_MINS |
Access token expiration (minutes) | 15 |
15 |
REFRESH_EXPIRATION_DAYS |
Refresh token expiration (days) | 7 |
7 |
RATE_LIMIT_RATE |
Rate limit requests per second | 1 |
1 |
RATE_LIMIT_BURST |
Rate limit burst size | 30 |
30 |
SWAGGER_ENABLED |
Enable Swagger UI | true |
true/false |
psql -U postgres
CREATE DATABASE dokuzsozluk;
\qgo run cmd/migrate/main.gogo run cmd/api/main.goThe server will start on http://localhost:8080 (configurable via PORT env var).
curl http://localhost:8080/api/healthExpected response:
{
"status": "OK",
"timestamp": "2026-04-10T12:00:00Z"
}- URL:
http://localhost:8080/swagger/index.html - Requires
SWAGGER_ENABLED=truein.env - Interactive API testing interface
- Auto-generated from code annotations
http://localhost:8080/api
All protected endpoints require a Bearer token in the Authorization header:
Authorization: Bearer <access_token>
POST /auth/login- Authenticate user, returns access & refresh tokensPOST /auth/logout- Revoke refresh tokenPOST /auth/refresh- Get new access token using refresh tokenPOST /auth/register- Create new user
GET /users- List all users (with search, pagination, sorting)GET /users/{slug}- Get user by slugGET /users/me- Get authed user detailsGET /users/{slug}/entries- Get entries by userGET /users/{slug}/topics- Get topics by userPATCH /users/{id}- Update user profile (requires permission)PATCH /users/me- Update authed user profile (requires permission)
GET /categories- List categories (searchable, paginated)GET /categories/{slug}- Get category by slugPOST /categories- Create new category (requires permission)PATCH /categories/{id}- Update category (requires permission)
GET /topics- List all topics (searchable, paginated, sortable)GET /topics/{slug}- Get topic by slugPOST /topics- Create new topic (requires permission)PATCH /topics/{id}- Update topic (requires permission)
POST /topics/{topicID}/entries- Create new entry to an existing topic (requires permission)PATCH /entries/{id}- Update entry (requires permission)
POST /entries/{entryId}/vote- Vote on an entry
GET /health- System health check
id UUID PRIMARY KEY
username VARCHAR(30) UNIQUE NOT NULL
email VARCHAR(255) UNIQUE NOT NULL
password_hash VARCHAR(255) NOT NULL
avatar_url VARCHAR(512) NOT NULL DEFAULT ''
bio TEXT NOT NULL DEFAULT ''
score INTEGER NOT NULL DEFAULT 0
entry_count INTEGER NOT NULL DEFAULT 0
is_online BOOLEAN NOT NULL DEFAULT false
role_id INTEGER FOREIGN KEY -> roles.id
slug TEXT
created_at TIMESTAMPTZ NOT NULL
updated_at TIMESTAMPTZ
deleted_at TIMESTAMPTZid INTEGER PRIMARY KEY
title VARCHAR(50) UNIQUE NOT NULL
description VARCHAR(255) NOT NULL
sort_order SMALLINT NOT NULL DEFAULT 1
slug TEXT
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
updated_at TIMESTAMPTZ
deleted_at TIMESTAMPTZid INTEGER PRIMARY KEY
title VARCHAR(50) UNIQUE NOT NULL
category_id INTEGER FOREIGN KEY -> categories.id
author_id UUID FOREIGN KEY -> users.id (nullable)
slug TEXT
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
updated_at TIMESTAMPTZ
deleted_at TIMESTAMPTZid INTEGER PRIMARY KEY
content TEXT NOT NULL
author_id UUID FOREIGN KEY -> users.id
topic_id INTEGER FOREIGN KEY -> topics.id
parent_id INTEGER FOREIGN KEY -> entries.id (nullable)
upvote_count INTEGER NOT NULL DEFAULT 0
downvote_count INTEGER NOT NULL DEFAULT 0
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
updated_at TIMESTAMPTZ
deleted_at TIMESTAMPTZid INTEGER PRIMARY KEY
entry_id INTEGER FOREIGN KEY -> entries.id
user_id UUID FOREIGN KEY -> users.id
vote SMALLINT NOT NULL (1 or -1)
UNIQUE(entry_id, user_id)id INTEGER PRIMARY KEY
name VARCHAR(30) UNIQUE NOT NULLid INTEGER PRIMARY KEY
name VARCHAR(30) UNIQUE NOT NULLrole_id INTEGER FOREIGN KEY -> roles.id
permission_id INTEGER FOREIGN KEY -> permissions.id
PRIMARY KEY (role_id, permission_id)id UUID PRIMARY KEY
user_id UUID FOREIGN KEY -> users.id ON DELETE CASCADE
token_hash TEXT NOT NULL
expires_at TIMESTAMPTZ NOT NULL
created_at TIMESTAMPTZ NOT NULL
revoked_at TIMESTAMPTZ- Login: User submits credentials
- Token Generation: Server returns access token (15 mins) + refresh token (7 days)
- Token Usage: Client includes access token in Authorization header
- Token Refresh: When access token expires, use refresh token to get new access token
- Logout: Refresh token is revoked in database
| Role | Description | Capabilities |
|---|---|---|
| Admin | Full system access | Manage users, categories, topics, and entries |
| User | Standard contributor | Create topics, write entries, vote |
| Restricted | Limited access | Read-only or restricted creation |
| Banned | No access | Account locked |
Permissions are linked to roles and define what actions can be performed on resources:
CATEGORY_CREATE- Create new categoriesCATEGORY_UPDATE- Update categoriesCATEGORY_DELETE- Delete categoriesTOPIC_CREATE- Create new topicsTOPIC_UPDATE- Update topicsENTRY_CREATE- Create new entriesENTRY_UPDATE- Update entriesUSER_MANAGE- Manage usersVIEW_ALL- View all content
# Get access token
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"user","password":"UserUser123*"}'
# Response includes tokens
# {
# "access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
# "refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
# "token_type": "Bearer",
# "expires_in": 900
# }
# Use access token in subsequent requests
curl http://localhost:8080/api/users \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc..."┌──────────────────┐
│ users │
├──────────────────┤
│ id (PK) │
│ username (UQ) │
│ email (UQ) │
│ role_id (FK) │◉──────┐
└──────────────────┘ │
│
┌──────────────────┐ │
│ roles │◄──────┘
├──────────────────┤
│ id (PK) │
│ name (UQ) │
└──────────────────┘
┌──────────────────┐
│ categories │
├──────────────────┤
│ id (PK) │
│ name │
│ slug (UQ) │
│ created_by (FK) │────┐
└──────────────────┘ │
▲ │
│ │
│ (FK) │
┌──────────────────┐ │
│ topics │ │
├──────────────────┤ │
│ id (PK) │ │
│ category_id (FK) │◄───┘
│ author_id (FK) │────┐
││ slug (UQ) │ │
└──────────────────┘ │
▲ │
│ │
│ │
┌──────────────────┐ │
│ entries │ │
├──────────────────┤ │
│ id (PK) │ │
│ topic_id (FK) │◄──┘
│ author_id (FK) │────┐
│ vote_count │ │
└──────────────────┘ │
▲ │
│ │
│ │
┌──────────────────┐ │
│ entry_votes │ │
├──────────────────┤ │
│ id (PK) │ │
│ entry_id (FK) │◄──┘
│ user_id (FK) │────────┐
│ vote │ │
└──────────────────┘ │
│
┌────────┴──────┐
│ │
(back to users) │
│
┌──────────────┘
│
┌──────────────────┐│
│ role_permissions│
├──────────────────┤
│ id (PK) │
│ role_id (FK) │
│ permission │
│ resource │
│ action │
└──────────────────┘
For optimized queries:
- users: username, email, slug
- topics: category_id, author_id, slug
- entries: topic_id, author_id
- entry_votes: entry_id, user_id (unique composite)
go build -o bin/dokuzsozluk cmd/api/main.gogo test ./...- cmd/: Executable entry points
- internal/domain/: Business entity definitions and interfaces
- internal/usecase/: Business logic and workflows
- internal/infrastructure/: Technical implementation details
- migrations/: Database schema changes
- Define domain entities in
internal/domain/ - Create interfaces for data access
- Implement business logic in
internal/usecase/ - Add repository implementations in
internal/infrastructure/repository/ - Create HTTP handlers in
internal/infrastructure/router/ - Add database migrations in
migrations/ - Update Swagger documentation in handler comments
# Add a new dependency
go get github.com/package/name
# Update a dependency
go get -u github.com/package/name
# Clean up unused dependencies
go mod tidy- Fork the repository
- Clone your fork locally
- Create a feature branch:
git checkout -b feature/your-feature-name - Install dependencies:
go mod download
- Write Code: Make your changes following Go conventions
- Test Locally: Run migrations and test endpoints
- Update Docs: Update Swagger comments if API changes
- Commit: Use clear, descriptive commit messages
git commit -m "feat: add new voting system" git commit -m "fix: correct user authentication flow" - Push: Push to your fork
git push origin feature/your-feature-name - Pull Request: Create PR with:
- Clear title and description
- Reference to any related issues
- Screenshots/examples if applicable
- Follow standard Go conventions (gofmt, golint)
- Use meaningful variable and function names
- Add comments for exported functions and complex logic
- Keep functions focused and concise
.env- Environment configuration (excluded from git)migrations/- Database schemainternal/- Core application codecmd/api/main.go- Application entry point
This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.
Found a bug? Have a feature request? Please create an issue on GitHub: Create Issue
- Project: dokuzsozluk - Advanced Forum Backend in Go
- GitHub: dokuzsertkol/dokuzsozluk
- Language: Go 1.25.5
- Database: PostgreSQL 14+
Last Updated: April 10, 2026