TaskFlow is a backend-focused task management API for projects and tasks. It supports registration, login, JWT-protected project/task workflows, owner checks, task assignment, pagination, and project stats.
This submission targets the Backend Engineer role, so there is no React app. Instead, the repo includes a Postman collection at docs/taskflow.postman_collection.json.
Tech stack:
- Go 1.23
- PostgreSQL 16
database/sqlwithlib/pqgolang-migratefor versioned migrations- bcrypt with cost 12 or higher
- JWT access tokens with 24-hour expiry
- Docker Compose for local infrastructure
slogJSON logs
Core capabilities:
- Register and log in users
- Create, list, update, and delete projects
- Create, list, update, and delete tasks inside projects
- Assign tasks to any existing user
- Filter tasks by status and assignee
- Return project-level task stats
The backend uses a small layered structure:
cmd/apiowns process startup, logging, migration/seed execution, and graceful shutdown.internal/httpapiowns routing, validation, JSON responses, auth middleware, and authorization decisions.internal/storeowns SQL queries and maps PostgreSQL rows into API models.internal/authowns JWT creation and parsing.internal/databaseowns Postgres connectivity, migrations, and seed data.
I used the standard library HTTP router instead of adding a framework. The routing surface is small, so explicit path dispatch keeps the behavior easy to inspect in a review call.
The schema adds creator_id to tasks. The assignment requires deleting tasks only when the caller is the project owner or task creator, and that rule needs durable data.
Authorization rules:
- Any authenticated user can create a project.
- A project is visible to its owner, task creators, and assignees.
- Only the project owner can update or delete a project.
- Task updates are allowed for the project owner, task creator, or current assignee.
- Task deletion is allowed only for the project owner or task creator.
Tradeoffs:
- There is no separate project membership table. Assignment is task-based, which matches the constrained scope but would be too thin for a larger product.
- Pagination is intentionally simple:
pageandlimit, with a max limit of 100 and no total count. - The API uses access tokens only. Refresh tokens, token revocation, and password reset flows are intentionally out of scope.
- Automated integration tests are not included in this pass; the Postman collection covers reviewer verification. With more time, I would add container-backed integration tests around auth and task authorization.
Assume the reviewer has Docker and nothing else installed.
git clone https://github.com/31adityakumar/taskflow-adityakumar
cd taskflow-adityakumar
cp .env.example .env
docker compose up --buildAPI available at:
http://localhost:8080
Health check:
curl http://localhost:8080/healthzLogin with the seed user:
curl -X POST http://localhost:8080/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","password":"password123"}'Migrations run automatically when the API container starts.
The migration files live in:
backend/db/migrations
Every migration has both an up and down file. The API uses golang-migrate internally and starts with:
RUN_MIGRATIONS=true
MIGRATIONS_PATH=/migrationsSeed data also runs automatically by default:
SEED_ON_START=trueThe equivalent seed SQL is included at:
backend/db/seed.sql
To disable automatic seeding, set SEED_ON_START=false in .env.
Use these credentials immediately after docker compose up --build:
Email: test@example.com
Password: password123
Seed data includes:
- 1 user
- 1 project
- 3 tasks with
todo,in_progress, anddonestatuses
Import the Postman collection:
docs/taskflow.postman_collection.json
All protected endpoints require:
Authorization: Bearer <token>All responses use JSON. Validation errors use:
{
"error": "validation failed",
"fields": {
"email": "is required"
}
}Common errors:
{ "error": "unauthorized" }{ "error": "forbidden" }{ "error": "not found" }POST /auth/register
Request:
{
"name": "Jane Doe",
"email": "jane@example.com",
"password": "secret123"
}Response 201:
{
"token": "<jwt>",
"user": {
"id": "uuid",
"name": "Jane Doe",
"email": "jane@example.com",
"created_at": "2026-04-14T10:00:00Z"
}
}POST /auth/login
Request:
{
"email": "test@example.com",
"password": "password123"
}Response 200:
{
"token": "<jwt>",
"user": {
"id": "uuid",
"name": "Test User",
"email": "test@example.com",
"created_at": "2026-04-14T10:00:00Z"
}
}GET /projects?page=1&limit=20
Response 200:
{
"projects": [
{
"id": "uuid",
"name": "Greening India Launch",
"description": "Seed project for reviewer testing",
"owner_id": "uuid",
"created_at": "2026-04-14T10:00:00Z"
}
],
"meta": {
"page": 1,
"limit": 20
}
}POST /projects
Request:
{
"name": "Restaurant Ops Board",
"description": "Track launch readiness"
}Response 201: created project object.
GET /projects/:id
Response 200:
{
"id": "uuid",
"name": "Greening India Launch",
"description": "Seed project for reviewer testing",
"owner_id": "uuid",
"created_at": "2026-04-14T10:00:00Z",
"tasks": []
}PATCH /projects/:id
Request:
{
"name": "Updated Name",
"description": "Updated description"
}Response 200: updated project object.
DELETE /projects/:id
Response 204.
GET /projects/:id/stats
Response 200:
{
"by_status": {
"todo": 1,
"in_progress": 1,
"done": 1
},
"by_assignee": [
{
"assignee_id": "uuid",
"name": "Test User",
"email": "test@example.com",
"count": 3
}
]
}GET /projects/:id/tasks?status=todo&assignee=uuid&page=1&limit=20
Response 200:
{
"tasks": [],
"meta": {
"page": 1,
"limit": 20
}
}POST /projects/:id/tasks
Request:
{
"title": "Prepare demo script",
"description": "Keep the backend flow crisp",
"priority": "high",
"assignee_id": "uuid",
"due_date": "2026-04-25"
}Response 201: created task object.
PATCH /tasks/:id
Request:
{
"title": "Updated title",
"status": "done",
"priority": "low",
"assignee_id": null,
"due_date": "2026-04-30"
}Response 200: updated task object.
DELETE /tasks/:id
Response 204.
- Add container-backed integration tests for auth, task assignment, and authorization edge cases.
- Add a project membership model so access does not depend only on task assignment.
- Add total counts to paginated responses.
- Add refresh tokens and token revocation for a more complete auth story.
- Add CI that runs
go test, builds the Docker image, and validates migrations from a clean database. - Add audit fields for task status transitions if this became a real team workflow product.