Skip to content

Repository files navigation

DevFlow

A collaborative project management platform — organize projects, track work on a kanban board, and collaborate with your team in one place. Built with the MERN stack (MongoDB, Express, React/Next.js, Node.js).

NodeNext.jsMongoDBDockerLicense

Contents

Features

  • Auth — register, login, email verification, forgot/reset password, rotating refresh tokens
  • Workspaces — invite teammates by email, role-based permissions (owner/admin/member)
  • Projects — organize work, markdown notes, archive, live stats dashboard
  • Kanban board — drag-and-drop tasks across Todo / In Progress / Review / Done
  • Tasks — priority, due dates, labels, checklists, multiple assignees, file attachments, threaded comments with @mentions
  • Notifications — task assignments, comments, and mentions, delivered to a notifications inbox (backend emits these live over Socket.io — see the architecture note below)
  • Global search — full-text search across projects and tasks
  • Dark mode, responsive layout throughout

Tech stack

LayerTechnology
FrontendNext.js 14 (App Router), TypeScript, Tailwind CSS, React Query, React Hook Form, Zod, Framer Motion, @dnd-kit
BackendNode.js, Express, MongoDB, Mongoose
AuthJWT access + rotating refresh tokens, email verification, password reset
RealtimeSocket.io (backend)
StorageCloudinary (avatars, task attachments)
EmailResend (transactional email API)
DocsSwagger / OpenAPI
TestingJest, Supertest, mongodb-memory-server
ToolingDocker Compose, GitHub Actions CI, ESLint, Prettier

Quick start (Docker — recommended)

The fastest way to get the whole stack running.

Requirements: Docker Desktop (or Docker Engine + Compose).

git clone <your-fork-url> devflow
cd devflow
docker compose up --build

Then open:

The compose file spins up MongoDB, the backend, and the frontend together, with sensible defaults baked in — no .env file is required to get it running. Without a RESEND_API_KEY configured, email sending falls back to console logging, so registration and invite flows still work end-to-end; you'll just read the verification/reset links from the backend container logs instead of an inbox:

docker compose logs -f backend

To load realistic demo data (a workspace, a project, and several tasks) once the containers are up:

docker compose exec backend npm run seed

This creates three demo accounts, all using the password password123:

EmailRole
ada@devflow-demo.testOwner
grace@devflow-demo.testAdmin
alan@devflow-demo.testMember

To stop everything: docker compose down (add -v to also delete the MongoDB volume).

Manual setup (without Docker)

Requirements: Node.js 20+, MongoDB 7+ (local install or MongoDB Atlas).

1. Backend

cd backend
cp .env.example .env
# edit .env - at minimum set MONGO_URI, JWT_ACCESS_SECRET, JWT_REFRESH_SECRET
npm install
npm run dev

The API starts on http://localhost:5000. Generate strong secrets for the two JWT values, for example:

node -e "console.log(require('crypto').randomBytes(48).toString('hex'))"

Optionally load demo data: npm run seed.

2. Frontend

In a second terminal:

cd frontend
cp .env.example .env.local
# NEXT_PUBLIC_API_URL defaults to http://localhost:5000/api/v1, which matches the# backend above - only change it if your backend runs somewhere else
npm install
npm run dev

The app starts on http://localhost:3000.

Running tests

cd backend
npm test

Uses mongodb-memory-server, so no separate database is needed — it downloads and runs a temporary MongoDB instance automatically (requires normal internet access, since it fetches the MongoDB binary the first time you run it).

Environment variables

Backend (backend/.env)

