Build and Deploy a Multi Vendor E Commerce With Nextjs, React & Stripe Connect (Part 2/2)

Code With AntonioAbout 20 min readMay 27, 2025Watch original
THE SUMMARYAI-generated

Multi-Tenant E-commerce Tutorial - Part 2: Detailed Summary

Key Concepts:

  • Multi-tenancy, E-commerce, Payload CMS, Stripe Connect, Product Reviews, Vendor Subdomains, Deployment, Cart, Checkout, Star Rating, Global Store (zustand), Hydration Errors, Webhooks, Access Control, Subdomain Rewrites, Storage Adapters (Vercel Blob).

1. Addressing Mistakes from Part 1:

  • Node Version Incompatibility:
    • The tutorial was initially developed using Node v21.7.3.
    • Upgrading to the latest LTS version (e.g., 22.14.0) can cause "module not found" errors, particularly when running scripts like generate types.
    • Fix 1: Downgrade to Node v21.7.3.
    • Fix 2: Add "type": "module" to package.json.
  • Typing Issue in Plugins User Access:
    • The roles property in the users collection was not correctly typed.
    • Fix: Import config from ./payload-types to properly type user.roles.

2. Chapter 17: Product Page Development

  • Objective: Develop a product page with detailed product information.
  • Step 1: Create get one Procedure:
    • Create a base procedure called get one in src/modules/products/server/procedures.ts.
    • Input: Zod object with only the ID of the product.
    • Query:
      • Destructure context and input.
      • Fetch product data using context.database.findByID with the product ID.
      • Return the product.
  • Step 2: Create Product ID Route:
    • Create a page.tsx file inside app/tenants/[slug]/home/products/[productID].
    • Interface props to destructure productID and slug from the promise.
    • Asynchronous method to destructure productID and slug from params.
    • Implement prefetching using useQuery and useSuspenseQuery.
    • Create a ProductView component and pass in productID and tenant slug.
  • Step 3: Create Product View Component:
    • Create ProductView.tsx inside src/modules/products/UI/views.
    • Define props accepting productID and tenant slug.
    • Build the UI:
      • Use next/image to display the product image (using placeholder if no image is available).
      • Implement a grid layout with product name, price, tenant details, ratings, and description.
      • Create a StarRating component for displaying ratings.
      • Implement a mobile-specific view for ratings.
      • Add a button to add the item to the cart.
      • Display refund policy information.
  • Step 4: Modify Product Card Link:
    • Update the href link in the ProductCard component to use generateTenantURL with the tenant slug and product ID.
  • Technical Terms:
    • depth: Controls how many levels of related data are populated in a query.
    • generateTenantURL: A utility function to generate URLs with the tenant slug.
    • useSuspenseQuery: A React Query hook for fetching data with suspense.
    • TRPC: A library for building type-safe APIs.
  • Logical Connections:
    • The get one procedure provides the data for the ProductView component.
    • The ProductCard component links to the ProductView component.
  • Data:
    • The product data includes name, price, description, image, tenant, and refund policy.
  • Conclusion:
    • A responsive product view is created, displaying detailed product information.

