The main GROWI wiki application - a full-stack Next.js application with Express.js backend and MongoDB database.
| Layer | Technology |
|---|---|
| Frontend | Next.js 14 (Pages Router), React 18 |
| Backend | Express.js with custom server |
| Database | MongoDB with Mongoose ^6.13.6 |
| State | Jotai (UI state) + SWR (server state) |
| API | RESTful API v3 with OpenAPI specs |
| Testing | Vitest, React Testing Library |
# Development
pnpm run dev # Start dev server (or turbo run dev from root)
pnpm run dev:migrate # Run database migrations
# Quality Checks
pnpm run lint:typecheck # TypeScript type check
pnpm run lint:biome # Biome linter
pnpm run test # Run tests
# Build
pnpm run build # Build for production
# Run Specific Tests
pnpm vitest run yjs.integ # Use partial file name
pnpm vitest run helper.spec # Vitest auto-finds matching files
src/
├── pages/ # Next.js Pages Router (*.page.tsx)
├── features/ # Feature modules (recommended for new code)
│ └── {feature-name}/
│ ├── server/ # Server-side (models, routes, services)
│ └── client/ # Client-side (components, hooks, states)
├── server/ # Express server (legacy structure)
│ ├── models/ # Mongoose models
│ ├── routes/apiv3/ # API v3 routes
│ └── services/ # Business logic
├── components/ # React components (legacy)
├── states/ # Jotai atoms
└── stores-universal/ # SWR hooks
Create new features in features/{feature-name}/:
features/my-feature/
├── index.ts # Public exports
├── interfaces/ # TypeScript types
├── server/
│ ├── models/ # Mongoose models
│ ├── routes/ # Express routes
│ └── services/ # Business logic
└── client/
├── components/ # React components
├── hooks/ # Custom hooks
└── states/ # Jotai atoms
SWR: Server data (pages, users, API responses)
// Jotai for UI state
import { atom } from 'jotai';
export const isModalOpenAtom = atom(false);
// SWR for server data
import useSWR from 'swr';
export const usePageById = (id: string) => useSWR(`/api/v3/pages/${id}`);
Use .page.tsx suffix and getLayout pattern:
// pages/admin/index.page.tsx
import type { NextPageWithLayout } from '~/interfaces/next-page';
const AdminPage: NextPageWithLayout = () => <AdminDashboard />;
AdminPage.getLayout = (page) => <AdminLayout>{page}</AdminLayout>;
export default AdminPage;
Add routes to server/routes/apiv3/ with OpenAPI docs:
/**
* @openapi
* /api/v3/pages/{id}:
* get:
* summary: Get page by ID
*/
router.get('/pages/:id', async (req, res) => {
const page = await PageService.findById(req.params.id);
res.json(page);
});
Use ~/ for absolute imports:
import { PageService } from '~/server/services/PageService';
import { Button } from '~/components/Button';
pnpm run lint:typecheck # 1. Type check
pnpm run lint:biome # 2. Lint
pnpm run test # 3. Run tests
pnpm run build # 4. Verify build (optional)
| Feature | Directory | Description |
|---|---|---|
| Page Tree | features/page-tree/ |
Hierarchical page navigation |
| OpenAI | features/openai/ |
AI assistant integration |
| Search | features/search/ |
Elasticsearch full-text search |
| Plugins | features/growi-plugin/ |
Plugin system |
| OpenTelemetry | features/opentelemetry/ |
Monitoring/telemetry |
When working in this directory, these skills are automatically loaded:
Plus all global skills (monorepo-overview, tech-stack).
For detailed patterns and examples, refer to the Skills in .claude/skills/.
The following rules in .claude/rules/ are always applied when working in this directory:
| Rule | Description |
|---|---|
| package-dependencies | Turbopack dependency classification — when to use dependencies vs devDependencies, verification procedure |