A RESTful task management API built with Go, following Clean Architecture principles. Users can register, log in, and manage their own tasks. Authentication is handled via HTTP-only JWT cookies.
| Layer | Technology |
|---|---|
| Language | Go 1.26 |
| HTTP Framework | Gin |
| Database | MongoDB (via mongo-driver v1) |
| Authentication | JWT (golang-jwt/jwt v5) — stored in HTTP-only cookie |
| Password Hashing | bcrypt (golang.org/x/crypto) |
| Config | .env via godotenv |
The project follows Clean Architecture with clear separation of concerns:
.
├── delivery/ # HTTP layer (entrypoint, routers, controllers)
│ ├── main.go
│ ├── routers/
│ └── controllers/
├── usecases/ # Business logic
├── repositories/ # Data access (MongoDB)
├── domain/ # Entities & interfaces
└── infrastructure/ # Cross-cutting concerns (JWT, password hashing, auth middleware)
Dependency flow: delivery → usecases → repositories → domain ← infrastructure
-
Clone the repository
git clone <repo-url> cd "Go task manager api"
-
Install dependencies
go mod download
-
Configure environment
Copy the example env file and fill in your values:
cp .env.example .env
Variable Description Example DB_URIMongoDB connection string mongodb://127.0.0.1:27017DB_NAMEDatabase name task_managerCONTEXT_TIMEOUTRequest timeout in seconds 10JWT_SECRETSecret key for signing JWTs your-random-secretJWT_EXPIRY_HOURJWT lifetime in hours 1SERVER_ADDRESSAddress the server listens on localhost:8000 -
Run the server
go run delivery/main.go
The API will be available at
http://localhost:8000.
Authentication uses HTTP-only cookies. After a successful /register or /login, the server sets an access_token cookie automatically. All protected routes require this cookie to be present.
Register a new user. Automatically logs in on success.
Request body:
{
"email": "user@example.com",
"password": "yourpassword"
}Responses:
| Status | Meaning |
|---|---|
201 Created |
Registered (and logged in) successfully |
400 Bad Request |
Missing or invalid fields |
409 Conflict |
A user with that email already exists |
Log in with existing credentials.
Request body:
{
"email": "user@example.com",
"password": "yourpassword"
}Responses:
| Status | Meaning |
|---|---|
200 OK |
Logged in, access_token cookie set |
400 Bad Request |
Missing fields |
401 Unauthorized |
Invalid credentials |
All routes below require the
access_tokencookie set by/loginor/register.
Create a new task.
Request body: (no fields are required by validation)
{
"title": "Finish the report",
"due_date": "2026-08-20T00:00:00Z",
"status": "pending"
}
statusdefaults to"pending"if omitted.due_dateexpects RFC 3339 / ISO 8601 format.
Response: 201 Created with the created task object.
Fetch all tasks belonging to the authenticated user.
Response: 200 OK with an array of task objects.
Fetch a single task by its MongoDB _id.
Response: 200 OK with the task object, or 403 Forbidden if the task belongs to another user.
Update a task by its _id. All three fields (title, status, due_date) are always overwritten — this is a full replacement, not a partial patch. Omitting a field will reset it to its zero value (empty string / zero time).
Request body:
{
"title": "Updated title",
"due_date": "2026-08-20T00:00:00Z",
"status": "completed"
}Response: 200 OK on success, 403 Forbidden if not the owner.
Delete a task by its _id.
Response: 200 OK on success, 403 Forbidden if not the owner.
Success:
{ "message": "..." }Error:
{ "message": "error description" }Task object:
{
"id": "6a7d75fb200d495245743f79",
"title": "Finish the report",
"due_date": "2026-08-20T00:00:00Z",
"status": "pending"
}- Passwords are hashed with bcrypt before storage — plaintext passwords are never persisted.
- JWTs are stored in HTTP-only cookies to prevent JavaScript access (XSS mitigation).
- Each user can only read, update, or delete their own tasks; attempts to access another user's tasks return
403 Forbidden.