3. Chapter 18: Cart and Checkout (UI Only)

  • Objective: Implement the cart functionality and checkout page UI.
  • Step 1: Add Zustand (Global Store):
    • Install zustand using bun add zustand.
    • Create a useCartStore.ts file inside src/modules/checkout/store.
    • Define interfaces:
      • TenantCart: productIDs (array of strings).
      • CartState: tenantCarts (object with tenant slugs as keys and TenantCart as values), addProduct, removeProduct, clearCart, clearAllCarts, getCartByTenant.
    • Implement useCartStore using create and persist from zustand.
    • Store cart data in localStorage using createJSONStorage.
  • Step 2: Implement Cart Functionality:
    • tenantCarts: Empty object initially.
    • addProduct: Adds a product ID to the productIDs array for a specific tenant.
    • removeProduct: Removes a product ID from the productIDs array for a specific tenant.
    • clearCart: Clears the productIDs array for a specific tenant.
    • clearAllCarts: Clears all tenant carts.
    • getCartByTenant: Returns the productIDs array for a specific tenant.
  • Step 3: Create useCart Hook:
    • Create a useCart.ts file inside src/modules/checkout/hooks.
    • Import useCartStore and destructure its methods.
    • Implement helper methods:
      • toggleProduct: Adds or removes a product based on its presence in the cart.
      • isProductInCart: Checks if a product is in the cart.
      • clearTenantCart: Clears the cart for a specific tenant.
    • Return an object with product IDs, addProduct, removeProduct, clearCart, clearAllCarts, isProductInCart, and totalItems.
  • Step 4: Implement Add to Cart Button:
    • Create a CartButton.tsx component inside src/modules/products/UI.
    • Accept tenant slug and productID as props.
    • Use the useCart hook to get cart functionality.
    • Implement the button logic:
      • If the product is in the cart, display "Remove from Cart" and a white background.
      • Otherwise, display "Add to Cart" and a pink background.
      • Use toggleProduct to add or remove the product.
    • Replace the original button in ProductView with the CartButton component.
  • Step 5: Fix Hydration Errors:
    • Use next/dynamic to dynamically load the CartButton component without server-side rendering (SSR).
    • Add a loading fallback with a disabled button.
  • Step 6: Implement Checkout Button:
    • Create a CheckoutButton.tsx component inside src/modules/checkout/UI/components.
    • Accept tenant slug and optional className and hideIfEmpty props.
    • Use the useCart hook to get the total number of items in the cart.
    • If hideIfEmpty is true and the cart is empty, return null.
    • Display a button with a shopping cart icon and the total number of items.
    • Link the button to the checkout page using generateTenantURL.
    • Add dynamic loading without SSR to avoid hydration errors.
    • Add a loading skeleton.
  • Step 7: Add Checkout Button to Navbar:
    • Import the CheckoutButton component into the Navbar component in src/modules/tenants/UI.
    • Pass in the tenant slug.
  • Technical Terms:
    • Zustand: A minimalist state management library.
    • Hydration Error: An error that occurs when the server-rendered HTML doesn't match the client-rendered HTML.
    • Dynamic Import: A way to load modules asynchronously.
    • SSR (Server-Side Rendering): Rendering a React component on the server.
  • Logical Connections:
    • The useCartStore provides the cart data to the useCart hook.
    • The useCart hook provides the cart functionality to the CartButton and CheckoutButton components.
  • Conclusion:
    • The cart functionality is implemented using Zustand.
    • The UI is modified to display the cart and checkout buttons.
    • Dynamic loading is used to avoid hydration errors.

4. Chapter 19: Checkout Page UI

  • Objective: Implement the checkout page UI.
  • Step 1: Create Checkout Route:
    • Create a page.tsx file inside app/tenants/[slug]/checkout.
    • Render a "Checkout Page" div.
  • Step 2: Create Checkout Layout:
    • Create a layout.tsx file inside app/tenants/[slug]/checkout.
    • Copy the Navbar component from src/modules/tenants/UI to src/modules/checkout/UI/components.
    • Modify the Navbar component to display "Checkout" text and a "Continue Shopping" button.
    • Copy the layout from app/tenants/[slug]/home and simplify it.
    • Remove prefetching, suspense, and hydration boundary.
    • Import the Navbar component from the checkout module.
  • Step 3: Implement Checkout Page:
    • Create a CheckoutView.tsx component inside src/modules/checkout/UI/views.
    • Accept tenant slug as a prop.
    • Render a div including the tenant slug.
    • Mark the component as a client component (use client).
  • Step 4: Create Checkout Procedures:
    • Copy the products/server folder to checkout/server.
    • Rename products router to checkout router.
    • Remove the get one procedure.
    • Modify the get many procedure to fetch products based on an array of IDs.
    • Add validation to check if all products were found.
  • Step 5: Add Checkout Router to TRPC:
    • Import the checkout router into src/server/api/root.ts.
    • Add the checkout router to the TRPC router.
  • Step 6: Fetch Products in Checkout View:
    • Use useTRPC and useQuery to fetch products based on the product IDs from the cart.
    • Display the products using JSON.stringify.
  • Step 7: Implement Error Handling:
    • Use useEffect to check for errors in the useQuery hook.
    • If a "not found" error occurs, clear all carts and display a warning message using useToast.
  • Step 8: Display Checkout Items:
    • Implement a grid layout with two columns: one for rendering items and one for the checkout sidebar.
    • Create a CheckoutItem.tsx component inside src/modules/checkout/UI/components.
    • Pass props to the CheckoutItem component: imageURL, name, productURL, tenantURL, tenantName, price, and onRemove.
    • Implement the UI for the CheckoutItem component, including an image, product name, tenant details, price, and a "Remove" button.
  • Step 9: Implement Checkout Sidebar:
    • Create a CheckoutSidebar.tsx component inside src/modules/checkout/UI/components.
    • Accept props: total, onCheckout, isCancelled, and isPending.
    • Implement the UI for the CheckoutSidebar component, including the total price and a "Checkout" button.
  • Step 10: Implement Loading and Empty States:
    • Add loading and empty states to the CheckoutView component.
    • Display a loading icon while the data is being fetched.
    • Display a "No products found" message if the cart is empty.
  • Step 11: Implement Cancelled State:
    • Add a cancelled state to the CheckoutSidebar component.
    • Display an error message if the checkout is cancelled.
  • Technical Terms:
    • Route Group: A way to group routes in Next.js.
    • Layout: A component that wraps a route and provides a consistent UI.
    • TRPC Error: An error that can be thrown from a TRPC procedure.
    • UseToast: A hook for displaying toast notifications.
  • Logical Connections:
    • The checkout router provides the data for the CheckoutView component.
    • The CheckoutItem component displays the individual items in the cart.
    • The CheckoutSidebar component displays the total price and the checkout button.
  • Conclusion:
    • The checkout page UI is implemented, including the checkout items, sidebar, loading state, empty state, and cancelled state.

