Building with AdonisJS & Inertia

We'll learn how to use InertiaJS with AdonisJS 6 to build a feature-complete application, called PlotMyCourse. Our application will use server-side rendering (SSR), Vue 3, and Shadcn-Vue. It'll feature organizations, drag-and-drop, and lots of forms.

Difficulty Intermediate
What's Inside 91 Lessons
Viewing Time 14h 5m
Sign up to save progress
1

Module 1
Getting Started

1.0

What Is InertiaJS?

In this lesson, we'll introduce InertiaJS. We'll discuss what it is, what it helps with, its adapters, and more.

1m 57s
1.1

What We'll Be Building

At the end of this series, we'll have a feature-complete application with organizations, teams, drag-and-drop ordering, password reset, and more! So, let's take a second to see our end goal and walk through the app.

6m 18s
1.2

Creating Our AdonisJS App With InertiaJS

In this lesson, we'll create a new AdonisJS 6 application with the InertiaJS Starter Kit. We'll then walk through the Inertia-specific aspects of our project structure and briefly discuss the configuration steps taken when adding Inertia.

11m 26s
1.3

Server-Side Rendering (SSR) vs Client-Side Rendering (CSR)

In this lesson, we'll discuss the differences between InertiaJS in a server-side rendered (SSR) and a client-side rendered (CSR) application. We'll then compare when you would want to choose one over the other and the pros and cons of both.

6m 6s
1.4

Setting Up TailwindCSS, Shadcn-Vue, and Automatic Component Imports

In this lesson, we'll install and configure Shadcn-Vue and TailwindCSS. We'll then set up automatic imports for all of our local Vue components.

19m 9s
2

Module 2
The Basics

2.0

The Flow of Pages and Page Props

In this lesson, we'll talk about the flow InertiaJS follows when rendering pages, from AdonisJS to our Vue application. Then, we'll talk about passing props to our Vue page components.

6m 55s
2.1

Sharing Data from AdonisJS to Vue via Inertia

In this lesson, we'll learn how to pass data from AdonisJS to Vue using Inertia as the broker. We'll discuss passing data from our controllers, from middleware, and globally via the Inertia shared data configuration.

7m 37s
2.2

Linking Between Pages & Page State Flow

In this lesson, we'll learn how to link from page to page the InertiaJS way. We'll then inspect how InertiaJS gets and updates our page's stateful information via our page props.

5m 58s
2.3

The Link Component and Programmatic Linking

In this lesson, we'll explore Inertia's Link component and its props. We'll then examine how to link between pages programmatically using Inertia's router.

8m 32s
2.4

Global Components and Hydration Mismatch in Action

In this lesson, we'll learn how to register components globally inside our Vue application. We'll also learn what to watch out for and examine a hydration mismatch in action.

5m 0s
2.5

Partial and Lazy Data Loading and Evaluation

In this lesson, we'll learn about Inertia's partial reload functionality that allows us to refresh only specifically specified prop items for our page. We'll also examine lazy properties and how our props are evaluated with partial reloads.

9m 11s
2.6

Creating A Layout

In this lesson, we'll learn how to create a layout component and apply it to all our pages, the Inertia way.

6m 55s
2.7

Default Layouts & Overwriting the Default Layout

In this lesson, we'll inspect how Inertia injects our layout component and the data passed to it. We'll also learn how we can overwrite our default layout from our page components.

6m 0s
2.8

Specifying Page Titles & Meta Tags

In this lesson, we'll learn how we can append information into the head of our document with Inertia on a per-page basis. We'll then create our own head component, wrapping Inertia's, to allow easier global changes.

8m 59s
2.9

What Code Can & Can't Be Shared Between AdonisJS & Inertia

In this lesson, we'll discuss what code we can and cannot share between AdonisJS and Inertia.

3m 10s
3

Module 3
Working with Forms

3.0

Inertia Form Basics

In this lesson, we'll introduce the basics of working with form in Inertia. We'll set up our register form with its fields, get our form state set up, and send off a post request to one of our AdonisJS routes.

4m 15s

Form Validation & Displaying Errors

In this lesson, we'll add validation to our POST handler for our register route. We'll then update our form fields to check for and display any validation errors that may have occurred.

6m 47s

The useForm Helper

In this lesson, we'll introduce the useForm helper composable provided by InertiaJS. We'll refactor our current form to use the useForm helper instead and see how it can help manage our form state, errors, and submission.

8m 0s

Common useForm Methods & Options

In this lesson, we'll walk through some of the more common methods and options we have with the useForm helper, like adding form errors, maintaining scroll position, mutating values prior to submission, and more.