VariableDescriptionRequired
MONGO_URIMongoDB connection stringYes
JWT_ACCESS_SECRET / JWT_REFRESH_SECRETLong random strings signing the two token typesYes
CLIENT_URLFrontend origin, used for CORS and links in emailsYes (defaults to http://localhost:3000)
PORTBackend portNo (default 5000)
RESEND_API_KEYTransactional email (verification, reset, invites)No — without it, emails are logged to the console instead of sent¹
CLOUDINARY_CLOUD_NAME / CLOUDINARY_API_KEY / CLOUDINARY_API_SECRETFile uploadsNo — required only for avatar/attachment uploads to work
RATE_LIMIT_WINDOW_MS / RATE_LIMIT_MAXAPI rate limitingNo

¹ Variable name shown as RESEND_API_KEY per Resend's standard convention — confirm this matches whatever your email.service.js actually reads before deploying.

See backend/.env.example for the full list with defaults.

Frontend (frontend/.env.local)

VariableDescription
NEXT_PUBLIC_API_URLBase URL of the backend API, e.g. http://localhost:5000/api/v1

Architecture overview

flowchart LR
FE["Next.js<br/>(App Router)"]
API["Express REST API<br/>(Node.js)"]
WS["Socket.io"]
DB[("MongoDB")]
CDN["Cloudinary"]
MAIL["Resend<br/>(email)"]
FE -- "HTTPS / REST + JWT" --> API
API -- Mongoose --> DB
API --> CDN
API --> MAIL
API -. events .-> WS
WS -. "planned: live push" .-> FE
Loading

Backend follows a layered architecture: routes (HTTP + validation) → controllers (thin, request/response only) → services (business logic) → models (Mongoose schemas). Every response follows a consistent shape ({ success, message, data } / { success: false, message, errors? }), produced by a single global error handler. See CONTINUE.md for the full set of conventions established while building this out (auth strategy, permission middleware chain, kanban position algorithm, notification triggers, etc.) if you want the detailed reasoning behind specific decisions.

Frontend uses the Next.js App Router with route groups: (auth) for the unauthenticated split-screen auth pages, (app) for the authenticated shell (sidebar + topbar), guarded by a client-side redirect that also preserves the original destination (so an invite-email link survives a login redirect). Server state lives in React Query; the only client state kept outside it is the in-memory access token and the kanban board's local drag state.

Note on real-time notifications: the backend fully implements Socket.io (JWT-authenticated connections, private per-user rooms, events emitted on task assignment/comments/mentions — see backend/src/config/socket.js), but the frontend doesn't yet open a socket connection to consume it; the notifications inbox currently works via a 30-second React Query poll instead, which is a fine fallback but means notifications aren't instant. Wiring up socket.io-client (already a frontend dependency) in a small hook that invalidates the notifications query on incoming events would close this gap — see Known limitations.

ER diagram

erDiagram
USER ||--o{ REFRESH_TOKEN : has
USER ||--o{ WORKSPACE_MEMBER : "is member via"
WORKSPACE ||--o{ WORKSPACE_MEMBER : has
WORKSPACE ||--o{ INVITATION : has
WORKSPACE ||--o{ PROJECT : contains
PROJECT ||--o{ TASK : contains
TASK ||--o{ COMMENT : has
TASK ||--o{ ATTACHMENT : has
TASK }o--o{ USER : "assigned to"
USER ||--o{ NOTIFICATION : receives
USER {
ObjectId _id
string name
string email
string password
string avatarUrl
boolean isEmailVerified
}
WORKSPACE {
ObjectId _id
string name
string slug
ObjectId owner
}
PROJECT {
ObjectId _id
ObjectId workspace
string name
string key
string status
number taskCounter
}
TASK {
ObjectId _id
ObjectId project
number number
string title
string status
string priority
number position
date dueDate
}
COMMENT {
ObjectId _id
ObjectId task
ObjectId author
string content
}
ATTACHMENT {
ObjectId _id
ObjectId task
string url
string publicId
}
NOTIFICATION {
ObjectId _id
ObjectId recipient
string type
boolean isRead
}
Loading

API documentation

Full interactive API docs (Swagger UI) are served by the running backend at /api/v1/docs (e.g. http://localhost:5000/api/v1/docs). Every route is documented with its request/response shape directly from the source (@openapi JSDoc blocks above each route definition in backend/src/routes/).

Folder structure

devflow/
├── backend/
│ ├── src/
│ │ ├── app.js, server.js # Express app + entry point
│ │ ├── config/ # env, db, cloudinary, socket, swagger
│ │ ├── models/ # Mongoose schemas
│ │ ├── services/ # business logic
│ │ ├── controllers/ # HTTP request/response handlers
│ │ ├── routes/ # route definitions + API docs
│ │ ├── middleware/ # auth, permissions, validation, errors
│ │ ├── validators/ # express-validator chains
│ │ ├── utils/ # ApiError, ApiResponse, helpers
│ │ └── scripts/seed.js # demo data
│ └── tests/ # Jest unit + integration tests
├── frontend/
│ └── src/
│ ├── app/ # Next.js App Router pages
│ │ ├── (auth)/ # login, register, password reset, etc.
│ │ └── (app)/ # authenticated shell: dashboard, workspaces,
│ │ # projects (kanban board), settings
│ ├── components/ # ui/ (primitives), kanban/, app/ (shell), etc.
│ ├── hooks/ # React Query hooks per resource
│ ├── lib/ # api clients, validation schemas, utils
│ └── context/ # auth context
├── docker-compose.yml
└── CONTINUE.md # full build log / architectural decision record

Deployment guide

Backend → Render or Railway

  1. Push this repo to GitHub.
  2. Create a new Web Service, point it at the backend/ directory as the root.
  3. Build command: npm install. Start command: npm start.
  4. Set the environment variables from the table above (use a MongoDB Atlas connection string for MONGO_URI in production).
  5. Set CLIENT_URL to your deployed frontend's URL once you have it (step below).

Database → MongoDB Atlas

  1. Create a free cluster at mongodb.com/atlas.
  2. Add a database user and allow network access from your backend host (or 0.0.0.0/0 for simplicity while testing).
  3. Copy the connection string into MONGO_URI.

Frontend → Vercel

  1. Import the repo into vercel.com, set the root directory to frontend/.
  2. Set NEXT_PUBLIC_API_URL to your deployed backend's API base URL (e.g. https://your-backend.onrender.com/api/v1).
  3. Deploy. Then go back to your backend's environment variables and set CLIENT_URL to this Vercel URL, so CORS and email links point to the right place.

File uploads → Cloudinary

Create a free account at cloudinary.com, grab your cloud name, API key, and API secret from the dashboard, and set the three CLOUDINARY_* backend environment variables.

Email → Resend

Create a free account at resend.com, generate an API key, and set RESEND_API_KEY in the backend environment. Verify a sending domain in the Resend dashboard before going to production, or use their shared test domain during development.

Testing

cd backend
npm test# run once
npm run test:watch

Tests use mongodb-memory-server for a real (in-memory, ephemeral) MongoDB instance, so they exercise actual database behavior rather than mocks — integration tests cover the full auth flow, workspace permissions, kanban task ordering, and file uploads (with Cloudinary itself mocked, since that's a genuine external network dependency).

Known limitations

A few trade-offs were made deliberately to keep this a realistic, maintainable portfolio project rather than an over-engineered one — worth knowing about, and reasonable things to mention if this comes up in an interview:

  • Deleting a workspace does not cascade-delete its projects/tasks/comments/attachments (only project deletion cascades to its tasks). A production system would handle this with a background cleanup job or a transaction.
  • Comment "edit" support exists in the API and UI, but there's no edit history — an edited comment just shows an "(edited)" marker.
  • The backend's Socket.io layer is fully built (see Architecture overview), but the frontend consumes notifications via polling rather than a live socket connection — a good next step, and a natural thing to discuss if asked "what would you improve next?"

License

MIT

About

Modern collaborative project management platform featuring Kanban boards, JWT authentication, workspaces, file uploads, and real-time notifications.

Topics

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages