Build a FullStack Travel Planner App With NextJS, Google Maps, PostgreSQL

PedroTechAbout 10 min readJun 25, 2025Watch original
THE SUMMARYAI-generated

Key Concepts

  • Full Stack Web Development: Building both the front-end (user interface) and back-end (server-side logic and database) of a web application.
  • Next.js: A React framework for building server-rendered and statically generated web applications.
  • Prisma: An Object-Relational Mapper (ORM) for Node.js and TypeScript, simplifying database interactions.
  • Tailwind CSS: A utility-first CSS framework for rapidly designing user interfaces.
  • Neon: A serverless PostgreSQL database platform.
  • NextAuth.js (Next O): An authentication library for Next.js applications.
  • UploadThing: A service for handling file uploads in web applications.
  • React Google Maps: A library for integrating Google Maps into React applications.
  • Radix UI: A set of accessible and unstyled React components.
  • React DND (Drag and Drop): A library for implementing drag-and-drop functionality in React applications.
  • Server Actions: Functions that run on the server in Next.js, allowing for secure data mutations.
  • Prisma Transactions: A mechanism to ensure that multiple database operations are executed atomically.

Project Setup and Initial Configuration

  • Creating a New Next.js App: Using npx create-next-app to scaffold a new Next.js project with TypeScript, ESLint, Tailwind CSS, and the app router.
  • Installing Dependencies: Installing various packages using npm install, including:
    • prisma: For database interactions.
    • next-auth: For user authentication.
    • @next-auth/prisma-adapter: To connect NextAuth.js with Prisma.
    • uploadthing: For image storage.
    • react-google-maps/api: For Google Maps integration.
    • @radix-ui/react-*: For pre-built UI components.
    • @dnd-kit/*: For drag-and-drop functionality.
  • Configuring Tailwind CSS: Defining custom colors and classes in tailwind.config.js using OKLCH color definitions for backgrounds, cards, text, buttons, inputs, and borders.
  • Creating a Navbar Component: Building a navigation bar with a logo, website title, links to "My Trips" and "Globe" pages, and a "Sign In" button.

Authentication Setup with NextAuth.js and Prisma

  • Setting up Neon Database: Creating a new project and database in Neon, a serverless PostgreSQL platform.
  • Prisma Initialization: Running npx prisma init to generate the Prisma schema and client.
  • Connecting to Neon: Copying the connection string from Neon and pasting it into the .env.local file as DATABASE_URL.
  • NextAuth.js Configuration:
    • Creating an auth.ts file at the root of the project.
    • Using the NextAuth function to configure authentication providers (GitHub) and the Prisma adapter.
    • Creating a lib/prisma.ts file to instantiate the Prisma client with proper type definitions.
    • Defining the necessary models (User, Account, Session, VerificationToken) in schema.prisma for NextAuth.js to store user data.
    • Running npx prisma migrate dev to create the corresponding tables in the Neon database.
    • Running npx prisma generate to update the Prisma client with the new models.
  • GitHub OAuth Setup:
    • Creating an OAuth app in GitHub Developer Settings.
    • Obtaining the Client ID and Client Secret.
    • Adding the GITHUB_ID and GITHUB_SECRET to the .env.local file.
    • Generating a NextAuth.js secret using npm exec nextauth secret and adding it to .env.local as NEXTAUTH_SECRET.
  • Creating API Routes for NextAuth.js: Creating the necessary API routes in app/api/auth/[...nextauth]/route.ts to handle authentication requests.
  • Implementing Sign-in and Sign-out Actions:
    • Creating a lib/auth-actions.ts file with login and logout functions.
    • Using the signIn and signOut functions from NextAuth.js.
    • Adding an onClick handler to the "Sign In" button in the Navbar component to call the login function.
    • Using the use client directive to make the Navbar component a client component.
  • Conditional Rendering in Navbar: Using the getSession function to determine if a user is authenticated and conditionally rendering the "Sign In" and "Sign Out" buttons, as well as the "My Trips" and "Globe" links.

Building the Travel Planner App

My Trips Page

  • Creating the Trips Page: Creating a trips/page.tsx file to display the user's planned trips.
  • Authentication Check: Using the getSession function to ensure that only authenticated users can access the page.
  • UI Structure: Building the basic UI structure with a title ("Dashboard"), a "New Trip" button, and a welcome message.
  • Radix UI Components: Importing and using pre-built UI components from Radix UI, such as Button, Card, CardHeader, and CardContent.
  • Fetching Trips from Database: Using Prisma to query the database and retrieve all trips associated with the currently logged-in user.
  • Displaying Trips: Displaying the trips in a grid layout, showing the title, description, start date, and end date for each trip.
  • Conditional Rendering: Displaying a message if the user has no trips planned yet.

New Trip Page

  • Creating the New Trip Page: Creating a trips/new/page.tsx file to allow users to create new trips.
  • Form Structure: Building a form with input fields for the trip title, description, start date, and end date.
  • Date Input: Using <input type="date"> for selecting the start and end dates.
  • Required Fields: Adding the required attribute to all input fields.
  • Create Trip Button: Adding a "Create Trip" button to submit the form.
  • Prisma Schema Update: Adding a Trip model to the schema.prisma file with fields for id, title, description, imageURL, startDate, endDate, userId, createdAt, and updatedAt.
  • Database Migration: Running npx prisma migrate dev to create the Trip table in the Neon database.
  • Prisma Client Generation: Running npx prisma generate to update the Prisma client with the new Trip model.
  • Server Action for Creating Trips:
    • Creating a lib/actions/create-trip.ts file with a createTrip function.
    • Using the use server directive to mark the function as a server action.
    • Extracting data from the form data.
    • Validating the input data.
    • Converting the start and end dates to JavaScript Date objects.
    • Using Prisma to create a new Trip record in the database.
    • Redirecting the user to the trips page after successful creation.
  • Integrating the Server Action:
    • Passing the createTrip function to the action prop of the form.
    • Using the useTransition hook to handle the pending state of the form submission.
    • Disabling the "Create Trip" button while the form is submitting.

Image Uploading with UploadThing

  • UploadThing Account Setup: Creating an account on UploadThing and creating a new app.
  • API Key Retrieval: Obtaining the API key from UploadThing and adding it to the .env.local file.
  • UploadThing API Route:
    • Creating an app/api/uploadthing folder with route.ts and core.ts files.
    • Copying the code from the UploadThing documentation into these files.
  • UploadThing Configuration:
    • Replacing the fake authentication function with the actual getSession function from NextAuth.js.
    • Adding the user ID to the metadata.
  • UploadThing Component:
    • Creating a lib/uploadthing.ts file with the UploadButton and UploadDropzone components.
  • Integrating the UploadButton:
    • Adding the UploadButton component to the new-trip/page.tsx file.
    • Specifying the endpoint as "imageUploader".
    • Implementing the onClientUploadComplete callback to handle the uploaded image URL.
    • Implementing the onUploadError callback to handle upload errors.
  • Image Preview: Displaying a preview of the uploaded image using the next/image component.
  • Next.js Image Configuration: Adding the UploadThing hostname to the remotePatterns array in next.config.js.
  • Passing the Image URL to the Server Action: Appending the image URL to the form data in the new-trip/page.tsx file.
  • Updating the Create Trip Action: Modifying the createTrip function in lib/actions/create-trip.ts to retrieve the image URL from the form data and save it to the database.

Trip Details Page

  • Creating the Trip Details Page: Creating a trips/[tripId]/page.tsx file to display the details of a specific trip.
  • Dynamic Route: Using a dynamic route parameter [tripId] to access the ID of the trip.
  • Server Component: Making the page a server component to fetch the trip data from the database.
  • Authentication Check: Using the getSession function to ensure that only authenticated users can access the page.
  • Fetching Trip Data: Using Prisma to query the database and retrieve the trip data based on the tripId.
  • Client Component: Creating a components/trip-detail.tsx file to build the UI for the trip details page.
  • Passing Data to Client Component: Passing the fetched trip data as props to the TripDetailClient component.
  • UI Structure: Building the UI structure with a banner image, a header card, and tabs for "Overview", "Itinerary", and "Map".
  • Conditional Rendering: Displaying a message if the trip is not found or if the user is not authorized to view the trip.
  • Tabs Component: Using the Tabs component from Radix UI to create the tab navigation.
  • Overview Tab: Displaying the trip summary, dates, and destinations in the overview tab.
  • Map Integration: Integrating Google Maps into the overview tab to display the trip locations.

Location Management

  • Prisma Schema Update: Adding a Location model to the schema.prisma file with fields for id, title, latitude, longitude, tripId, and order.
  • Database Migration: Running npx prisma migrate dev to create the Location table in the Neon database.
  • Prisma Client Generation: Running npx prisma generate to update the Prisma client with the new Location model.
  • Creating the New Location Page: Creating a trips/[tripId]/itinerary/new/page.tsx file to allow users to add new locations to a trip.
  • Form Structure: Building a form with an input field for the location address.
  • Add Location Button: Adding an "Add Location" button to submit the form.
  • Server Action for Adding Locations:
    • Creating a lib/actions/add-location.ts file with an addLocation function.
    • Using the use server directive to mark the function as a server action.
    • Extracting the address from the form data.
    • Using the Google Maps Geocoding API to retrieve the latitude and longitude for the address.
    • Using Prisma to create a new Location record in the database.
    • Redirecting the user to the trip details page after successful creation.
  • Google Maps Geocoding API:
    • Creating a lib/geocode.ts file with a getCountryFromCoordinates function.
    • Using the Google Maps Geocoding API to retrieve the country and formatted address for a given latitude and longitude.
  • Integrating the Server Action:
    • Passing the addLocation function to the action prop of the form.
    • Using the useTransition hook to handle the pending state of the form submission.
    • Disabling the "Add Location" button while the form is submitting.
  • Displaying Locations on the Map:
    • Modifying the TripDetailClient component to fetch the locations associated with the trip.
    • Passing the locations to the Map component.
    • Using the react-google-maps/api library to display the locations on the map.
  • Itinerary Tab:
    • Creating a components/sortable-itinerary.tsx file to display the list of locations in the itinerary tab.
    • Using the react-dnd-kit library to implement drag-and-drop functionality for reordering the locations.
  • Reordering Itinerary:
    • Creating a lib/actions/reorder-itinerary.ts file with a reorderItinerary function.
    • Using the use server directive to mark the function as a server action.
    • Using Prisma transactions to update the order of the locations in the database.
    • Integrating the server action with the SortableItinerary component.

Globe Page

  • Creating the Globe Page: Creating a globe/page.tsx file to display a 3D globe with pins for visited countries.
  • React GlobeGl Library: Using the react-globe.gl library to render the 3D globe.
  • Fetching Visited Countries:
    • Creating an API route app/api/trips/route.ts to fetch the locations of all trips and extract the unique countries.
    • Using the Google Maps Geocoding API to retrieve the country for each location.
  • Displaying Visited Countries: Displaying a list of the visited countries in a card on the globe page.
  • Pinning Countries on the Globe: Using the pointsData prop of the react-globe.gl component to pin the visited countries on the globe.

Conclusion

The video provides a comprehensive guide to building a full-stack travel planner application using a modern tech stack. It covers everything from setting up the project and configuring authentication to implementing complex features like image uploading, Google Maps integration, drag-and-drop functionality, and 3D globe visualization. The video emphasizes best practices, such as using server actions for secure data mutations and Prisma transactions for atomic database operations. By following the steps outlined in the video, developers can gain valuable experience in building real-world web applications with Next.js, Prisma, Tailwind CSS, and other cutting-edge technologies.

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.