4m 23s

Creating A FormInput Vue Component

In this lesson, we'll create a reusable FormInput Vue Component using our current form field as a starting point.

7m 9s

Cross-Site Request Forgery (CSRF) Protection in InertiaJS

In this lesson, we'll learn how cross-site request forgery (CSRF) protection between AdonisJS and InertiaJS behaves in our application.

2m 55s

What Are Some of Inertia's Limitations

In this lesson, we'll discuss a couple of Inertia's request and routing limitations and how we can circumvent them by reaching for axios or fetch instead.

4m 26s
4

Module 4
Database & Project Scaffolding

Understanding Our Database Schema

In this lesson, we'll walk through a diagram of our application's database schema to understand how things relate to one another.

2m 18s

Defining Our Migrations & Foreign Keys

In this lesson, we'll create our migrations, models, and some of our controllers. We'll then fill out our migrations and define our relationship's foreign key constraints.

13m 48s

Defining Our Lucid Models & Relationships

In this lesson, we'll convert our migrations into Lucid Models and define both sides of the relationships so they're ready to go.

19m 8s

Creating A Lucid Model Mixin for our Organization Relationship

In this lesson, we'll learn how we can extract repetitive relationships, and other model properties/methods, into a mixin. We'll also learn what to look out for when using decorators within a TypeScript mixin.

8m 29s

Seeding Our Initial Database Data

In this lesson, we'll create a seeder file to create the initial data we'll need in our database to get going. For now, that's just going to be our application's roles.

3m 39s

Typing Lucid Models in Inertia with DTOs

In this lesson, we'll learn how we can specify types for our Lucid Models easily using DTOs we'll generate directly from our models.

10m 58s

Completing Our AppLayout & Navigation Bar

In this lesson, we'll finish setting up our application's layout shell and navigation bar.

16m 31s

Creating A Toast Message Manager

Learn how to implement a user feedback manager in your app using toast messages and vue-sonner. We'll integrate our flash message manager with state provided from AdonisJS' flash messages store to display success and error messages.

15m 7s
5

Module 5
Authentication & Onboarding

User Registration with InertiaJS

In this lesson, we'll complete our user registration flow by validating our registration form data, creating a new user, logging that user in, and forwarding them to the next page in the flow.

8m 37s

Splitting Our Routes Between Auth & Web

In this lesson, we'll split our routes into two files: auth and web. Our auth routes file will contain all our authentication-based route definitions and our web routes will contain the remaining.

5m 5s

Logging Out Users

In this lesson, we'll hook up our logout user menu button to a POST route to logout an authenticated user.

4m 20s

Onboarding Newly Registered Users

In this lesson, we'll create our onboarding flow for newly registered users. Before users can enter the application, they'll need to have at least one organization set up so everything works smoothly.

22m 31s

Logging In Users & Displaying Exceptions

In this lesson, we'll add the ability to login to our application. We'll then discuss the differences between errors and errorsBag and how we can display long-lived exception messages as an alternative to our toast manager.

10m 40s

Adding the Remember Me Token

In this lesson, we'll enable the remember me feature on our auth login flow and add the remember me tokens table to our database.

5m 45s

Forgot Password & Password Reset

In this lesson, we'll walk through setting up the complete forgot password flow including, creating a password reset token with time-expiry, sending an email notification with a password reset link, verifying the token, and resetting the users password.

29m 42s
6

Module 6
The User's Organizations

Setting & Loading the User's Active Organization

In this lesson, we'll set up our organization middleware and actions that'll be in charge of loading the user's active organization and role

12m 42s

Listing the User's Organizations

In this lesson, we'll update our organization middleware to query all the user's organizations. We'll then provide everything into our Vue page state via Inertia and begin building our organization select component.

6m 42s

The Form Dialog Component & Adding Organizations

In this lesson, we'll add a new form dialog component that simplifies the create and update forms we'll use throughout our application. We'll then use this component to add a create organization dialog within our organization selector.

11m 51s

Switching Between Organizations

In this lesson, we'll add the ability for our users to change which of their organizations is their active organization via our organization selector.

2m 56s

Creating A UseResourceActions Composable

In this lesson, we'll create a composable that'll be in charge of maintaining form and dialog state for the resources throughout our application, starting with our organizations.

8m 42s

Editing the Active Organization

In this lesson, we'll make use of our useResourceActions composable to add the ability for our user's to edit their active organization.

7m 0s

The Confirm Delete Dialog & Deleting the Active Organization

In this lesson, we'll create a reusable confirm deletion dialog and bind its state into our use resource actions composable. We'll then incorporate this all together to allow users to delete their active organization.

