Next.js Dashboard with NestJS API Template
Build a full-stack dashboard application with Next.js 14 frontend connected to a separate NestJS backend API, featuring cookie-based JWT authentication, role-based access control, and real-time updates.
Architecture Overview
This template implements a **Next.js 14+ frontend** with App Router that connects to a separate NestJS backend API. The architecture features cookie-based JWT authentication with automatic token refresh, granular role-based access control, and real-time WebSocket integration.
Core Architecture Patterns
**Authentication Flow**: JWT tokens stored in HTTP-only cookies, middleware handles authentication on `/dashboard/*` routes, automatic token refresh via Axios interceptors**Permission System**: Granular permissions (create/read/update/delete) per resource (users/roles/settings), enforced via `useCanAccess` hook in components**API Integration**: Single Axios instance (`src/configs/api.ts`) with request queuing during token refresh to prevent race conditions**State Management**: React Query for server state management, React Context for user session and permissionsDevelopment Instructions
When building features or fixing bugs in this codebase, follow these patterns:
1. Authentication & Authorization
**User Context**: Retrieved from middleware header `x-authenticated-user` → `getAuthenticatedUser()` → injected into dashboard layout**Permission Checks**: Always use `useCanAccess(EPermission.USERS, EPermissionType.READ)` hook pattern for component-level authorization**Token Management**: Never manually manage JWT tokens - Axios interceptors automatically handle 401 responses and token refresh**Route Protection**: All `/dashboard/*` routes are automatically protected by middleware - no manual checks neededExample permission check:
```typescript
const canEditUsers = useCanAccess(EPermission.USERS, EPermissionType.UPDATE);
if (!canEditUsers) {
return <AccessDenied />;
}
```
2. API Service Layer Pattern
All API services follow a consistent structure under `src/services/api/{resource}/`:
```
src/services/api/{resource}/
├── use-get-all-{resource}.ts # Paginated list query
├── use-get-{resource}.ts # Single item query
├── use-add-{resource}.ts # Create mutation
├── use-update-{resource}.ts # Update mutation
└── use-delete-{resource}.ts # Delete mutation
```
**Standard service implementation:**
```typescript
// use-get-all-users.ts
export const useGetAllUsers = (params: PaginationParams) => {
return useQuery({
queryKey: ["users", "list", params],
queryFn: async () => {
const url = createPaginationUrl("/users", params);
const { data } = await api.get<ApiResponse<User[]>>(url);
return data;
},
});
};
```
3. Data Table Implementation
Use the standardized data table pattern for all paginated lists:
```typescript
const { data } = useGetAllUsers(paginationParams);
const { sorting, pagination, columnFilters, onSortingChange, onPaginationChange } = useDataTable();
<DataTable
columns={columns}
data={data?.data || []}
pagination={pagination}
sorting={sorting}
onPaginationChange={onPaginationChange}
onSortingChange={onSortingChange}
/>
```
4. File Organization
**Routes**: Use App Router with route groups - `(auth)` for login/register pages**Components**: - `components/ui/` - Radix UI primitives with Tailwind styling (Button, Dialog, etc.)
- `components/custom/` - Reusable project components (DataTable, StepMenu)
- `components/pages/{route}/` - Page-specific components and forms
**Services**: Domain-based organization (`auth/`, `users/`, `roles/`) with consistent naming conventions**Utils**: Pure functions in `src/utils/` (permissions, formatting, validation)5. API Integration Best Practices
**Always import**: `api` from `@/configs/api` - never use native `fetch()` directly**React Query patterns**: Consistent query key structure: `["resource", "action", params]`**Pagination helpers**: Use `createPaginationUrl()` and `createPaginationQueryKey()` utilities**Response typing**: All API responses wrapped in `ApiResponse<T>` type for consistency**Error handling**: Axios interceptors handle global errors - use mutation callbacks for specific error handling6. WebSocket Integration
**Hook usage**: Import `useSocket()` from `@/services/socket/use-socket`**Auto-connection**: Socket automatically connects when user is authenticated**Event pattern**: Use `listenToEvent(eventName, callback)` and `sendEvent(eventName, data)` methods**Cleanup**: Event listeners automatically cleaned up on component unmountExample:
```typescript
const { listenToEvent, sendEvent } = useSocket();
useEffect(() => {
listenToEvent("notification", (data) => {
showNotification(data);
});
}, [listenToEvent]);
```
Environment Configuration
Required Variables
```env
NEXT_PUBLIC_API_URL="http://localhost:4000" # Backend API base URL
JWT_SECRET="your-jwt-secret" # For middleware token verification (must match backend)
APP_PREFIX="" # Optional path prefix for deployment
```
Development Commands
```bash
yarn dev # Start development server with Turbopack
yarn build # Production build
yarn lint # Run ESLint checks
yarn type-check # TypeScript type checking
```
Key Integration Points
Backend Communication
**Base URL**: Set via `process.env.NEXT_PUBLIC_API_URL`**Credentials**: All requests use `withCredentials: true` for cookie-based auth**Error Handling**: Axios interceptors automatically handle 401 responses and queue requests during token refresh**WebSocket**: Connects to same backend URL, authentication handled via existing cookiesExternal Services
**Payments**: Stripe integration via `use-create-checkout-session` hook**Real-time Updates**: Socket.io client for live notifications and data updates**UI Components**: Radix UI primitives styled with Tailwind CSSCritical Files for Understanding Patterns
Reference these files when implementing new features:
`src/configs/api.ts` - Core Axios instance with token refresh interceptors`src/middlewares/authentication.ts` - Route protection and automatic token refresh logic`src/components/providers/authentication.tsx` - User context provider and permission management`src/utils/isAllowed.ts` - Permission checking logic and helper functions`src/components/custom/data-table/` - Reusable data table component with sorting, filtering, and paginationCommon Development Tasks
Adding a New Protected Resource
1. Create service layer in `src/services/api/{resource}/` following the standard pattern
2. Add permission enum values to `EPermission` and `EPermissionType`
3. Create page components in `src/app/(dashboard)/{resource}/`
4. Implement data table with pagination using `useDataTable` hook
5. Add permission checks using `useCanAccess` hook
6. Update backend NestJS API with corresponding endpoints
Implementing Real-time Features
1. Define event names in a constants file
2. Use `useSocket()` hook in components
3. Set up event listeners with `listenToEvent()`
4. Emit events with `sendEvent()` when needed
5. Handle cleanup automatically via hook lifecycle
Adding New UI Components
1. Check `components/ui/` for existing Radix primitives
2. Extend primitives in `components/custom/` if needed
3. Follow Tailwind class patterns from existing components
4. Use TypeScript for prop typing
5. Export from appropriate index file
Security Considerations
JWT tokens are HTTP-only cookies - never expose in client-side JavaScriptAll sensitive operations require permission checks via `useCanAccess`Middleware validates tokens on every protected route requestAxios interceptors prevent request leakage during token refreshWebSocket connections authenticated via existing session cookiesPerformance Optimization
React Query handles caching and background refetching automaticallyPagination implemented server-side for large datasetsWebSocket events debounced where appropriateTurbopack used for fast development buildsProduction builds optimized with Next.js 14+ features