
Build an AI-Powered Fullstack Next.js App, v3
Learning Paths:
Topics:
Table of Contents
Introduction
App Setup & Authentication
Setup App & Homepage
Scott uses the `create-next-app` CLI tool to scaffold a new application. Next.js version 13.4.5 is recommended for this course. The default application code is removed, and a Prettier config is added.Homepage with Tailwind CSS & Link
Scott codes the homepage layout. JSX is added and includes a title, description, and a button to take the user to the journal page. Tailwind CSS classes are applied to style the page.Authentication with Clerk
Scott introduces Clerk, which will handle the application's authentication. Clerk allows multiple auth providers to be configured. The Clerk library is installed, and SignIn and SignUp components are added to their corresponding pages.Authentication Middleware
Scott creates a middleware function to ensure all routes except for the home page are protected by the authentication provider. Sign-in and sign-up redirection is configured and the authentication flow is tested.
Connecting to a Database
Understanding Webhooks
Scott explains why webhooks can help integrate multiple services. When an event in one service is triggered, an API call can push data to another service or server to maintain synchronicity between services. The auth branch can be used as a starting point for this lesson.Serverless Database with PlanetScale
Scott creates a database in PlanetScale, a database environment compatible with serverless applications. The database is provisioned, and the pscale CLI tool is installed, which allows the local application to connect to the database.Setup Prisma ORM
Scott installs and initializes the Prisma ORM. A basic User schema is written and pushed to PlaneScale.User & JournalEntry Schemas
Scott completes the User and JournalEntry schemas. A relation between the two schemas is established, and Prisma will ensure the names and types between the joined fields match.Analysis Schema
Scott creates the Analysis schema. An analysis contains the mood, summary, color, and a boolean value representing whether the entry is negative.Database Utility Methods
Scott creates a database utility method for managing connections. With hot module reloading, multiple database connection objects can be created and cause issues in the application. These utility methods check if the connection has already been established and reuse the existing connection.
Building the Journal Page
Creating a New User
Scott codes the workflow for creating a new user. When Clerk creates a new user, they are redirected to new-user route where the user data is sent to Prisma. Once Prisma is updated, the user is redirected to the journal page. The db branch can be used as a starting point for this lesson.Creating the Journal Page
Scott creates the journal page. A layout is created to contain any elements like headers and sidebars that will be reused across the subpages of the journal route.User Profile Widget
Scott demonstrates how to incorporate a user profile widget provided by the Clerk authentication service.Selecting Journal Entries
Scott imports the database helper library to the journal page and creates a getEntries method for retrieving entries from the current user.New Journal Entry UI
Scott creates EntryCard and NewEntryCard components. These components are responsible for displaying existing entries and creating new entries. They are added to the journal page, and additional Tailwind CSS classes are applied.
Creating & Updating Journal Entries
Creating a New Entry
Scott creates the route for the new journal entries. A click handler is used to navigate to the new route by appending the ID of the new entry to the end of the URL. The journal-page branch can be used as a starting point for this lesson.EntryCard Component UI
Scott adds a few Tailwind CSS classes to the EntryCard component. A Link component is used to navigate to the individual entry page.Creating the Entry Page
Scott creates the journal entry editor page. This page will display when a user navigates to an individual entry. Issues around the revalidation of data between client and server components are also discussed in this lesson.Revalidation
Scott discussed the issues with revalidating data on subsequent routes. Since client and server data are not linked, client-side routes requiring updated data from the server must be revalidated.Displaying Entry Details
Scott begins adding a text editor to the EntryCard component. A compound constraint must be added to the Prisma schema to select unique entries belonging to the authenticated user. The combination of the user's id and the entry's id creates a virtual key for selecting the entry from the database.Journal Entry Editor
Scott uses a textarea element as the editor for the journal entry. The client-side state is managed with the useState hook.Updating Journal Entries
Scott creates a route for updating the entry content. A PATCH method receives the request object and URL parameters. An updateEntry API method is also added to the application.Autosaving Entries
Scott implements autosaving with the react-autosave library to save the entry content as the application state is updated. A loading state is added, and debouncing can be controlled with the interval property.
Understanding AI & Prompt Engineering
Sidebar UI
Scott creates the sidebar to display the entry's summary, subject, and mood information. A background color changes based on the overall mood of the entry. The entry-editor branch can be used as a starting point for this lesson.LLMs & Prompts
Scott introduces Large Language Models (LLMs). One of the most popular LLMs is GPT, created by OpenAI. Prompts and the Langchain framework are also introduced in this lesson.OpenAI Setup
Scott walks through how to create an OpenAI API account. First-time users will be given a $5 credit for API usage. A credit card can be added after the credits have expired. An API key is created and added to the applications .env.local file, and the first prompt is sent to the API.Creating Consistent Prompts
Scott demonstrates how to provide a prompt that returns consistent results. Giving an example of the data to be returned from the LLM helps create more consistency and reduces the amount of parsing needed back in the application.Structured Output with Zod
Scott uses the Zod library to supply the LLM with a schema for the results returned from the LMM. Types and descriptions are provided for each data property. Temperature is also explained in this lesson.
Analyzing Entries
Prompt Template
Scott uses the PromptTemplate object from the Langchain library to create the payload sent to the OpenAI API. The prompt template includes the structured output from the Zod library along with the instructions for the LLM.Testing the Prompt Template
Scott tests the new prompt template and logs the results to the console. The template describes the JSON object needing to be returned and sends general information about JSON so the LLM understands the format.Saving Analysis to Database
Scott updates the journal API so analysis is added to the database when creating a journal entry. The OpenAI API call returns the analysis data, and Prisma writes it to the PlanetScale database.Connecting the Sidebar UI
Scott uses the include property to pull in the data from the analysis table when selecting a unique journal entry. The analysis data is added to the sidebar UI.Adding Analysis Data to Entry
Scott calls the OpenAI API when entry content is updated. The upsert method is used, so any entries without analysis will have one created. The analyze branch can be used as a starting point for this lesson.Revalidation & Client State Management
Scott discusses revalidation and how to update the client UI when a new analysis returns from the API. The sidebar is moved into the Editor component to leverage the client-side state management available to client components.Navigation
Scott adds a couple of navigation links to the left side of the application to navigate to the home page and to the journal page.
Vectors & Similarity Searching
Entry Search UI
Scott creates the UI and writes the client-side code for the search form, allowing users to ask questions to the OpenAI API about the journal entries. The code is wrapped in a client component, so the state and interactivity are available on the client. The analyze-ui branch can be used as a starting point for this lesson.Vector Database Overview
Scott introduces vectors and vector databases. Vectors group related data points which simplify the amount of context required for the OpenAI API to respond to a prompt accurately. Each entry will be added to a vector database, and only those closely matching the subject of the prompt will be used in the search analysis.Similarity Searches with Vectors
Scott codes the helper method to receive a question, creates an in-memory vector database, and performs a similarity search based on the entries. The result is an embedding that can be sent to the OpenAI API for analysis.Search API & Results
Scott finishes the search feature. The question API endpoint is added to the application. A loading state is added to the Question component, and the results are displayed when a response is received.
Sentiment Analysis
Computing a Sentiment Score
Scott adds a sentiment score to each entry to plot the user's mood over time. The score is added to the StructuredOutputParser, and the Prisma schema is updated. The question branch can be used as a starting point for this lesson.Adding Sentiments to Database
Scott begins building the history page, which will plot the sentiment scores for the user. The userId is added to the Analysis schema to make it easier to query a user's sentiment scores. This change requires new journal entries to be created.Charting Sentiment Values
Scott imports a charting library and plots the sentiment values on the history page. A custom tooltip component is created, which overrides the default component in the charting library.
Testing & Deployment
Vitest Setup
Scott introduces Vitest and installs the testing dependencies. Configuration files are added to the project, and a setupTests.ts file is created to import the jest-dom testing library.Mocking Functions & Testing Homepage
Scott creates a test for the home page. To avoid testing the functionality of third-party dependencies like Clerk, mocks are created for the library's methods.Deploying to Vercel
Scott deploys the app to Vercel. The project is pushed up to a GitHub repo and connected to the Vercel deployment. A production branch of the database is created in PlanetScale, and the schema is migrated.Q&A
Scott answers questions about mocking the OpenAI API and other testing strategies specific to this application.