Workspace Notes — a multi-tenant, production-oriented SaaS for workspace notes:
multi-workspace, multi-company system built with AdonisJS 6 (backend), MySQL, and React (Vite + Tailwind) (frontend).
Designed for performance (500k+ notes), security, and maintainability.
- What it is
- Features
- Architecture & File structure
- Database design (summary)
- Environment variables (.env example)
- Backend: setup & common commands
- Frontend: setup & common commands
- Running migrations & seeder (large dataset)
- Note history (7-day retention) — implementation & cleanup
- API reference & examples (curl)
- Security, performance & scaling notes
- Troubleshooting / FAQs
- Development tips / tests / CI
- Contributing & License
A multi-tenant notes system where:
- A Company can have many Workspaces.
- Each workspace holds many Notes.
- Notes have tags, draft/published status, public/private type, votes, and history.
- Hostname (or header) identifies the tenant (company).
- Focus on fast queries, batch seeding, and safe history retention.
- Multi-tenant (hostname-based)
- Workspaces per company
- Notes: title, content, tags (many-to-many), type (public/private), status (draft/published)
- Note votes (upvote/downvote) with counters
- Note history on update (7-day retention, restorable)
- Large-data seeder (1,000 workspaces / ~500k notes)
- React frontend (Vite), Tailwind CSS
- Secure user flow with hashed passwords and auth middleware
This project is built using a decoupled Backend (AdonisJS) and Frontend (React) architecture. It utilizes a feature-based organization to ensure scalability and maintainability.
The backend follows the Controller-Service-Validator pattern to separate concerns.
- app/Controllers/Http/: Organized by domain modules.
companies/,users/,workspaces/,notes/: Each contains its own controller, service, validator, and route definitions.
- Middleware/TenantMiddleware.ts: Handles multi-tenancy logic to scope requests.
- Models/: Lucid ORM models defining database schemas and relationships (Notes, Tags, History, etc.).
- ace-commands/: Custom CLI commands, including
NoteHistoryCleanup.tsfor automated maintenance. - start/: Application entry points for global routes and kernel configuration.
The frontend is a TypeScript-based React application powered by Vite and Tailwind CSS.
- api/: Centralized Axios instances and API service wrappers.
- app/: Core application setup including the router and main entry points.
- context/: Global state management for Authentication, Company, and Workspace contexts.
- hooks/: Custom React hooks for reusable logic (e.g.,
useAuth,useWorkspace). - utils/tenant.ts: Utility functions for handling multi-tenant identifiers.
- Components/Views: Feature-specific UI folders:
auth/: Login and Registration.company/: Organization management.workspace/: Workspace navigation and creation.notes/: Core Note-taking features including the editor and list views.
backend/
├── app/
│ ├── Controllers/Http/
│ │ ├── companies/ (controller, service, validator, routes)
│ │ ├── users/ (controller, service, validator, routes)
│ │ ├── workspaces/ (controller, service, validator, routes)
│ │ └── notes/ (controller, service, validator, routes)
│ ├── Middleware/
│ │ └── TenantMiddleware.ts
│ └── Models/
│ └── (company.ts, user.ts, workspace.ts, note.ts, etc.)
├── database/
│ ├── migrations/
│ └── seeders/
├── ace-commands/
│ └── NoteHistoryCleanup.ts
└── start/
├── routes.ts
└── kernel.ts
frontend/
├── src/
│ ├── api/ (axios.ts, auth.api.ts, workspace.api.ts, note.api.ts)
│ ├── app/ (App.tsx, router.tsx)
│ ├── auth/ (Login, Register)
│ ├── company/ (CreateCompany)
│ ├── workspace/ (WorkspaceList, CreateWorkspace)
│ ├── notes/ (NoteList, NoteCreate, NoteEditor, NoteView)
│ ├── context/ (AuthContext, CompanyContext, WorkspaceContext)
│ ├── hooks/ (useAuth.ts, useWorkspace.ts)
│ └── utils/ (tenant.ts)
├── tailwind.config.js
└── vite.config.ts
---
Key tables and columns (simplified):
companies(id, name, hostname, creator_id, created_at, updated_at)users(id, company_id, username, email, password, role[owner|member], created_at, updated_at)workspaces(id, company_id, name, created_at, updated_at)notes(id, workspace_id, author_id, title, content, type, status, upvotes_count, downvotes_count, published_at, created_at, updated_at)tags(id, name, hostname, created_at, updated_at)note_tags(id, note_id, tag_id, created_at)note_history(id, note_id, user_id, title, content, created_at)note_votes(id, note_id, user_id, vote_type, created_at, updated_at)
Important indexes:
notes(title)index for fast title search.note_history(created_at)for cleanup queries.- Foreign keys for referential integrity (pay attention to creation order to avoid circular FK issues).
Create backend/.env:
APP_KEY=some_random_string
HOST=0.0.0.0
PORT=3333
NODE_ENV=development
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=root
DB_PASSWORD=your_password
DB_NAME=workspace_notes
SESSION_DRIVER=cookie
SESSION_SECRET=another_random_secret
HASH_DRIVER=scrypt```
## Frontend .env (Vite):
VITE_API_BASE=http://localhost:3333
VITE_TENANT_HOST=localhost