Skip to content

cybertronprime/taskflow_task_app

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TaskFlow

REST API for a task management system. Register, log in, create projects, add tasks, assign them — the usual workflow but built properly with auth, ownership checks, and clean error handling.

Stack: Go 1.22 · PostgreSQL 16 · chi router · pgx · JWT (golang-jwt) · bcrypt · Docker

Project Structure

backend/
├── cmd/server/          # Entrypoint
├── internal/
│   ├── config/          # Env-based config
│   ├── database/        # Postgres connection (retry on startup)
│   ├── handler/         # HTTP handlers
│   ├── middleware/      # JWT auth
│   ├── model/           # Domain types
│   ├── repository/      # SQL queries (pgx, no ORM)
│   ├── router/          # Routes (chi)
│   └── service/         # Business logic
├── migrations/          # Up + down SQL migrations
├── seed/                # Seed data
├── tests/               # Integration tests
├── Dockerfile           # Multi-stage build
└── postman_collection.json

Architecture Decisions

The code follows a straightforward layered pattern: Handler → Service → Repository → DB.

  • Handlers deal with HTTP: parsing requests, writing responses, returning the right status codes.
  • Services hold the business rules — things like "only the project owner can delete" or "default status is todo."
  • Repositories own all the SQL. Nothing else touches the database.

Why chi? It's a thin router that plays nicely with net/http. Middleware chaining, URL params, route groups — it has what I need without pulling in a framework.

Why pgx over an ORM? I needed dynamic WHERE/SET clauses for task filtering and partial updates. Writing SQL directly felt cleaner than fighting an ORM for these cases.

Why creator_id on tasks? The spec says only the task creator or project owner can delete a task, so I needed to track who created it. The spec allows adding fields.

What I chose not to do:

  • No repository interfaces — concrete types are easier to follow at this scale. I'd add interfaces if the project grew or needed mocked tests.
  • No request ID in logs — chi generates one, but I didn't thread it into slog. Would fix in a real service.
  • Integration tests live in one file. I'd split by domain in a bigger project.

Running Locally

git clone https://github.com/cybertronprime/zomato_task_app
cd zomato_task_app
cp .env.example .env
docker compose up

API is at http://localhost:8080. Migrations and seed data run automatically on startup.

Running Migrations

Handled automatically by the entrypoint script — no manual steps.

If you ever need to run them by hand:

brew install golang-migrate

# up
migrate -path backend/migrations \
  -database "postgres://taskflow:taskflow_secret@localhost:5432/taskflow?sslmode=disable" up

# down
migrate -path backend/migrations \
  -database "postgres://taskflow:taskflow_secret@localhost:5432/taskflow?sslmode=disable" down

Test Credentials

Seeded on startup:

Email:    test@example.com
Password: password123

API Reference

Auth

Method Endpoint Description
POST /auth/register Register (name, email, password)
POST /auth/login Returns JWT
// POST /auth/register
{ "name": "Jane Doe", "email": "jane@example.com", "password": "secret123" }
// → 201 { "token": "...", "user": { "id": "...", "name": "Jane Doe", "email": "jane@example.com" } }

// POST /auth/login
{ "email": "jane@example.com", "password": "secret123" }
// → 200 { "token": "...", "user": { ... } }

Projects (requires Authorization: Bearer <token>)

Method Endpoint Description
GET /projects List projects (paginated: ?page=&limit=)
POST /projects Create project
GET /projects/:id Project + its tasks
PATCH /projects/:id Update name/description (owner only)
DELETE /projects/:id Delete project + tasks (owner only)
GET /projects/:id/stats Task counts by status and assignee

Tasks (requires Authorization: Bearer <token>)

Method Endpoint Description
GET /projects/:id/tasks List tasks (?status=&assignee=&page=&limit=)
POST /projects/:id/tasks Create task
PATCH /tasks/:id Update fields (all optional)
DELETE /tasks/:id Delete (project owner or task creator)

Errors

// 400
{ "error": "validation failed", "fields": { "email": "is required" } }

// 401
{ "error": "unauthorized" }

// 403
{ "error": "forbidden" }

// 404
{ "error": "not found" }

A Postman collection is included at backend/postman_collection.json.

Running Tests

docker compose run --rm test

This spins up a container with the Go toolchain, creates a test database, runs migrations, and executes the integration tests. No local Go install needed.

What I'd Do With More Time

  • More tests. The integration tests cover happy paths but I'd want unit tests per service with mocked repos, plus edge cases like concurrent updates.
  • Transactions. Deleting a project relies on CASCADE. I'd wrap multi-step operations in explicit transactions for safety.
  • Validation library. The manual field checking in handlers is repetitive — something like go-playground/validator would clean it up.
  • Rate limiting on auth endpoints to prevent brute-force login attempts.
  • CI pipeline. GitHub Actions for lint + test + build on every push.

About

TaskFlow — a minimal but real task management system.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors