Documentation Index
Fetch the complete documentation index at: https://mintlify.com/OWASP/Nest/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The OWASP Nest frontend is built with Next.js 16, React 19, and TypeScript, using the App Router for server-side rendering and modern React features.
Tech Stack
Next.js 16
React framework with App Router
TypeScript
Type-safe JavaScript
Tailwind CSS 4
Utility-first CSS
Apollo Client
GraphQL client
HeroUI
React component library
Project Structure
frontend/
├── __tests__/ # Test files
│ ├── a11y/ # Accessibility tests
│ ├── e2e/ # End-to-end tests
│ ├── unit/ # Unit tests
│ └── mockData/ # Test fixtures
├── public/ # Static assets
├── src/
│ ├── app/ # Next.js 16 App Router
│ │ ├── about/ # About pages
│ │ ├── api/ # API routes
│ │ ├── auth/ # Authentication pages
│ │ ├── chapters/ # Chapter pages
│ │ ├── committees/ # Committee pages
│ │ ├── community/ # Community pages
│ │ ├── projects/ # Project pages
│ │ ├── layout.tsx # Root layout
│ │ └── page.tsx # Home page
│ ├── components/ # React components
│ │ ├── ui/ # Reusable UI components
│ │ ├── icons/ # Icon components
│ │ └── ... # Feature components
│ ├── contexts/ # React contexts
│ ├── hooks/ # Custom React hooks
│ ├── server/ # Server-side utilities
│ ├── types/ # TypeScript types
│ ├── utils/ # Client utilities
│ └── wrappers/ # Component wrappers
├── .env # Environment variables
├── jest.config.ts # Jest configuration
├── next.config.js # Next.js configuration
├── package.json # Dependencies
├── playwright.config.ts # Playwright configuration
├── tailwind.config.js # Tailwind configuration
└── tsconfig.json # TypeScript configuration
Development Setup
Prerequisites
Ensure you have completed the local development setup first.
Dependencies
Dependencies are managed with pnpm and defined in package.json:
Core
UI
Data Fetching
Dev Tools
{
"dependencies": {
"next": "^16.1.6",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"typescript": "~5.9.3"
}
}
{
"dependencies": {
"@heroui/react": "^2.8.9",
"tailwindcss": "^4.2.1",
"framer-motion": "^12.34.3",
"clsx": "^2.1.1",
"tailwind-merge": "^3.5.0"
}
}
{
"dependencies": {
"@apollo/client": "^4.1.6",
"graphql": "^16.13.0",
"next-auth": "^4.24.13"
}
}
{
"devDependencies": {
"@playwright/test": "^1.58.2",
"jest": "^30.2.0",
"eslint": "^9.39.3",
"prettier": "^3.8.1"
}
}
Update Dependencies
make update-frontend-dependencies
This runs pnpm update to update all frontend dependencies.
Common Commands
Development
Code Quality
GraphQL
Make Commands
# Runs on http://localhost:3000
pnpm run dev
# Check for issues
pnpm run lint:check
# Fix issues
pnpm run lint
This generates TypeScript types from GraphQL schema and queries.
Next.js App Router
File-Based Routing
Next.js uses file-based routing in the app/ directory:
app/
├── page.tsx # / (home)
├── layout.tsx # Root layout
├── about/
│ └── page.tsx # /about
├── projects/
│ ├── page.tsx # /projects
│ ├── [key]/
│ │ └── page.tsx # /projects/[key]
│ └── dashboard/
│ └── page.tsx # /projects/dashboard
└── api/
└── auth/
└── [...nextauth]/
└── route.ts # /api/auth/*
Layouts
Root Layout
Nested Layout
import { Inter } from 'next/font/google'
import './globals.css'
const inter = Inter({ subsets: ['latin'] })
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body className={inter.className}>
{children}
</body>
</html>
)
}
export default function ProjectsLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<div className="container mx-auto">
<nav>{/* Project navigation */}</nav>
{children}
</div>
)
}
Server vs Client Components
Server Component
Client Component
// No "use client" directive = Server Component
import { getProjects } from '@/server/projects'
export default async function ProjectsPage() {
const projects = await getProjects()
return (
<div>
<h1>Projects</h1>
{projects.map((project) => (
<div key={project.id}>{project.name}</div>
))}
</div>
)
}
Benefits:
- Zero client-side JavaScript
- SEO-friendly
- Direct database/API access
'use client'
import { useState } from 'react'
export function SearchBar() {
const [query, setQuery] = useState('')
return (
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
)
}
When to use:
- Interactive features
- useState, useEffect hooks
- Browser APIs
- Event handlers
Data Fetching
GraphQL with Apollo Client
Define Query
app/projects/queries.graphql
query GetProjects($first: Int) {
projects(first: $first) {
edges {
node {
id
name
description
url
}
}
}
}
Generate Types
Creates queries.generated.ts with TypeScript types. Use in Component
app/projects/ProjectList.tsx
'use client'
import { useQuery } from '@apollo/client'
import { GetProjectsDocument } from './queries.generated'
export function ProjectList() {
const { data, loading, error } = useQuery(GetProjectsDocument, {
variables: { first: 10 },
})
if (loading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return (
<div>
{data?.projects?.edges?.map(({ node }) => (
<div key={node.id}>{node.name}</div>
))}
</div>
)
}
REST API Fetch
Server Component
Client Component
async function getProjects() {
const res = await fetch('http://backend:8000/api/v0/projects/', {
cache: 'no-store', // or 'force-cache'
})
return res.json()
}
export default async function ProjectsPage() {
const { projects } = await getProjects()
return <div>{/* Render projects */}</div>
}
components/ProjectList.tsx
'use client'
import { useEffect, useState } from 'react'
export function ProjectList() {
const [projects, setProjects] = useState([])
useEffect(() => {
fetch('/api/v0/projects/')
.then((res) => res.json())
.then((data) => setProjects(data.projects))
}, [])
return <div>{/* Render projects */}</div>
}
Styling
Tailwind CSS
Utility Classes
Custom Classes
clsx & tw-merge
<div className="container mx-auto px-4">
<h1 className="text-4xl font-bold text-gray-900 dark:text-white">
OWASP Nest
</h1>
<p className="mt-4 text-lg text-gray-600 dark:text-gray-400">
Your gateway to OWASP
</p>
</div>
module.exports = {
theme: {
extend: {
colors: {
owasp: {
blue: '#1E40AF',
orange: '#F97316',
},
},
},
},
}
Usage:<div className="bg-owasp-blue text-white">
OWASP Blue
</div>
import { clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
function cn(...classes: string[]) {
return twMerge(clsx(classes))
}
// Usage
<div className={cn(
'px-4 py-2',
isActive && 'bg-blue-500',
'hover:bg-blue-600'
)}>
Button
</div>
HeroUI Components
import { Button, Card, CardBody, CardHeader } from '@heroui/react'
export function ProjectCard({ project }) {
return (
<Card>
<CardHeader>
<h3>{project.name}</h3>
</CardHeader>
<CardBody>
<p>{project.description}</p>
<Button color="primary">View Project</Button>
</CardBody>
</Card>
)
}
Authentication
NextAuth.js Setup
Configure Provider
app/api/auth/[...nextauth]/route.ts
import NextAuth from 'next-auth'
import GithubProvider from 'next-auth/providers/github'
const handler = NextAuth({
providers: [
GithubProvider({
clientId: process.env.GITHUB_ID!,
clientSecret: process.env.GITHUB_SECRET!,
}),
],
callbacks: {
async session({ session, token }) {
// Add custom session data
return session
},
},
})
export { handler as GET, handler as POST }
Session Provider
import { SessionProvider } from 'next-auth/react'
export default function RootLayout({ children }) {
return (
<html>
<body>
<SessionProvider>
{children}
</SessionProvider>
</body>
</html>
)
}
Use in Components
'use client'
import { useSession, signIn, signOut } from 'next-auth/react'
export function UserMenu() {
const { data: session } = useSession()
if (!session) {
return <button onClick={() => signIn('github')}>Sign In</button>
}
return (
<div>
<p>Welcome, {session.user?.name}</p>
<button onClick={() => signOut()}>Sign Out</button>
</div>
)
}
Custom Hooks
import { useQuery } from '@apollo/client'
import { GetProjectsDocument } from '@/app/projects/queries.generated'
export function useProjects(limit = 10) {
const { data, loading, error } = useQuery(GetProjectsDocument, {
variables: { first: limit },
})
return {
projects: data?.projects?.edges?.map(({ node }) => node) || [],
loading,
error,
}
}
Usage:
const { projects, loading } = useProjects()
TypeScript
Type Definitions
export interface Project {
id: string
name: string
description: string
level: 'Lab' | 'Production' | 'Flagship'
type: 'Code' | 'Documentation' | 'Tool'
url: string
leaders: string[]
}
GraphQL Generated Types
Generated from GraphQL schema:
Produces:
export type Project = {
__typename?: 'ProjectNode'
id: Scalars['ID']
name: Scalars['String']
description: Scalars['String']
// ...
}
Testing
See the Testing Guide for comprehensive frontend testing documentation.
Environment Variables
Key variables in frontend/.env:
# API URLs
NEXT_PUBLIC_API_URL=http://localhost:8000/
NEXT_PUBLIC_GRAPHQL_URL=http://localhost:8000/graphql/
NEXT_PUBLIC_CSRF_URL=http://localhost:8000/csrf/
# Environment
NEXT_PUBLIC_ENVIRONMENT=development
NEXT_PUBLIC_RELEASE_VERSION=1.0.0
# Auth
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=<secret>
GITHUB_ID=<github-oauth-id>
GITHUB_SECRET=<github-oauth-secret>
# Analytics (optional)
NEXT_PUBLIC_GTM_ID=GTM-XXXXXXX
NEXT_PUBLIC_SENTRY_DSN=<sentry-dsn>
# Search (optional)
NEXT_PUBLIC_IDX_URL=http://localhost:8000/idx/
Variables prefixed with NEXT_PUBLIC_ are exposed to the browser.
Image Optimization
import Image from 'next/image'
<Image
src="/logo.png"
alt="OWASP Nest"
width={200}
height={50}
priority // Load immediately
/>
Code Splitting
import dynamic from 'next/dynamic'
const HeavyComponent = dynamic(() => import('@/components/HeavyComponent'), {
loading: () => <div>Loading...</div>,
ssr: false, // Client-side only
})
Memoization
import { memo, useMemo } from 'react'
export const ProjectCard = memo(({ project }) => {
const formattedDate = useMemo(
() => new Date(project.createdAt).toLocaleDateString(),
[project.createdAt]
)
return <div>{formattedDate}</div>
})
Debugging
Install the React Developer Tools browser extension.
Next.js Debug Mode
NODE_OPTIONS='--inspect' pnpm run dev
Then attach debugger in Chrome DevTools or VS Code.
Console Logging
console.log('Debug:', data)
console.error('Error:', error)
Use Sentry for production errors:import * as Sentry from '@sentry/nextjs'
Sentry.captureException(error)
Build & Deployment
Production Build
Build
Creates optimized production build in .next/ directory. Analyze Bundle
ANALYZE=true pnpm run build
Opens bundle analyzer to identify large dependencies.
Docker Build
make build-frontend-local-image
Builds production Docker image for deployment.
Next Steps
Testing Guide
Write and run frontend tests
Backend Development
Learn about Django backend
Components
Browse UI components
Contributing
Contribution guidelines