18m 24s
7

Module 7
Difficulties, Statuses, & Access Levels

Listing & Creating Difficulties

In this lesson, we'll create a page to list the active organization's difficulties. Then, we'll add the ability to create a new difficulty and add a new color picker type to our FormInput component.

15m 14s

Updating Difficulties

In this lesson, we'll add the ability to update an organization's difficulties using our useResourceActions composable.

4m 44s

Confirming & Deleting Difficulties

In this lesson, we'll add the ability to delete an organization's difficulties. We'll also make use of our ConfirmDeleteDialog component to confirm the deletion action with our user.

4m 15s

Replacing A Course's Deleted Difficulty

In this lesson, we'll add the ability to gracefully handle instances where a required relationship's record is being deleted. When a difficulty is being used by a course, we'll have our users select a replacement difficulty for the one being deleted.

16m 31s

Reusable VineJS Exists In Organization Validation

In this lesson, we'll take our difficulty's exists in organization VineJS validation and make it reusable so we can easily use it for our statuses, access levels, courses, etc.

3m 38s

Sorting Difficulties with Drag & Drop

In this lesson, we'll allow our user's to customize the ordering of their difficulties via drag-and-drop using VueDraggable. When they commit a change by dropping an item, we'll persist the updated sort to our database.

9m 21s

Creating A Reusable Sorting Vue Component

In this lesson, we'll extract the drag-and-drop logic we added in the last lesson into a reusable Vue component so we can easily make use of the same behavior for our access levels & statuses.

4m 1s

Replicating Behaviors for Access Levels & Statuses

In this lesson, we'll replicate everything we've done for difficulties to add our access levels and statuses.

14m 28s
8

Module 8
Courses

Querying & Listing An Organization's Courses

In this lesson, we'll query the active organization's courses and list them out in a table.

9m 6s

Creating A New Course

In this lesson, we'll add the ability to create a new course within the active organization.

14m 2s

Editing & Updating Courses

In this lesson, we'll add the ability to edit and update a course within the active organization.

5m 6s

Deleting Courses

In this lesson, we'll add the ability to delete courses from an organization with confirmation from our user.

3m 31s

Showing A Course's Details

In this lesson, we'll add our courses show page, which will be in charge of displaying the course details along with its modules & lessons. In this lesson we'll lay the groundwork, then in the next module we'll add in our modules & lessons.

5m 58s

The Tag Selector

In this lesson, we'll define a reusable tag selector component that we'll use for our difficulties, access levels, and statuses. This component will accept props that will automatically send a patch request to our server to update values as they change.

13m 28s
9

Module 9
Modules & Lessons

Querying & Listing Sortable Course Modules

In this lesson, we'll query and add a sortable list of a course's modules on the courses show page.

14m 43s

Creating, Editing, & Deleting Course Modules

In this lesson, we'll add the ability to create, edit, and delete a course's modules using the reusable components and composables we've created in past lessons.

15m 57s

Creating & Listing Sortable Course Lessons

In this lesson, we'll add the ability to create new lessons within a course's module. We'll then list the lessons within their designated module using the order specified by the user.

19m 30s

Editing & Deleting Course Lessons

In this lesson, we'll add the ability to edit and delete lessons from a course's module. When editing, we'll also decrement the order field for all lessons within the module after the lesson being deleted.

5m 9s

Adding A Publish Date & Time Input

In this lesson, we'll add publish at date & time fields to our lesson form. We'll learn how we can split these two values apart for their inputs and join them back together before sending to the server. We'll also touch on timezone considerations.

8m 20s

Patching Tag Changes for our Modules & Lessons

In this lesson, we’ll incorporate our Tag Selector component into our modules and lessons to facilitate easy visibility and updates of their statuses and access levels.

8m 42s

Storing Module Order Changes from Vue Draggable

In this lesson, we'll persist sort order changes to the database when dragging and dropping a course's modules using Vue Draggable

8m 31s

Storing Lesson Order Changes & Handling Cross-Module Drag & Drops

In this lesson, save the changes made to lessons inside a course when a user uses drag-and-drop to move a lesson. Users can change the lesson order inside a single module or move a lesson into a new module, so we'll need to handle both use cases.

15m 44s
10

Module 10
Settings

Creating the Settings Shell

In this lesson, we'll create shell specifically for our settings pages to live between our pages and their layouts.

8m 10s

User Profile Settings

In this lesson, we'll add our user profile settings form and update logic.

6m 58s

Allowing Users to Safely Update Their Account Email