5. Chapter 20: Stripe Integration

  • Objective: Integrate Stripe payment processing into the application.
  • Step 1: Create Stripe Project:
    • Create a new Stripe account or use an existing one.
    • Obtain the Stripe secret key from the Stripe dashboard.
  • Step 2: Install Stripe SDK:
    • Install the Stripe SDK using bun add Stripe.
  • Step 3: Create Stripe Util:
    • Create a stripe.ts file inside lib.
    • Import stripe from stripe.
    • Export a stripe instance with the Stripe secret key and API version.
  • Step 4: Create Protected Procedure:
    • Create a protected procedure in src/server/api/trpc.ts that extends the base procedure.
    • Check if the user is authenticated.
    • If not, throw a TRPC error with code unauthorized.
    • Add the session user to the context.
  • Step 5: Implement Purchase Procedure:
    • Create a purchase procedure in src/modules/checkout/server/procedures.ts using the protected procedure.
    • Input: Zod object with productIDs (array of strings) and tenant slug.
    • Mutation:
      • Fetch products from the database based on the productIDs and tenant slug.
      • Fetch the tenant from the database based on the tenant slug.
      • Create line items for the Stripe checkout session.
      • Create a Stripe checkout session with customer, success URL, cancel URL, mode, line items, and metadata.
      • Return the checkout URL.
  • Step 6: Implement Checkout View:
    • Use useTRPC and useMutation to call the purchase procedure.
    • Implement onSuccess and onError callbacks to handle the checkout result.
    • Redirect the user to the Stripe checkout page on success.
    • Display an error message if the checkout fails.
  • Step 7: Implement Checkout States Hook:
    • Create a useCheckoutStates.ts file inside src/modules/checkout/hooks.
    • Use useQueryState to manage the success and cancel states.
  • Step 8: Implement Webhook Route:
    • Create a route.ts file inside app/api/stripe/webhooks.
    • Import necessary modules.
    • Validate the Stripe webhook signature.
    • Handle the checkout.session.completed event.
    • Fetch the user and the expanded session.
    • Create orders in the database for each line item.
    • Return a 200 OK response.
  • Step 9: Create Orders Collection:
    • Create an orders.ts file inside src/collections.
    • Define the orders collection with fields for name, user, product, and Stripe checkout session ID.
  • Step 10: Add Orders Collection to Payload Config:
    • Import the orders collection into payload.config.ts.
    • Add the orders collection to the collections array.
  • Step 11: Configure Stripe Webhook:
    • Download and install the Stripe CLI.
    • Log in to Stripe using stripe login.
    • Listen for webhook events using stripe listen forward to.
    • Copy the signing secret.
    • Add the signing secret to the environment variables.
  • Technical Terms:
    • Stripe Connect: A Stripe feature that allows platforms to facilitate payments between third-party sellers and buyers.
    • Protected Procedure: A TRPC procedure that requires the user to be authenticated.
    • Line Item: A product or service being purchased in a Stripe checkout session.
    • Webhook: A mechanism for receiving real-time notifications from Stripe.
  • Logical Connections:
    • The purchase procedure creates a Stripe checkout session.
    • The webhook route handles the checkout.session.completed event and creates orders in the database.
  • Data:
    • The Stripe checkout session contains information about the customer, line items, and total amount.
    • The orders collection stores information about the user, product, and Stripe checkout session ID.
  • Conclusion:
    • Stripe payment processing is integrated into the application.
    • A webhook route is implemented to handle Stripe events and create orders.
    • A protected procedure is used to ensure that only authenticated users can initiate a purchase.

