Build a Fullstack App with Next.js, v2
Table of Contents
IntroductionScott Moss begins the course by demonstrating the project task management application that will be built with Next.js. The course website is shared, and a few key features of the latest version of Next.js are mentioned.
Database & Routes
Setup Next.js AppScott uses the Create Next App CLI tool to scaffold a new application. Some additional dependencies are added to the project that will be needed as the application is built. A brief tour of the application structure is also shared in this lesson.
Setup a Postgres Database on RailwayScott creates a free, hosted Postgres database on Railway.app. The connection string is saved in an ENV file in the project. The Prisma ORM, which will connect to the database, is introduced.
Setup Database Schema with PrismaScott builds the Prisma schema for the applications. The models include users, projects, and tasks. The Prisma extension for VSCode adds syntax highlighting and formatting for .prisma files.
Sync Schema & Setup Prisma ClientScott runs a migration on the database. Prisma creates an SQL file with the instructions for syncing the changes from the schema. A helper script for caching the Prisma client is also created.
Seeding the DatabaseScott creates a seed script to populate the database. This script can be run after migrations to ensure the database contains the correct data for testing the application.
Routes & Route GroupsScott introduces how routing works in Next.js 13. The page.tsx file in a directory will be loaded when that route segment is browsed. Route groups can be created by wrapping the directory name in parentheses.
GlassPane Container ComponentScott creates the GlassPane component that adds the diffused or blurred effect to the background. Since this is a container component, it is passed a parameter for the children it should render.
Root Layout ComponentsScott implements the root layouts for both the dashboard and auth route groups. Both layouts use the GlassPane component to render the children for that specific route group.
Setup Styles & Tailwind CSSScott creates a tailwind.config.js file to point Tailwind CSS at the content directories and override any theme properties. The base, components, and utilities styles are imported to the global.css file.
Button Component with VariantsScott creates a Button component and uses a library to allow variants to be applied through the component's props. Variants are added for the button's intent (primary, secondary, and tertiary) and size (small, medium, and large).
Card & Input ComponentsScott creates the Card and Input components. The Card component will display projects and tasks in the interface. The Input component will be reused in Form elements throughout the application.
SidebarLink ComponentScott builds the SidebarLink component, the first "client component" in the application. It will be rendered inside a server component, so the potential icons for each link are imported. An issue with Tailwind CSS is also fixed in this lesson.
Sidebar ComponentScott creates the Sidebar component, which will display the links for the application. An array of data for each link is mapped over to generate the SidebarLink components.
Authentication HandlersScott writes the client-side authentication handler functions for registering and signing in to the application. These handlers will be called from client-side components and interact with the API endpoints inside the Next.js pages directory.
AuthForm ComponentScott scripts an AuthForm component that can be reused for the login and registration forms. A mode property is set based on where the form is being used.
AuthForm PagesScott adds the AuthForm component to the register and sign in pages.
Password ManagementScott uses the bcrypt npm package to create two password authentication utility functions. The hashPassword function receives a plain-text password and returns a hashed version. The comparePasswords function receives a plain-text password and a hashed password. It determines if the plain-text version matches the hash.
JSON Web TokensScott creates utility functions for creating, validating, and accessing authentication JWT Tokens. A JWT token is created from a user's ID and email when they sign in. Environment variables for the JWT_SECRET and COOKIE_NAME are also made.
Register API RouteScott writes the route handler function for the register route. The handler verifies the method is POST and uses the POST data to create a User object. A JWT is created and serialized in the cookie.
Sign In API RouteScott completes the route handler for the signin route. The handler finds the user in the database, compares the saved hashed password with the password submitted from the form, and logs the user in if the password is valid.
MiddlewareScott introduces middleware which intercepts all requests to the server. A middleware function can manually check if it should run on a route, or a "matches" configuration object can be exported with a list of valid paths. This middleware function verifies a valid JWT exists before allowing access to the protected resources.
Testing Authentication RoutesScott tests the authentication routes. The home route is now protected and redirects to the login screen. Edge functions are also discussed in this lesson.
Dashboard Home PageScott builds the dashboard home page. A utility function to slow down network requests by delaying API responses is also created in this lesson.
Greetings ComponentScott creates the Greetings component, which displays the current user's name across the top of the application. The delay utility function is used to slow down the loading of the component. Since this is a server component, that delay must finish before the page can be loaded
GreetingsSkeleton ComponentScott adds a GreetingsSkeleton component, which displays a skeleton UI or "shimmer" while the entire component is loading. This provides valuable context to the user for what to expect and makes the network delay less noticeable.
ProjectCard ComponentScott demonstrates how to leverage the types created by Prisma while creating a component for the project cards. Leveraging the existing types eliminates redundancy in type declarations or the need to refactor when there are changes to the schema.
Fetching Project DataScott explains why data fetching needs to occur in the parent containing the project cards and not in each individual card. Project card data is fetched in the application. The database is reset and reseeded so the seed user's password is hashed.
Data Loading & Server ComponentsScott explains how data is loaded with server components and outlines different strategies for loading projects without blocking the initial load from the server.
TaskCard ComponentScott implements the TaskCard component. The component will display the next five tasks due across all projects. When a project is selected, this component will be reused to display tasks for a specific project.
Loader ComponentScott creates a loader component for the home page. Next.js recognizes the naming convention used for the component, and it's automatically uses it to handle the delay between page load and when the component has received data.
Creating the Project PageScott implements the project page, which displays a list of tasks for a specific project. The page uses the ID parameter passed in the URL to query the project data. The TaskCard component is reused to display the tasks.
New Project API HandlerScott writes the API handler for creating new projects. A post request is sent to the handler. Since projects are loaded server-side, the application will need to reload for the new project to be displayed.
Modal Client ComponentScott builds the UI for the NewProject modal component. Since the component renders a form and has client-side state, the "use client" directive is added.
Deploying to VercelScott deploys the application to vercel.com. The Vercel project is connected to a GitHub repo, so future commits to the main branch will be automatically deployed.