Table of Contents
IntroductionScott Moss introduces the course and demonstrates the AI-powered application that will be built from scratch with Next.js. The application allows users to log in and manage journal entries with the standard CRUD operations. The entries are analyzed and given an AI-generated sentiment value, summary, and color. The application will be built from scratch, and the final version can be found in the GitHub repo linked below.
App Setup & Authentication
Setup App & HomepageScott 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 & LinkScott 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 ClerkScott 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 MiddlewareScott 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 WebhooksScott 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 PlanetScaleScott 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 ORMScott installs and initializes the Prisma ORM. A basic User schema is written and pushed to PlaneScale.
User & JournalEntry SchemasScott 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 SchemaScott creates the Analysis schema. An analysis contains the mood, summary, color, and a boolean value representing whether the entry is negative.
Database Utility MethodsScott 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 UserScott 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 PageScott 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 WidgetScott demonstrates how to incorporate a user profile widget provided by the Clerk authentication service.
Selecting Journal EntriesScott imports the database helper library to the journal page and creates a getEntries method for retrieving entries from the current user.
New Journal Entry UIScott 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 EntryScott 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 UIScott 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 PageScott 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.
RevalidationScott 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 DetailsScott 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 EditorScott uses a textarea element as the editor for the journal entry. The client-side state is managed with the useState hook.
Updating Journal EntriesScott 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 EntriesScott 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 UIScott 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 & PromptsScott 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 SetupScott 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 PromptsScott 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 ZodScott 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.
Prompt TemplateScott 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 TemplateScott 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 DatabaseScott 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 UIScott 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 EntryScott 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 ManagementScott 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.
NavigationScott 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 UIScott 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 OverviewScott 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 VectorsScott 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 & ResultsScott 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.
Computing a Sentiment ScoreScott 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 DatabaseScott 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 ValuesScott 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 SetupScott 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 HomepageScott 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 VercelScott 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&AScott answers questions about mocking the OpenAI API and other testing strategies specific to this application.