6. Chapter 21: Library Implementation

  • Objective: Implement the orders/purchases library feature.
  • Step 1: Fix Invalid Checkout:
    • Modify the checkout view to redirect to /library on successful purchase.
    • Invalidate the library get many query to refetch the library data.
  • Step 2: Create Get One Library Procedure:
    • Create a get one procedure in src/modules/library/server/procedures.ts.
    • Input: Zod object with the productID.
    • Query:
      • Fetch the order based on the productID and the user's ID.
      • Fetch the product based on the productID.
      • Return the product.
  • Step 3: Develop Library Product ID UI:
    • Create a page.tsx file inside app/library/[productID].
    • Implement prefetching using useQuery.
    • Render the ProductView component.
  • Step 4: Develop Library Product View:
    • Create a ProductView.tsx component inside src/modules/library/UI/views.
    • Accept productID as a prop.
    • Fetch the product data using useSuspenseQuery.
    • Implement the UI for the product view, including the product name and a "Back to Library" button.
  • Step 5: Modify Product Card:
    • Update the ProductCard component to link to the library page.
  • Step 6: Add Library Button:
    • Add a library button to the search input component.
  • Technical Terms:
    • Prefetching: Fetching data before it is needed to improve performance.
    • Suspense: A React feature that allows components to "wait" for data to load.
  • Logical Connections:
    • The get one library procedure provides the data for the ProductView component.
    • The ProductCard component links to the ProductView component.
  • Conclusion:
    • The orders/purchases library feature is implemented, allowing users to view their purchased products.

7. Chapter 22: Reviews Implementation

  • Objective: Implement the reviews feature, allowing users to leave reviews for purchased products.
  • Step 1: Create Reviews Collection:
    • Create a reviews.ts file inside src/collections.
    • Define the reviews collection with fields for description, rating, product, and user.
  • Step 2: Add Reviews Collection to Payload Config:
    • Import the reviews collection into payload.config.ts.
    • Add the reviews collection to the collections array.
  • Step 3: Create Reviews Procedures:
    • Create a procedures.ts file inside src/modules/reviews/server.
    • Implement the following procedures:
      • get one: Fetches a single review for a product and user.
      • create: Creates a new review.
      • update: Updates an existing review.
  • Step 4: Add Reviews Router to TRPC:
    • Import the reviews router into src/server/api/root.ts.
    • Add the reviews router to the TRPC router.
  • Step 5: Develop Review Sidebar:
    • Create a ReviewSidebar.tsx component inside src/modules/library/UI/components.
    • Accept productID as a prop.
    • Fetch the review data using useSuspenseQuery.
    • Render the ReviewForm component.
  • Step 6: Develop Review Form:
    • Create a ReviewForm.tsx component inside src/components.
    • Implement the UI for the review form, including a star picker and a text area.
    • Use useForm and zodResolver to handle form validation.
    • Implement the onSubmit method to create or update the review.
  • Step 7: Implement Star Picker:
    • Create a StarPicker.tsx component inside src/components.
    • Implement the UI for the star picker, allowing users to select a rating.
  • Technical Terms:
    • Zod: A TypeScript-first schema declaration and validation library.
    • UseForm: A React Hook for managing form state.
    • ZodResolver: A resolver that integrates Zod with useForm.
  • Logical Connections:
    • The reviews router provides the data for the ReviewSidebar component.
    • The ReviewForm component allows users to create or update reviews.
  • Conclusion:
    • The reviews feature is implemented, allowing users to leave reviews for purchased products.