In this lesson, we'll add the ability for our users to safely update their account email address. We'll require them to confirm their password, then make the update in our database and log it to the user's email histories.

10m 39s

Alerting Users When Their Account Email Is Changed

In this lesson, we'll add an additional security step onto our account email change logic, by also notifying the user's old email address about the change.

4m 35s

Account Deletion & Cleaning Dangling Organizations

In this lesson, we'll add the ability for our user's to delete their accounts. During account deletion, we'll also delete any organization's this user is the only member of, keeping them from dangling inside our database without users.

10m 6s

Updating & Deleting an Organization

In this lesson, we'll begin work on our organization's settings page by adding the ability to update and delete the active organization.

10m 15s
11

Module 11
Organization Members

Listing Current Organization Members

In this lesson, we'll query and list all current members within our active organization.

8m 25s

Sending an Invitation to Join Our Organization

In this lesson, we'll begin our organization invite system by first adding the ability to send an invitation email to join our organization.

13m 30s

Accepting an Organization Invitation

We'll add our route to handle accepting an organization invite. Within this route, we'll verify our signed url, ensure the invitation is valid, accept the invite, and gracefully handle the use-case where users may need to first login or register.

18m 4s

Adding the Organization Invite User Interface

In this lesson, we'll wrap up our invite send & accept flows by adding the UI needed to view pending organization invites as well as send new invites. We'll then walk through tests of each flow scenario to ensure all is working.

20m 0s

Canceling an Organization Invite

In this lesson, we'll add the ability to cancel a sent invitation to an organization.

6m 5s

Removing an Organization User

In this lesson, we'll add the ability to remove users, including ourselves, from an organization. We'll also discuss a few key elements needed to handle this gracefully.

9m 37s

Refreshing Partial Page Data

In this lesson, we'll implement a refresh functionality on our org users and invites tables using Inertia's nifty partial data reloading feature.

5m 17s
12

Module 12
Authorization

Rolling Our Own Authorization Access Controls

In this lesson, we'll create our own simple authorization access control list. We'll then share this list globally throughout our application by appending it to our HttpContext and sharing it with our Vue application via Inertia.

6m 55s

Applying Our Server-Side Authorization Checks

In this lesson, we'll use our access controls to add authorization checks to our controllers where needed. This will help ensure members can't update, delete, or invite users.

13m 0s

Applying Our Authorization UI Checks

In this lesson, we'll use our access controls to apply authorization checks to the user interface of our application. This will ensure users don't see actionable items for operations they aren't allowed to perform.

6m 23s

Setting Up Secondary TailwindCSS Config & CSS File for our Landing Page

In this lesson, we'll create a second TailwindCSS configuration and CSS file specifically for our landing page, which we'll render with EdgeJS.

8m 43s

Restricting Login Attempts with Rate Limiting

In this lesson, we'll add AdonisJS' Rate Limiter to our web login action to restrict the number of times a user can attempt to login to our application with invalid credentials.

9m 24s

Clearing Login Attempt Rate Limits on Password Reset

In this lesson, we'll finish our authentication rate limiting flow by clearing out any rate limits counting against the user when they reset their password.

4m 31s
13

Module 13
InertiaJS 2

Upgrading to Inertia 2

During this series, InertiaJS release version 2 consisting of minimal breaking changes and several new features. In this lesson, we'll upgrade our packages to the latest for both InertiaJS & AdonisJS.

11m 53s

Polling for Changes in InertiaJS 2

In this lesson, we'll cover the new polling feature in InertiaJS 2 to see how we can use it to poll our server at an interval for updated information.

4m 12s

Prefetching Page to Boost Load Times in InertiaJS 2

In this lesson, we'll take a look at the new prefetching feature in InertiaJS 2. We can use this feature to load the next page's data before the user even clicks on the link.

11m 26s

Defer Loading Props in InertiaJS 2

In this lesson, we'll take a look at the new deferred props feature in InertiaJS 2. Deferred props allows us to delay a prop from loading until the page itself has mounted, which is great for slower queries or below the fold.

5m 55s

Deferring A Prop Load Until it is Visible in InertiaJS 2

In this lesson, we'll take a look at the new WhenVisible InertiaJS 2 component. This component uses an intersection observer to allow us to defer loading a prop's data until it is actually visible on the page.

4m 27s

Super Easy Infinite Scroll in InertiaJS 2 with Prop Merging

In this lesson, we'll learn how we can combine the new InertiaJS 2 features together, along with Lucid's pagination, to add infinite scrolling to our application. This is made super easily by the last new feature we'll dicuss, called merged props.

11m 9s