WDD 430: Web Full-Stack Development

What is Next.js?

Overview

Now that you understand how full-stack applications are organized—frontend, backend, and database layers working together—it's time to meet the framework that makes this integration seamless: Next.js.

Next.js is a modern React framework built for production-grade full-stack applications. It solves the complexity of connecting your user interfaces with backend logic, all within a single, unified framework. Instead of juggling separate frontend and backend projects, Next.js lets you build both in one place.

Prepare

Why Next.js Matters for Full-Stack Development

The Problem It Solves

Historically, building a full-stack application meant managing:

This fragmentation creates overhead: you need multiple codebases, servers, deployment pipelines, and constant context-switching between frontend and backend thinking.

Next.js: One Framework, Full Stack

Next.js brings everything under one roof. It's built on React and extends it with powerful server-side capabilities:

The Developer Experience

With Next.js, you get the following benefits:

Next.js in the Real World

Next.js is used by many teams and companies that want a modern React-based approach to building full-stack web applications:

Learning the basics of Next.js helps you build a practical understanding of how modern full-stack frameworks work and how frontend, backend, and data concerns fit together in a single app.

By the end of Week 01, you'll have a working Next.js application deployed and running using the modern App Router. You'll understand how to connect frontend, backend, and database using Server Components and API routes, and you'll have a solid foundation for the more advanced work in the rest of the course.

How Next.js Structures the Three Layers

Remember the architecture you researched earlier (Netflix, Spotify, and so on.)? Here's how Next.js maps to those layers using the modern App Router:

Your Next.js Project
├── app/                  ← FRONTEND Layer
│   ├── page.js           (Home page - Server Component)
│   ├── profile/
│   │   └── page.js       (Profile page)
│   └── api/              ← BACKEND Layer
│       ├── users/
│       │   └── route.js  (API endpoint for user data)
│       └── posts/
│           └── route.js  (API endpoint for posts)
└── lib/
    └── database.js       ← DATA Layer
        (Connection to PostgreSQL, MongoDB, and so on.)

A Concrete Example: Two Approaches

Next.js App Router gives you flexibility in how layers communicate. Here are two common patterns:

Approach 1: Server Components (Direct Database Access)

Frontend Page: /app/page.js

// Server Component (runs on server)
// Fetches data directly - no API route needed!
import { db } from '@/lib/database';

export default async function Home() {
  // Fetch data directly from database
  const posts = await db.query('SELECT * FROM posts');

  return (
    <div>
      <h1>Posts</h1>
      {posts.map(post => (
        <div key={post.id}>{post.title}</div>
      ))}
    </div>
  );
}

Notice: The component is async and fetches data directly. No useState, no useEffect, no API calls. This is a Server Component—it runs on the server and sends finished HTML to the browser.

Approach 2: API Routes (For Client Components or External Access)

Backend API Route: /app/api/posts/route.js

// API Route (Backend)
import { db } from '@/lib/database';
import { NextResponse } from 'next/server';

export async function GET() {
  const posts = await db.query('SELECT * FROM posts');
  return NextResponse.json({ posts });
}

Client Component: /app/posts/page.js

'use client'; // Makes this a Client Component
import { useEffect, useState } from 'react';

export default function Posts() {
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    fetch('/api/posts')
      .then(res => res.json())
      .then(data => setPosts(data.posts));
  }, []);

  return (
    <div>
      <h1>Posts</h1>
      {posts.map(post => (
        <div key={post.id}>{post.title}</div>
      ))}
    </div>
  );
}

When to use each: Use Server Components (Approach 1) when you can—they're faster and simpler. Use API routes (Approach 2) when you need client-side interactivity, external API access, or mobile app integration.

All in one Next.js project. All deployed together. This is the power of Next.js.

Key Next.js Features

1. File-Based Routing with App Router

In the app/ directory, folders define routes and page.js files render the UI:

2. Server Components (Default)

React components in the app/ directory are Server Components by default. They run on the server, can be async, and fetch data directly from databases. This means:

3. Client Components (When Needed)

Add 'use client' at the top of a file to make it a Client Component for interactivity (hooks, event handlers, browser APIs). Mix Server and Client Components as needed.

4. API Routes

Build REST APIs with route.js files. Export functions like GET, POST, PUT, DELETE to handle HTTP methods.

5. Database Integration

Connect to any database (PostgreSQL, MongoDB, Firebase, and so on.) directly from Server Components or API routes. Credentials stay on the server (never exposed to the browser).

Assignment Preview: Concepts You Will Use This Week

Your Week 01 assignment uses a practical subset of modern Next.js + TypeScript patterns. Review these concepts before you begin:

1. TypeScript Interfaces for Component Props

In your assignment, components such as ProjectCard use an interface to define expected props and their types.

interface ProjectCardProps {
  title: string;
  description: string;
  technologies: string[];
  link?: string;
}

Why this matters: Interfaces provide type safety, clearer documentation, and better editor support while you code.

2. Props Destructuring and Reusable Components

You will pass data into components and destructure props directly in function parameters, for example:

export default function ProjectCard({ title, description, technologies, link }: ProjectCardProps) {
  // ...
}

This keeps components reusable and focused on a single responsibility.

3. Rendering Lists with map() and Prop Spreading

Your ProjectList component will iterate over an array and render one ProjectCard per project.

{projects.map((project) => (
  <ProjectCard key={project.title} {...project} />
))}

This example uses project.title as a simple key. In production apps, use a stable, unique ID when available.

4. Conditional Rendering

You will use conditional rendering to show optional content only when data exists:

{link && (
  <a href={link} target="_blank" rel="noopener noreferrer">View Project</a>
)}

5. Navigation: Next.js Link vs HTML <a>

6. App Router File Structure and Root Layout

Your assignment uses the App Router structure and includes default files created by create-next-app, including app/globals.css and app/favicon.ico. You will also compose pages with a shared root layout using children, plus reusable Header and Footer components.

Submission

Return to Canvas after completing all the learning activities and submit the W01 Learning Activities quiz.