8. Chapter 23: Aggregating Reviews

  • Objective: Aggregate reviews and display them in the product cards and product views.
  • Step 1: Modify Products Get Many Procedure:
    • Modify the get many procedure in src/modules/products/server/procedures.ts to calculate the average review rating and the number of reviews for each product.
    • Use promise.all to fetch the reviews for each product in parallel.
  • Step 2: Modify Product List:
    • Modify the ProductList component in src/modules/products/UI/components to display the average review rating and the number of reviews.
  • Step 3: Modify Products Get One Procedure:
    • Modify the get one procedure in src/modules/products/server/procedures.ts to calculate the average review rating and the rating distribution for a single product.
  • Step 4: Modify Product View:
    • Modify the ProductView component in src/modules/products/UI/views to display the average review rating, the number of reviews, and the rating distribution.
  • Step 5: Implement Copy URL Button:
    • Add a button to the ProductView component that copies the current URL to the clipboard.
  • Step 6: Modify Library Procedures:
    • Modify the get many procedure in src/modules/library/server/procedures.ts to calculate the average review rating and the number of reviews for each product.
  • Technical Terms:
    • Promise.all: A method that takes an array of promises and returns a single promise that resolves when all of the input promises have resolved.
  • Logical Connections:
    • The get many and get one procedures provide the review data to the ProductList and ProductView components.
  • Conclusion:
    • The reviews are aggregated and displayed in the product cards and product views.
    • A copy URL button is implemented in the product view.

9. Chapter 24: Access Control

  • Objective: Implement access control to restrict access to sensitive data and operations in the CMS.
  • Step 1: Create Access Control Util:
    • Create an access.ts file inside lib.
    • Export an is super admin function that checks if a user has the "super-admin" role.
  • Step 2: Implement Access Control in Users Collection:
    • Restrict create and update operations to super admins.
    • Allow users to update their own profile.
    • Restrict delete operations to super admins.
    • Hide the users collection from non-admin users.
    • Restrict the role field to super admins.
  • Step 3: Implement Access Control in Categories Collection:
    • Restrict all operations (create, read, update, delete) to super admins.
  • Step 4: Implement Access Control in Products Collection:
    • Allow read access to all users.
    • Restrict create access to tenants who have submitted their Stripe details.
  • Step 5: Implement Access Control in Tags Collection:
    • Restrict all operations (create, read, update, delete) to super admins.
  • Step 6: Implement Access Control in Tenants Collection:
    • Restrict create and delete operations to super admins.
    • Allow users to update their own tenant.
    • Restrict the Stripe account ID and Stripe details submitted fields to super admins.
  • Step 7: Implement Access Control in Orders and Reviews Collections:
    • Restrict all operations (create, read, update, delete) to super admins.
  • Step 8: Implement Access Control in Media Collection:
    • Allow read and create access to all users.
    • Restrict delete operations to super admins.
    • Hide the media collection from non-admin users.
  • Step 9: Restrict Purchased Content:
    • Modify the get one and get many procedures in the products module to prevent the content field from being loaded for non-authenticated users.
  • Technical Terms:
    • Access Control: A mechanism for restricting access to sensitive data and operations.
    • Role-Based Access Control (RBAC): A type of access control that assigns permissions based on the user's role.
  • Logical Connections:
    • The is super admin util is used to determine if a user has the "super-admin" role.
    • The access control settings in the collections restrict access to sensitive data and operations.
  • Conclusion:
    • Access control is implemented to restrict access to sensitive data and operations in the CMS.
    • The is super admin util is used to determine if a user has the "super-admin" role.
    • The access control settings in the collections restrict access to sensitive data and operations.

10. Chapter 25: Stripe Connect

  • Objective: Implement Stripe Connect to enable fee sharing.
  • Step 1: Set Up Stripe Connect:
    • Set up Stripe Connect in the Stripe dashboard.
  • Step 2: Modify Register Procedure:
    • Modify the register procedure in src/modules/auth/server/procedures.ts to assign a Stripe account ID to each tenant.
    • Create a Stripe account using stripe.accounts.create.
  • Step 3: Create Verify Procedure:
    • Create a verify procedure in src/modules/checkout/server/procedures.ts.
    • Fetch the user and the tenant.
    • Create an account link using stripe.accountLinks.create.
    • Return the account link URL.
  • Step 4: Add Verify Button to CMS:
    • Create a StripeVerify.tsx component inside src/components.
    • Add a "Verify Account" button that links to the /stripe-verify route.
    • Add the StripeVerify component to the before nav links section in the admin configuration in payload.config.ts.
  • Step 5: Implement Stripe Verify Page:
    • Create a page.tsx file inside app/admin/tenants/stripe-verify.
    • Use useTRPC and useMutation to call the verify procedure.
    • Redirect the user to the account link URL on success.
    • Redirect the user to the root page on error.
  • Step 6: Implement Webhook Route:
    • Add account.updated to the list of permitted events in the webhook route.
    • Handle the account.updated event.
    • Update the stripe details submitted field in the tenants collection.
  • Step 7: Modify Purchase Procedure:
    • Modify the purchase procedure in src/modules/checkout/server/procedures.ts to take a fee percentage.
    • Calculate the platform fee amount.
    • Add the application fee amount and stripe account to the payment intent data.
  • Technical Terms:
    • Stripe Account Link: A URL that allows users to connect their Stripe account to the platform.
    • Application Fee: A fee that the platform takes from each transaction.
  • Logical Connections:
    • The register procedure creates a Stripe account for each tenant.
    • The verify procedure creates an account link that allows tenants to connect their Stripe account to the platform.
    • The webhook route updates the stripe details submitted field in the tenants collection.
    • The purchase procedure takes a fee percentage from each transaction.
  • Conclusion:
    • Stripe Connect is implemented, allowing the platform to take a fee percentage from each transaction.
    • A user-facing Stripe verification page is created.
    • An admin action is added to prompt users to verify their account before creating products.

11. Chapter 26: General Improvements

  • Objective: Implement general improvements to the application, including soft delete, private products, rich text elements, and missing suspense wrappers.
  • Step 1: Implement Soft Delete:
    • Disable the delete option in the products collection for non-admin users.
    • Add an is archived field to the products collection.
    • Modify the get many and get one procedures in the products module to filter out archived products.
  • Step 2: Implement Private Products:
    • Add an is private field to the products collection.
    • Modify the get many procedure in the products module to filter out private products from the public storefront.
  • Step 3: Add Rich Text Elements:
    • Install the @payloadcms/richtext-lexical package.
    • Enable the lexical editor in the payload.config.ts file.
    • Change the content and description fields in the products collection to use the rich text type.
  • Step 4: Add Missing Suspense Wrappers:
    • Identify components that use useSuspenseQuery but are not wrapped in a Suspense component.
    • Wrap those components in a Suspense component and provide a fallback.
  • Technical Terms:
    • Soft Delete: A way to delete data without permanently removing it from the database.
    • Rich Text Element: A text editor that allows users to format text and add media.
    • Suspense Wrapper: A React component that allows components to "wait" for data to load.
  • Logical Connections:
    • The is archived and is private fields are used to filter products in the get many and get one procedures.
    • The rich text type allows users to format text and add media to the content and description fields.
    • The Suspense component allows components to "wait" for data to load.
  • Conclusion:
    • Soft delete and private products are implemented.
    • Rich text elements are added to the content and description fields.
    • Missing suspense wrappers are added to improve the user experience.

12. Chapter 27: Subdomain Rewrites

  • Objective: Implement subdomain rewrites to enable tenant-specific subdomains.
  • Step 1: Add Root Domain to Environment Variables:
    • Add a NEXT_PUBLIC_ROOT_DOMAIN environment variable to the .env file.
  • Step 2: Modify Generate Tenant URL Method:
    • Modify the generateTenantURL method in lib/utils.ts to generate URLs with the tenant slug as a subdomain.
  • Step 3: Implement Middleware:
    • Create a middleware.ts file in the src directory.
    • Implement a middleware function that rewrites requests to the correct tenant-specific subdomain.
  • Step 4: Update Stripe URLs:
    • Update the Stripe success and cancel URLs to use the new subdomain format.
  • Step 5: Add Cookie Settings:
    • Add proper cookie settings to the generateOutCookie method in src/modules/auth/utils.ts.
  • Technical Terms:
    • Subdomain Rewrite: A technique for rewriting URLs to use subdomains.
    • Middleware: A function that intercepts requests and responses.
  • Logical Connections:
    • The generateTenantURL method generates the URLs that are used in the middleware.
    • The middleware rewrites the requests to the correct tenant-specific subdomain.
  • Conclusion:
    • Subdomain rewrites are implemented, allowing tenants to have their own subdomains.
    • The generateTenantURL method is modified to generate URLs with the tenant slug as a subdomain.
    • A middleware function is implemented to rewrite requests to the correct tenant-specific subdomain.

13. Chapter 28: Deployment

  • Objective: Prepare the application for deployment to Versel.
  • Step 1: Run Build:
    • Run bun run build to check for any errors.
  • Step 2: Add Dynamic Force Dynamic:
    • Add export const dynamic = 'force-dynamic' to the page.tsx files in the auth and library modules.
  • Step 3: Deploy to Versel:
    • Deploy the application to Versel.
  • Step 4: Update Environment Variables:
    • Update the environment variables in

AI summaries can miss context or contain errors. Check important details against the original video.

Go a little deeper.

Have a question about this video? Load its transcript to open the video chat.