{"id":20731864,"url":"https://github.com/lllariogonzalez/nextjs-dashboard","last_synced_at":"2025-05-11T01:31:27.006Z","repository":{"id":205693863,"uuid":"714836157","full_name":"lllariogonzalez/nextjs-dashboard","owner":"lllariogonzalez","description":"Next.js 14 dashboard-app example Log in application using the following credentials:       ~ Email: user@nextmail.com   ~ Password: 123456","archived":false,"fork":false,"pushed_at":"2025-02-05T00:40:43.000Z","size":1519,"stargazers_count":111,"open_issues_count":1,"forks_count":22,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-05T01:29:02.735Z","etag":null,"topics":["hooks","next-navigation","nextauthjs","nextjs14","postgresql","sql","tailwindcss","typescript","vercel"],"latest_commit_sha":null,"homepage":"https://nextjs-dashboard-acme.vercel.app/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lllariogonzalez.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-11-05T23:53:05.000Z","updated_at":"2025-02-05T00:40:46.000Z","dependencies_parsed_at":"2025-02-05T01:24:26.530Z","dependency_job_id":"39a4abf4-1624-49c2-910e-8296357b16ad","html_url":"https://github.com/lllariogonzalez/nextjs-dashboard","commit_stats":null,"previous_names":["lllariogonzalez/nextjs-dashboard"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lllariogonzalez%2Fnextjs-dashboard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lllariogonzalez%2Fnextjs-dashboard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lllariogonzalez%2Fnextjs-dashboard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lllariogonzalez%2Fnextjs-dashboard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lllariogonzalez","download_url":"https://codeload.github.com/lllariogonzalez/nextjs-dashboard/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253504524,"owners_count":21918825,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hooks","next-navigation","nextauthjs","nextjs14","postgresql","sql","tailwindcss","typescript","vercel"],"created_at":"2024-11-17T05:16:54.212Z","updated_at":"2025-05-11T01:31:26.672Z","avatar_url":"https://github.com/lllariogonzalez.png","language":"TypeScript","funding_links":[],"categories":["NextJS"],"sub_categories":["Dashboard"],"readme":"# Acme dashboard full web application.\n\n![Acme full web application](https://nextjs.org/_next/image?url=%2Flearn%2Fcourse-explainer.png\u0026w=750\u0026q=75\u0026dpl=dpl_Ejtt9BCyCFNeRJdBoVsM9Es9x8xe)\n\n## Next.js App Router Course - Starter\n\nThis is the starter template for the Next.js App Router Course. It contains the starting code for the dashboard application.\n\nFor more information, see the [course curriculum](https://nextjs.org/learn) on the Next.js Website.\n\n## Creating a new project\n\n```bash\nnpx create-next-app@latest nextjs-dashboard --use-npm --example \"https://\ngithub.com/vercel/next-learn/tree/main/dashboard/starter-example\"\n\n```\n\nThis command uses create-next-app, a Command Line Interface (CLI) tool that sets up a Next.js application for you. In the command above, you're also using the --example flag with the starter example for this course.\n\n## Folder structure\n\n**/app:** Contains all the routes, components, and logic for your application, this is where you'll be mostly working from.\n**/app/lib:** Contains functions used in your application, such as reusable utility functions and data fetching functions.\n**/app/ui:** Contains all the UI components for your application, such as cards, tables, and forms. To save time, we've pre-styled these components for you.\n**/public:** Contains all the static assets for your application, such as images.\n**/scripts/:** Contains a file that you'll use to populate your database in a later chapter.\n\nConfig Files: You'll also notice config files such as next.config.js at the root of your application. Most of these files are created and pre-configured when you start a new project using create-next-app.\n\n**app/lib/placeholder-data.js:** Each JavaScript object in the file represents a table in your database. For example, for the invoices table:\n\n**/app/lib/definitions.ts:** Here, we manually define the types that will be returned from the database.\n\nBy using **TypeScript**, you can ensure you don't accidentally pass the wrong data format to your components or database, like passing a string to amount instead of a number.\n\n### If you're a TypeScript developer:\n\n- Next.js detects if your project uses TypeScript and automatically installs the necessary packages and configuration. Next.js also comes with a TypeScript plugin for your code editor, to help with auto-completion and type-safety.\n\n- We're manually declaring the data types, but for better type-safety, we recommend tools like Prisma, which automatically generates types based on your database schema.\n\n## Running the development server\n\nRun npm i or npm install to install the project's packages.\n\n```bash\nnpm install\n```\n\nFollowed by npm run dev to start the development server.\n\n```bash\nnpm run dev\n```\n\n```npm run dev``` starts your Next.js development server on port 3000. Let's check to see if it's working. Open http://localhost:3000 on your browser.\n\n## CSS Styling\n\nIn this chapter...\n\nHere are the topics we’ll cover\n\n- How to add a global CSS file to your application.\n\n- Two different ways of styling: Tailwind and CSS modules.\n\n- How to conditionally add class names with the clsx utility package.\n\n### Global styles\n\n**/app/ui:** If you look inside folder, you'll see a file called global.css. You can use this file to add CSS rules to all the routes in your application - such as CSS reset rules, site-wide styles for HTML elements like links, and more.\n\nYou can import global.css in any component in your application, but it's usually good practice to add it to your top-level component. In Next.js, this is the root layout (more on this later).\n\n\u003e **/app/layout.tsx**\n```TypeScript\nimport '@/app/ui/global.css';\n```\n\nIf you take a look inside global.css, you'll notice some @tailwind directives.\n\n```css\n@tailwind base;\n@tailwind components;\n@tailwind utilities;\n```\n\n### Tailwind\n\nTailwind is a CSS framework that speeds up the development process by allowing you to quickly write utility classes directly in your JSX markup. The CSS styles are shared globally, but each utility is singularly focused and applied to each individual element. This means you don't have to worry about style collisions, maintaining separate stylesheets, or your CSS bundle size growing as your application grows.\n\nWhen you use create-next-app to start a new project, Next.js will ask if you want to use Tailwind. If you select yes, it will automatically install the necessary packages and configure Tailwind in your application.\n\nIf you look at /app/page.tsx, you'll see that we're using Tailwind classes in the example.\n\n```tsx\n\u003cdiv className=\"h-0 w-0 border-b-[30px] border-l-[20px] border-r-[20px] border-b-black border-l-transparent border-r-transparent\"/\u003e\n```\n\n### CSS Modules\n\nIf you prefer writing traditional CSS rules or keeping your styles separate from your JSX, CSS Modules are a great alternative.\n\nCSS Modules allow you to scope CSS to a component by automatically creating unique class names, so you don't have to worry about name collisions.\n\nHere's how you could create the same shape from the quiz above using CSS modules.\n\n\u003e **app/ui/home.module.css**\n```css\n.shape {\n  height: 0;\n  width: 0;\n  border-bottom: 30px solid black;\n  border-left: 20px solid transparent;\n  border-right: 20px solid transparent;\n}\n```\n\n\u003e Provide a way to make CSS classes locally scoped to components by default, enabling better modularity and reducing the risk of styling conflicts.\n\n### Library clsx\n\nThere may be cases where you may need to conditionally style an element based on state or some other condition.\n\n- Suppose that you want to create an InvoiceStatus component which accepts status. The status can be 'pending' or 'paid'.\n- If it's 'paid', you want the color to be green. If it's 'pending', you want the color to be gray.\n\nYou can use clsx to conditionally apply the classes, like this:\n\n```tsx\nimport clsx from 'clsx';\n \nexport default function InvoiceStatus({ status }: { status: string }) {\n  return (\n    \u003cspan\n      className={clsx(\n        'inline-flex items-center rounded-full px-2 py-1 text-sm',\n        {\n          'bg-gray-100 text-gray-500': status === 'pending',\n          'bg-green-500 text-white': status === 'paid',\n        },\n      )}\n    \u003e\n    // ...\n)}\n```\n\n## Optimizing Fonts and Images\n\nIn the previous chapter, you learned how to style your Next.js application. Let's continue working on your home page by adding a custom font and a hero image.\n\nIn this chapter...\n\nHere are the topics we’ll cover\n\n- ✏️ How to add custom fonts with next/font.\n\n- 🛣️ How to add images with next/image.\n\n- ✅ How fonts and images are optimized in Next.js.\n\n\n### Why optimize fonts?\n\nFonts play a significant role in the design of a website, but using custom fonts in your project can affect performance if the font files need to be fetched and loaded.\n\nCumulative Layout Shift is a metric used by Google to evaluate the performance and user experience of a website. With fonts, layout shift happens when the browser initially renders text in a fallback or system font and then swaps it out for a custom font once it has loaded. This swap can cause the text size, spacing, or layout to change, shifting elements around it.\n\n![Cumulative Layout Shift](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Ffont-layout-shift.png\u0026w=1920\u0026q=75\u0026dpl=dpl_9EKEbD7jAviauyTffgoEyAkQSGtP)\n\nNext.js automatically optimizes fonts in the application when you use the next/font module. It does so by downloading font files at build time and hosting them with your other static assets. This means when a user visits your application, there are no additional network requests for fonts which would impact performance.\n\n### Adding a primary font\n\nIn your /app/ui folder, create a new file called fonts.ts. You'll use this file to keep the fonts that will be used throughout your application.\n\nImport the Inter font from the next/font/google module - this will be your primary font.\nThen, specify what subset you'd like to load. In this case, 'latin':\n\n```tsx\nimport { Inter } from 'next/font/google';\n\nexport const inter = Inter({ subsets: ['latin'] });\n```\n\nFinally, add the font to the \u003cbody\u003e element in /app/layout.tsx\n\nBy adding Inter to the \u003cbody\u003e element, the font will be applied throughout your application. Here, you're also adding the Tailwind antialiased class which smooths out the font. It's not necessary to use this class, but it adds a nice touch to your fonts.\n\n```css\n.antialiased {\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n}\n```\n\n```tsx\n\u003cbody className={`${inter.className} antialiased`}\u003e{children}\u003c/body\u003e\n```\n\n\u003e You can also add fonts to specific elements of your application.\n\n---\n\n### Why optimize images?\n\nNext.js can serve static assets, like images, under the top-level /public folder. Files inside /public can be referenced in your application.\n\nIf you look inside the folder, you'll see there's are two images: hero-desktop.png and hero-mobile.png. These two images are completely different, and they'll be shown depending on the user's device is a desktop or mobile.\n\nWith regular HTML, you would add an image as follows:\n\n```tsx\n    \u003cimg src=\"/hero.png\" alt=\"Screenshots of the dashboard project showing desktop and mobile versions\"/\u003e\n```\n\nHowever, this means you have to manually:\n\n- Ensure your image is responsive on different screen sizes.\n- Specify image sizes for different devices.\n- Prevent layout shift as the images load.\n- Lazy load images that are outside the user's viewport.\n\nInstead of manually handling these optimizations, you can use the ```next/image``` component to automatically optimize your images.\n\n### The \\\u003cImage\\\u003e component\n\nThe \\\u003cImage\\\u003e Component is an extension of the HTML \\\u003cimg\\\u003e tag, and comes with automatic image optimization, such as:\n\n- Preventing layout shift automatically when images are loading.\n- Resizing images to avoid shipping large images to devices with a smaller viewport.\n- Lazy loading images by default (images load as they enter the viewport).\n- Serving images in modern formats, like WebP and AVIF, when the browser supports it.\n\n### Adding the desktop hero image\n\nLet's swap the \\\u003cimg\\\u003e tag for an \\\u003cImage\\\u003e component.\n\nIn your /app/page.tsx file, import the component from next/image. Then, add the image under the comment:\n\n```tsx\n    \u003cImage\n        src=\"/hero-desktop.png\"\n        width={1000}\n        height={760}\n        className=\"hidden md:block\"\n        alt=\"Screenshots of the dashboard project showing desktop and mobile versions\"\n    /\u003e\n```\n\nHere, you're setting the width to 1000 and height to 760 pixels. It's good practice to set the width and height of your images to avoid layout shift, these should be an aspect ratio identical to the source image.\n\n### Adding the mobile hero image\n\nNow it's your turn again! Under the image you've just added, add another \u003cImage\u003e component for the mobile hero.\n\n- The image should have a width of 560 and height of 620 pixels.\n- It should be shown on mobile screens, and hidden on desktop.\n- You can use Dev Tools to check if the desktop and mobile images are swapped correctly.\n\n```tsx\n    \u003cImage\n        src=\"/hero-mobile.png\"\n        width={560}\n        height={620}\n        className=\"block md:hidden\"\n        alt=\"Screenshot of the dashboard project showing mobile version\"\n    /\u003e\n```\n\n### Recommended reading\n\nThere's a lot more to learn about these topics, including optimizing remote images and using local font files. If you'd like to dive deeper into fonts and images, see:\n\n- [Image Optimization Docs](https://nextjs.org/docs/app/building-your-application/optimizing/images)\n- [Font Optimization Docs](https://nextjs.org/docs/app/building-your-application/optimizing/fonts)\n- [Improving Web Performance with Images (MDN)](https://developer.mozilla.org/en-US/docs/Learn/Performance/Multimedia)\n- [Web Fonts (MDN)](https://developer.mozilla.org/en-US/docs/Learn/CSS/Styling_text/Web_fonts)\n\n\n---\n\n## Creating Layouts and Pages\n\nSo far, your application only has a home page. Let's learn how you can create more routes with layouts and pages.\n\nIn this chapter...\n\nHere are the topics we’ll cover\n\n- Create the /login and dashboard pages using file-system routing.\n\n- Understand the role of folders and files when creating new route segments.\n\n- Create a layout that can be shared between multiple dashboard pages.\n\n- Understand what colocation, partial rendering, and root layout are.\n\n### Nested routing\n\nNext.js uses **file-system routing** where folders are used to create nested routes. Each folder represents a route segment that maps to a URL segment.\n\n![Nested routing](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Ffolders-to-url-segments.png\u0026w=1920\u0026q=75\u0026dpl=dpl_9EKEbD7jAviauyTffgoEyAkQSGtP)\n\npage.tsx is a special Next.js file that exports a React component containing the UI for the route. In your application, you already have a page file: /app/page.tsx - this is the home page which is associated with the route /.\n\nTo create a nested route, you can nest folders inside each other with their own page.tsx files. For example:\n\n![Nest folders inside](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Flogin-route.png\u0026w=1920\u0026q=75\u0026dpl=dpl_9EKEbD7jAviauyTffgoEyAkQSGtP)\n\n/app/login/page.tsx is associated with the /login path. Let's create the page to see how it works!\n\n### Creating the dashboard page\n\nCreate a new folder called dashboard inside /app. Then, create a new page.tsx file inside the dashboard folder with the following content:\n\n```tsx\nexport default function Page() {\n  return \u003cp\u003eDashboard Page\u003c/p\u003e;\n}\n```\nNow, make sure that the development server is running and visit http://localhost:3000/dashboard.\n\nLet's practice creating more routes. In your dashboard, create two more pages:\n\n- Customers Page: The page should be accessible on http://localhost:3000/dashboard/customers. For now, it should return a \\\u003cp\\\u003eCustomers Page\\\u003c/p\\\u003e element.\n\n- Invoices Page: The invoices page should be accessible on http://localhost:3000/dashboard/invoices. For now, also return a \\\u003cp\\\u003eInvoices Page\\\u003c/p\\\u003e element.\n\nYou should have the following folder structure:\n\n![folder structure](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Frouting-solution.png\u0026w=1920\u0026q=75\u0026dpl=dpl_9EKEbD7jAviauyTffgoEyAkQSGtP)\n\n\n### Creating the dashboard layout\n\nDashboards also have some sort of navigation that is shared across multiple pages. In Next.js, you can use a special layout.tsx file to create UI that is shared between multiple pages. Let's create a layout for the dashboard!\n\nInside the /dashboard folder, add a new file called layout.tsx and paste the following code:\n\n```tsx\nimport SideNav from '@/app/ui/dashboard/sidenav';\n \nexport default function Layout({ children }: { children: React.ReactNode }) {\n  return (\n    \u003cdiv className=\"flex h-screen flex-col md:flex-row md:overflow-hidden\"\u003e\n      \u003cdiv className=\"w-full flex-none md:w-64\"\u003e\n        \u003cSideNav /\u003e\n      \u003c/div\u003e\n      \u003cdiv className=\"flex-grow p-6 md:overflow-y-auto md:p-12\"\u003e{children}\u003c/div\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\nOne benefit of using layout is that on navigation, only the page components update while the layout won't re-render. In Next.js, this is called partial rendering:\n\n![Partial rendering](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Fpartial-rendering-dashboard.png\u0026w=1920\u0026q=75\u0026dpl=dpl_9EKEbD7jAviauyTffgoEyAkQSGtP)\n\nThis layout is required and is called a root layout. Any UI you add to the root layout will be shared across all pages in your application. You can use the root layout to modify your \\\u003chtml\\\u003e and \\\u003cbody\\\u003e tags, and add metadata (you'll learn more about metadata in a later chapter).\n\nSince the new layout you've just created (/app/dashboard/layout.tsx) is unique to the dashboard pages, you don't need to add any UI to the root layout above.\n\n\n## Navigating Between Pages\n\nIn the previous chapter, you created the dashboard layout and pages. Now, let's add some links to allow users to navigate between the dashboard pages.\n\nIn this chapter...\n\nHere are the topics we’ll cover\n\n- How to use the next/link component.\n\n- How to show an active link with the usePathname() hook.\n\n- How client-side navigation works in Next.js.\n\n### Why optimize navigation?\n\nTo link between pages, you'd traditionally use the \\\u003ca\\\u003e HTML element. At the moment, the sidebar links use \\\u003ca\\\u003e elements, but notice what happens when you navigate between the home, invoices, and customers pages on your browser.\n\nDid you see it?\n\nThere's a full page refresh on each page navigation!\n\n#### The \\\u003cLink\\\u003e component\n\nIn Next.js, you can use the Link Component to link between pages in your application. \u003cLink\u003e allows you to do client-side navigation with JavaScript. Although parts of your application are rendered on the server, navigation is faster and there's no full page refresh - making it feel more like a web app.\n\n#### Pattern: Showing active links\n\nA common UI pattern is to show an active link to indicate to the user what page they are currently on. To do this, you need to get the user's current path from the URL. Next.js provides a hook called usePathname() that you can use to check the path.\n\nSince [usePathname()](https://nextjs.org/docs/app/api-reference/functions/use-pathname) is a hook, you'll need to turn nav-links.tsx into a Client Component. Add React's ```\"use client\" ``` directive to the top of the file, then import usePathname() from next/navigation:\n\n### Automatic code-splitting and prefetching\n\nIn addition to client-side navigation, Next.js automatically code splits your application by route segments. This is different from a traditional SPA, where the browser loads all your application code on initial load.\n\nSplitting code by routes means that pages become isolated. If a certain page throws an error, the rest of the application will still work.\n\nFurthermore, in production, whenever \\\u003cLink\\\u003e components appear in the browser's viewport, Next.js automatically prefetches the code for the linked route in the background. By the time the user clicks the link, the code for the destination page will already be loaded in the background, and the page transition will be near-instant!\n\n---\n\n## Setting Up Your Database\n\nBefore you can continue working on your dashboard, you'll need some data. In this chapter, you'll be setting up a PostgreSQL database using @vercel/postgres. If you're already familiar with PostgreSQL and would prefer to use your own provider, you can skip this chapter and set it up on your own. Otherwise, let's continue!\n\nIn this chapter...\n\nHere are the topics we’ll cover\n\n- Create and link your project to a Postgres database.\n\n- Seed the database with initial data.\n\n### Create a Postgres database\n\nNext, to set up a database, click the Storage tab from your Vercel dashboard. Select Connect Store → Create New → Postgres → Continue.\n\n![Vercel Postgres](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Fcreate-database.png\u0026w=1080\u0026q=75\u0026dpl=dpl_9qQQdh4D2Yn6grRGdiVN5fQKpqX7)\n\nOnce connected, copy the contents from the .env.local tab on the database page.\n\n![env.local](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Fdatabase-dashboard.png\u0026w=1080\u0026q=75\u0026dpl=dpl_9qQQdh4D2Yn6grRGdiVN5fQKpqX7)\n\nNavigate to your code editor and rename the .env.example file to .env. Paste in the copied contents from Vercel.\n\nImportant: Go to your .gitignore file and make sure .env are in the ignored files to prevent your database secrets from being exposed on GitHub.\n\nFinally, run:\n```bash \nnpm i @vercel/postgres\n```\nin your terminal to install the Vercel Postgres SDK.\n\n### Seed your database\n\nNow that your database has been created, let's seed it with some initial data. This will allow you to have some data to work with as you build the dashboard.\n\nIn the ```/scripts``` folder of your project, there's a file called seed.js. This script contains the instructions for creating and seeding the invoices, customers, user, revenue tables.\n\n```json\n\"scripts\": {\n  \"build\": \"next build\",\n  \"dev\": \"next dev\",\n  \"start\": \"next start\",\n  \"seed\": \"node -r dotenv/config ./scripts/seed.js\"\n},\n```\n\n-r : require\n\nThis is the command that will execute the seed.js. Before we can run the command, we must first npm i bcrypt, which is used to hash user passwords.\n\nNow, run npm run seed. You should see some console.log messages in your terminal to let you know the script is running.\n\n\u003e Troubleshooting:\n\u003e\n\u003e If you run into any issues while seeding your database and want to run the script again, you can drop any existing tables by running DROP TABLE tablename in your database query interface. See the executing queries section below for more details.\n\u003e\n\u003e This command will delete the tables and all their data. But be careful, it's ok to do this with your example app since you're working with placeholder data, but you shouldn't run this command in a production app.\n\u003e\n\u003e If you continue to experience issues while seeding your Verce Postgres database, please open a discussion on GitHub.\n\n### Exploring your database\n\nLet's see what your database looks like. Go back to Vercel, and click Data in the sidenav.\n\nIn this section, you'll find the four new tables: users, customers, invoices, revenue.\n\n![Data postgres Vercel](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Fdatabase-tables.png\u0026w=1080\u0026q=75\u0026dpl=dpl_9qQQdh4D2Yn6grRGdiVN5fQKpqX7)\n\n### Executing queries\n\nYou can switch to the \"query\" tab to interact with your database. This section supports standard SQL commands. For instance, inputting DROP TABLE customers will delete \"customers\" table along with all its data - so be careful!\n\n---\n\n## Fetching Data\n\nLet's discuss the different ways you can fetch data from your database, including using SQL and alternatives.\n\nNow that you've created and seeded your database, let's discuss the different ways you can fetch data for your application, and choose the most appropriate one for the dashboard overview page.\n\nIn this chapter...\n\nHere are the topics we’ll cover\n\n- Learn about some approaches to fetching data: APIs, ORMs, SQL, etc.\n\n- How Server Components help us access our back-end resources more securely.\n\n- What network waterfalls are.\n\n- How to implement parallel data fetching using a JavaScript Pattern.\n\n### Choosing how to fetch data\n\n#### API layer\nAPIs are an intermediary layer between your application code and database. There are a few cases where you might use an API:\n\nIf you're using 3rd party services that provide an API.\nIf you're fetching data from the client, you want to have an API layer that runs on the server to avoid exposing your database secrets to the client.\nIn Next.js, you can create API endpoints using Route Handlers.\n\n#### Database queries\nWhen you're creating a full-stack application, you'll also need to write logic to interact with your database. For relational databases like Postgres, you can do this with SQL, or an ORM like Prisma.\n\nThere are a few cases where you have to write database queries:\n\nWhen creating your API endpoints, you need to write logic to interact with your database.\nIf you are using React Server Components (fetching data on the server), you can skip the API layer, and query your database directly without risking exposing your database secrets to the client.\n\nThere are a few other ways you can fetch data with React and Next.js. We won't cover all of them due to time. If you'd like to learn more, check out the Data Fetching docs.\n\nIn the next section, we'll explore how you can fetch data using a relatively new approach: async React Server Components.\n\n### Using Server Components to fetch data\n\nBy default, Next.js applications use React Server Components, and you can opt into Client Components when needed. There are a few benefits to fetching data with React Server Components:\n\nServer Components execute on the server, so you can keep expensive data fetches and logic on the server and only send the result to the client.\nServer Components support promises, providing a simpler solution for asynchronous tasks like data fetching. You can use async/await syntax without reaching out for useEffect, useState or data fetching libraries.\nSince Server Components execute on the server, you can query the database directly without an additional API layer.\n\n### Using SQL\n\nFor your dashboard project, you'll write database queries using the Vercel Postgres SDK and SQL. There are a few reasons why we'll be using SQL:\n\n- SQL is the industry standard for querying relational databases (e.g. ORMs generate SQL under the hood).\n\n- Having a basic understanding of SQL can help you understand the fundamentals of relational databases, allowing you to apply your knowledge to other tools.\n\n- SQL is versatile, allowing you to fetch and manipulate specific data.\n\n- The Vercel Postgres SDK provides protection against SQL injections.\n\n\u003e You can call sql inside any Server Component. But to allow you to navigate the components more easily, we've kept all the data queries in the data.ts file, and you can import them into the components.\n\n## Fetching data for the dashboard overview page\n\n```tsx\nimport { Card } from '@/app/ui/dashboard/cards';\nimport RevenueChart from '@/app/ui/dashboard/revenue-chart';\nimport LatestInvoices from '@/app/ui/dashboard/latest-invoices';\nimport { lusitana } from '@/app/ui/fonts';\n \nexport default async function Page() {\n  return (\n    \u003cmain\u003e\n      \u003ch1 className={`${lusitana.className} mb-4 text-xl md:text-2xl`}\u003e\n        Dashboard\n      \u003c/h1\u003e\n      \u003cdiv className=\"grid gap-6 sm:grid-cols-2 lg:grid-cols-4\"\u003e\n        {/* \u003cCard title=\"Collected\" value={totalPaidInvoices} type=\"collected\" /\u003e */}\n        {/* \u003cCard title=\"Pending\" value={totalPendingInvoices} type=\"pending\" /\u003e */}\n        {/* \u003cCard title=\"Total Invoices\" value={numberOfInvoices} type=\"invoices\" /\u003e */}\n        {/* \u003cCard\n          title=\"Total Customers\"\n          value={numberOfCustomers}\n          type=\"customers\"\n        /\u003e */}\n      \u003c/div\u003e\n      \u003cdiv className=\"mt-6 grid grid-cols-1 gap-6 md:grid-cols-4 lg:grid-cols-8\"\u003e\n        {/* \u003cRevenueChart revenue={revenue}  /\u003e */}\n        {/* \u003cLatestInvoices latestInvoices={latestInvoices} /\u003e */}\n      \u003c/div\u003e\n    \u003c/main\u003e\n  );\n}\n```\n\nIn the code above:\n\nPage is an async component. This allows you to use await to fetch data.\nThere are also 3 components which receive data: \\\u003cCard\\\u003e, \\\u003cRevenueChart\\\u003e, and \\\u003cLatestInvoices\\\u003e. They are currently commented out to prevent the application from erroring.\n\n### Fetching data for \\\u003cRevenueChart/\\\u003e\n\nTo fetch data for the \\\u003cRevenueChart/\\\u003e component, import the fetchRevenue function from data.ts and call it inside your component\n\n### Fetching data for \\\u003cLatestInvoices/\\\u003e\n\nFor the \u003cLatestInvoices /\u003e component, we need to get the latest 5 invoices, sorted by date.\n\nYou could fetch all the invoices and sort through them using JavaScript. This isn't a problem as our data is small, but as your application grows, it can significantly increase the amount of data transferred on each request and the JavaScript required to sort through it.\n\nInstead of sorting through the latest invoices in-memory, you can use an SQL query to fetch only the last 5 invoices.\n\nFor example, this is the SQL query from your data.ts file:\n\n```tsx\n// Fetch the last 5 invoices, sorted by date\nconst data = await sql\u003cLatestInvoiceRaw\u003e`\n  SELECT invoices.amount, customers.name, customers.image_url, customers.email\n  FROM invoices\n  JOIN customers ON invoices.customer_id = customers.id\n  ORDER BY invoices.date DESC\n  LIMIT 5`;\n```\n\n### Fetch data for the \\\u003cCard\\\u003e components\n\nNow it's your turn to fetch data for the \\\u003cCard\\\u003e components. The cards will display the following data:\n\n- Total amount of invoices collected.\n- Total amount of invoices pending.\n- Total number of invoices.\n- Total number of customers.\n\nAgain, you might be tempted to fetch all the invoices and customers, and use JavaScript to manipulate the data.\n\n```tsx\nconst totalInvoices = allInvoices.length;\nconst totalCustomers = allCustomers.length;\n```\n\nBut with SQL, you can fetch only the data you need. It's a little longer than using Array.length, but it means less data needs to be transferred during the request. This is the SQL alternative:\n\n\u003e /app/lib/data.ts\n```tsx\nconst invoiceCountPromise = sql`SELECT COUNT(*) FROM invoices`;\nconst customerCountPromise = sql`SELECT COUNT(*) FROM customers`;\n```\n\nThe function you will need to import is called fetchCardData. You will need to destructure the values returned from the function.\n\nHint:\n\n- Check the card components to see what data they need.\n- Check the data.ts file to see what the function returns.\n\nGreat! You've now fetched all the data for the dashboard overview page. Your page should look like this:\n\n![Dashboard Data Fetching](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Fcomplete-dashboard.png\u0026w=1080\u0026q=75\u0026dpl=dpl_9qQQdh4D2Yn6grRGdiVN5fQKpqX7)\n\n\nHowever... there are two things you need to be aware of:\n\n1. The data requests are unintentionally blocking each other, creating a request waterfall.\n\n2. By default, Next.js prerenders routes to improve performance, this is called Static Rendering. So if your data changes, it won't be reflected in your dashboard.\n\n## Request Waterfalls\n\nWhat are request waterfalls?\n\nA \"waterfall\" refers to a sequence of network requests that depend on the completion of previous requests. In the case of data fetching, each request can only begin once the previous request has returned data.\n\nFor example, we need to wait for fetchRevenue() to execute before fetchLatestInvoices() can start running, and so on.\n\nThis pattern is not necessarily bad. There may be cases where you want waterfalls because you want a condition to be satisfied before you make the next request. For example, you might want to fetch a user's ID and profile information first. Once you have the ID, you might then proceed to fetch their list of friends. In this case, each request is contingent on the data returned from the previous request.\n\nHowever, this behavior can also be unintentional and impact performance.\n\n## Parallel data fetching\n\nA common way to avoid waterfalls is to initiate all data requests at the same time - in parallel.\n\nIn JavaScript, you can use the Promise.all() or Promise.allSettled() functions to initiate all promises at the same time. For example, in data.ts, we're using Promise.all() in the fetchCardData() function.\n\n\u003e Good to know:\n\u003e\n\u003e With Promise.allSettled(), you can also return an array of objects with status and value keys, so can check a promise's status is fulfilled or rejected before passing the value to your component. It's useful if you want to handle errors more gracefully.\n\nBy using this pattern, you can:\n\n- Start executing all data fetches at the same time, which can lead to performance gains.\n- Use a native JavaScript pattern that can be applied to any library or framework.\n\nHowever, there is one disadvantage of using this JavaScript pattern: what happens if one data request is slower than all the others?\n\n---\n\n## Static and Dynamic Rendering\n\nLearn about how you can use to further optimize data fetching with PPR and Streaming.\n\n\u003e ⚠️ This chapter contains Next.js 14 experimental features that are subject to change. The content may be updated as the features are finalized.\n\nIn the previous chapter, you fetched data for the Dashboard Overview page. However, we briefly discussed two limitations of the current setup:\n\n1. The data requests are creating an unintentional waterfall.\n2. The dashboard is static, so any data updates will not be reflected on your application.\n\nIn this chapter...\n\nHere are the topics we’ll cover\n\n- What static rendering is and how it can improve your application's performance.\n\n- What dynamic rendering is and when to use it.\n\n- Different approaches to make your dashboard dynamic.\n\n- The limitation of fetching data at request time.\n\n### What is Static Rendering?\n\nWith static rendering, data fetching and rendering happens on the server at build time (when you deploy) or during revalidation. The result can then be distributed and cached (stored) in a Content Delivery Network (CDN).\n\n![CDN](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Fstatic-site-generation.png\u0026w=1920\u0026q=75\u0026dpl=dpl_7RpAeq75qgcdEqm8L4GS5sRJ7t3C)\n\nWhenever a user visits your application, the cached result is served. There are a couple of benefits of static rendering:\n\nFaster Websites - Prerendered content can be cached. This ensures that users around the world can access your website's content more quickly and reliably.\nReduced Server Load - Because the content is cached, your server does not have to dynamically generate content for each user request.\nSEO - Prerendered content is easier for search engine crawlers to index, as the content is already available when the page loads. This can lead to improved search engine rankings.\nStatic rendering is useful for UI with no data or data that is shared across users, such as a static blog post or a product page. It might not be a good fit for a dashboard that has data that is regularly updated.\n\nThe opposite of static rendering is dynamic rendering.\n\n### What is Dynamic Rendering?\n\nWith dynamic rendering, content is rendered on the server for each user at request time (when the user visits the page). There are a couple of benefits of dynamic rendering:\n\n- Real-Time Data - Dynamic rendering allows your application to display real-time or frequently updated data. This is ideal for applications where data changes often.\n\n- User-Specific Content - It's easier to serve user-specific content, such as personalized dashboards or user profiles, through dynamic rendering, as the data is updated based on user interaction.\n\n- Request Time Information - Dynamic rendering allows you to access information that can only be known at request time, such as cookies or the URL search parameters.\n\n## Making the dashboard dynamic\n\nBy default, @vercel/postgres doesn't set its own caching semantics. This allows the framework to set its own static and dynamic behavior.\n\nYou can use a Next.js API called unstable_noStore inside your Server Components or data fetching functions to opt out of static rendering. Let's add this.\n\nIn your data.ts, import unstable_noStore from next/cache, and call it the top of your data fetching functions:\n\n```tsx\n// ...\nimport { unstable_noStore as noStore } from 'next/cache';\n \nexport async function fetchRevenue() {\n  // Add noStore() here to prevent the response from being cached.\n  // This is equivalent to in fetch(..., {cache: 'no-store'}).\n  noStore();\n \n  // ...\n}\n```\n\n\u003e Note: unstable_noStore is an experimental API and may change in the future. If you prefer to use a stable API in your own projects, you can also use the Segment Config Option export const dynamic = \"force-dynamic\".\n\n### Simulating a Slow Data Fetch\n\nMaking the dashboard dynamic is a good first step. However... there is still one problem we mentioned in the previous chapter. What happens if one data request is slower than all the others?\n\nLet's simulate a slow data fetch to see what happens. In your data.ts file, uncomment the console.log and setTimeout inside fetchRevenue():\n\n```tsx\nexport async function fetchRevenue() {\n  try {\n    // We artificially delay a response for demo purposes.\n    // Don't do this in a real application\n    console.log('Fetching revenue data...');\n    await new Promise((resolve) =\u003e setTimeout(resolve, 3000));\n \n    const data = await sql\u003cRevenue\u003e`SELECT * FROM revenue`;\n \n    console.log('Data fetch complete after 3 seconds.');\n \n    return data.rows;\n  } catch (error) {\n    console.error('Database Error:', error);\n    throw new Error('Failed to fetch revenue data.');\n  }\n}\n```\n\nHere, you've added an artificial 3-second delay to simulate a slow data fetch. The result is that now your whole page is blocked while the data is being fetched.\n\n![data fetch blocking](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Fsequential-parallel-data-fetching.png\u0026w=1920\u0026q=75\u0026dpl=dpl_7RpAeq75qgcdEqm8L4GS5sRJ7t3C)\n\nWhich brings us to a common challenge developers have to solve:\n\nWith dynamic rendering, your application is only as fast as your slowest data fetch.\n\n## Streaming\n\nIn the previous chapter, you made your dashboard page dynamic, however, we discussed how the slow data fetches can impact the performance of your application. Let's look at how you can improve the user experience when there are slow data requests.\n\nIn this chapter...\n\nHere are the topics we’ll cover\n\n- What streaming is and when you might use it.\n\n- How to implement streaming with loading.tsx and Suspense.\n\n- What loading skeletons are.\n\n- What route groups are, and when you might use them.\n\n- Where to place Suspense boundaries in your application.\n\n### What is streaming?\n\nStreaming is a data transfer technique that allows you to break down a route into smaller \"chunks\" and progressively stream them from the server to the client as they become ready.\n\nBy streaming, you can prevent slow data requests from blocking your whole page. This allows the user to see and interact with parts of the page without waiting for all the data to load before any UI can be shown to the user.\n\nData fetching and rendering are initiated in parallel, so the user can see the UI as it becomes ready. This is different from the traditional waterfall approach, where data fetching and rendering are initiated sequentially, blocking the UI from rendering until all the data is ready.\n\n![Project example streaming](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Fserver-rendering-with-streaming-chart.png\u0026w=1920\u0026q=75\u0026dpl=dpl_7RpAeq75qgcdEqm8L4GS5sRJ7t3C)\n\nStreaming works well with React's component model, as each component can be considered a chunk.\n\nThere are two ways you implement streaming in Next.js:\n\n1. At the page level, with the loading.tsx file.\n2. For specific components, with \\\u003cSuspense\\\u003e.\n\nLet's see how this works.\n\nA few things are happening here:\n\n1. loading.tsx is a special Next.js file built on top of Suspense, it allows you to create loading UI to show as a replacement while page content loads.\n2. Since \u003cSidebar\u003e is static, it's shown immediately. The user can interact with \u003cSidebar\u003e while the dynamic content is loading.\n3. The user doesn't have to wait for the page to finish loading before navigating away (this is called interruptable navigation).\n\nCongratulations! You've just implemented Streaming with Next.js!\n\nBut we can do more to improve the user experience. Let's show a skeleton instead of the Loading… text.\n\n### Adding loading skeletons\nA loading skeleton is a simplified version of the UI. Many websites use them as a placeholder (or fallback) to indicate to users that the content is loading. Any UI you embed into loading.tsx will be embedded as part of the static file, and sent first. Then, the rest of the dynamic content will be streamed in from the server to the client.\n\nInside your loading.tsx file, import a new component called \\\u003cDashboardSkeleton\\\u003e\n\n![Dashboard skeleton](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Floading-page-with-skeleton.png\u0026w=1080\u0026q=75\u0026dpl=dpl_7RpAeq75qgcdEqm8L4GS5sRJ7t3C)\n\n### Fixing the loading skeleton bug with route groups\n\nRight now, your loading skeleton will apply to the invoices and customers pages as well.\n\nSince loading.tsx is a level higher than /invoices/page.tsx and /customers/page.tsx in the file system, it's also applied to those pages.\n\nWe can change this with Route Groups. Create a new folder called /(overview) inside the dashboard folder. Then, move your loading.tsx and page.tsx files inside the folder:\n\n![Route groups /(overview)](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Froute-group.png\u0026w=1920\u0026q=75\u0026dpl=dpl_7RpAeq75qgcdEqm8L4GS5sRJ7t3C)\n\nNow, the loading.tsx file will only apply to your dashboard overview page.\n\nRoute groups allow you to organize files into logical groups without affecting the URL path structure. When you create a new folder using parentheses (), the name won't be included in the URL path. So /dashboard/(overview)/page.tsx becomes /dashboard.\n\nHere, you're using a route group to ensure loading.tsx only applies to your dashboard overview page. However, you can also use route groups to separate your application into sections (e.g. (marketing) routes and (dashboard) routes) or by teams for larger applications.\n\n## Streaming a component\n\nSo far, you're streaming a whole page. But, instead, you can be more granular and stream specific components using React Suspense.\n\nSuspense allows you to defer rendering parts of your application until some condition is met (e.g. data is loaded). You can wrap your dynamic components in Suspense. Then, pass it a fallback component to show while the dynamic component loads.\n\nIf you remember the slow data request, fetchRevenue(), this is the request that is slowing down the whole page. Instead of blocking your page, you can use Suspense to stream only this component and immediately show the rest of the page's UI.\n\nTo do so, you'll need to move the data fetch to the component.\n\nFinally, update the \\\u003cRevenueChart\\\u003e component to fetch its own data and remove the prop passed to it.\n\nNow refresh the page, you should see the dashboard information almost immediately, while a fallback skeleton is shown for \\\u003cRevenueChart\\\u003e\n\n![Dashboard skeleton Streaming](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Floading-revenue-chart.png\u0026w=1080\u0026q=75\u0026dpl=dpl_7RpAeq75qgcdEqm8L4GS5sRJ7t3C)\n\nPractice: Streaming \\\u003cLatestInvoices\\\u003e\nNow it's your turn! Practice what you've just learned by streaming the \\\u003cLatestInvoices\\\u003e component.\n\nMove fetchLatestInvoices() down from the page to the \\\u003cLatestInvoices\\\u003e component. Wrap the component in a \\\u003cSuspense\\\u003e boundary with a fallback called \\\u003cLatestInvoicesSkeleton\\\u003e.\n\n## Grouping components\n\nGreat! You're almost there, now you need to wrap the \\\u003cCard\\\u003e components in Suspense. You can fetch data for each individual card, but this could lead to a popping effect as the cards load in, this can be visually jarring for the user.\n\n\u003e So, how would you tackle this problem?\n\nTo create more of a staggered effect, you can group the cards using a wrapper component. This means the static \\\u003cSidebar/\\\u003e will be shown first, followed by the cards, etc.\n\nIn your page.tsx file:\n\n1. Delete your \\\u003cCard\\\u003e components.\n2. Delete the fetchCardData() function.\n3. Import a new wrapper component called \\\u003cCardWrapper /\\\u003e.\n4. Wrap \\\u003cCardWrapper /\\\u003e in Suspense.\n\n### Deciding where to place your Suspense boundaries\n\nWhere you place your Suspense boundaries will depend on a few things:\n\n1. How you want the user to experience the page as it streams.\n2. What content you want to prioritize.\n3. If the components rely on data fetching.\n\nTake a look at your dashboard page, is there anything you would've done differently?\n\n- You could stream the whole page like we did with loading.tsx... but that may lead to a longer loading time if one of the components has a slow data fetch.\n- You could stream every component individually... but that may lead to UI popping into the screen as it becomes ready.\n- You could also create a staggered effect by streaming page sections. But you'll need to create wrapper components.\n\nWhere you place your suspense boundaries will vary depending on your application. In general, it's good practice to move your data fetches down to the components that need it, and then wrap those components in Suspense. But there is nothing wrong with streaming the sections or the whole page if that's what your application needs.\n\nDon't be afraid to experiment with Suspense and see what works best, it's a powerful API that can help you create more delightful user experiences.\n\n---\n\n## Partial Prerendering (Optional)\n\nPartial Prerendering is an experimental feature introduced in Next.js 14. The content of this page may be updated as the feature progresses in stability. You may want to skip this chapter if you prefer to not use experimental features. This chapter is not required to complete the course.\n\nIn this chapter...\n\nHere are the topics we’ll cover\n\n- What Partial Prerendering is.\n\n- How Partial Prerendering works.\n\n### Combining Static and Dynamic Content\n\nCurrently, if you call a dynamic function inside your route (e.g. noStore(), cookies(), etc), your whole route becomes dynamic.\n\nThis aligns with how most web apps are built today, you either choose between static and dynamic rendering for your entire application or for specific routes.\n\nHowever, most routes are not fully static or dynamic. You may have a route that has both static and dynamic content. For example, let's say you have a social media feed, the posts would be static, but the likes for the post would be dynamic. Or an ecommerce site, where the product details are static, but the user's cart is dynamic.\n\nGoing back to your dashboard page, what components would you consider static vs. dynamic?\n\n![Dashboard Combining Static and Dynamic Content](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Fdashboard-static-dynamic-components.png\u0026w=1920\u0026q=75\u0026dpl=dpl_7RpAeq75qgcdEqm8L4GS5sRJ7t3C)\n\n### What is Partial Prerendering?\n\nIn Next.js 14, there is a preview of a new compiler optimization called Partial Prerendering. Partial Prerendering is an experimental feature that allows you to render a route with a static loading shell, while keeping some parts dynamic. In other words, you can isolate the dynamic parts of a route.\n\n![Example partial prerendering](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Fthinking-in-ppr.png\u0026w=1920\u0026q=75\u0026dpl=dpl_7RpAeq75qgcdEqm8L4GS5sRJ7t3C)\n\nWhen a user visits a route:\n\n- A static route shell is served, this makes the initial load fast.\n- The shell leaves holes where dynamic content will load in async.\n- The async holes is loaded in parallel, reducing the overall load time of the page.\n\nThis is different from how your application behaves today, where entire routes are either fully static or dynamic.\n\n### How does Partial Prerendering work?\n\nPartial Prerendering leverages React's Concurrent APIs and uses Suspense to defer rendering parts of your application until some condition is met (e.g. data is loaded).\n\nThe fallback is embedded into the initial static file along with other static content. At build time (or during revalidation), the static parts of the route are prerendered, and the rest is postponed until the user requests the route.\n\nThe great thing about Partial Prerendering is that you don't need to change your code to use it. As long as you're using Suspense to wrap the dynamic parts of your route, Next.js will know which parts of your route are static and which are dynamic.\n\n## Summary\n\nTo recap, you've done a few things to optimize data fetching in your application, you've:\n\n1. Created a database in the same region as your application code to reduce latency between your server and database.\n2. Fetched data on the server with React Server Components. This allows you to keep expensive data fetches and logic on the server, reduces the client-side JavaScript bundle, and prevents your database secrets from being exposed to the client.\n3. Used SQL to only fetch the data you needed, reducing the amount of data transferred for each request and the amount of JavaScript needed to transform the data in-memory.\n4. Parallelize data fetching with JavaScript - where it made sense to do so.\n5. Implemented Streaming to prevent slow data requests from blocking your whole page, and to allow the user to start interacting with the UI without waiting for everything to load.\n6. Move data fetching down to the components that need it, thus isolating which parts of your routes should be dynamic in preparation for Partial Prerendering.\n\n---\n\n## Adding Search and Pagination\n\nIn the previous chapter, you improved your dashboard's initial loading performance with streaming. Now let's move on to the /invoices page, and learn how to add search and pagination!\n\nIn this chapter...\n\nHere are the topics we’ll cover\n\n- Learn how to use the Next.js APIs: searchParams, usePathname, and useRouter.\n\n- Implement search and pagination using URL search params.\n\n### /dashboard/invoices/page.tsx\n\nSpend some time familiarizing yourself with the page and the components you'll be working with:\n\n\\\u003cSearch/\\\u003e allows users to search for specific invoices.\n\\\u003cPagination/\\\u003e allows users to navigate between pages of invoices.\n\\\u003cTable/\\\u003e displays the invoices.\n\nYour search functionality will span the client and the server. When a user searches for an invoice on the client, the URL params will be updated, data will be fetched on the server, and the table will re-render on the server with the new data.\n\n### Why use URL search params?\n\nAs mentioned above, you'll be using URL search params to manage the search state. This pattern may be new if you're used to doing it with client side state.\n\nThere are a couple of benefits of implementing search with URL params:\n\n- Bookmarkable and Shareable URLs: Since the search parameters are in the URL, users can bookmark the current state of the application, including their search queries and filters, for future reference or sharing.\n- Server-Side Rendering and Initial Load: URL parameters can be directly consumed on the server to render the initial state, making it easier to handle server rendering.\n- Analytics and Tracking: Having search queries and filters directly in the URL makes it easier to track user behavior without requiring additional client-side logic.\n\n### Adding the search functionality\n\nThere are three Next.js client hooks that you'll use to implement the search functionality:\n\n- **useSearchParams** Allows you to access the parameters of the current URL. For example, the search params for this URL /dashboard/invoices?page=1\u0026query=pending would look like this: {page: '1', query: 'pending'}.\n\n- **usePathname** - Lets you read the current URL's pathname. For example, the route /dashboard/invoices, usePathname would return '/dashboard/invoices'.\n\n- **useRouter** - Enables navigation between routes within client components programmatically. There are multiple methods you can use.\n\nHere's a quick overview of the implementation steps:\n\n1. Capture the user's input.\n2. Update the URL with the search params.\n3. Keep the URL in sync with the input field.\n4. Update the table to reflect the search query.\n\n1. Capture the user's input\n\nGo into the \\\u003cSearch\\\u003e Component (/app/ui/search.tsx), and you'll notice:\n\n\"use client\" - This is a Client Component, which means you can use event listeners and hooks.\n\\\u003cinput\\\u003e - This is the search input.\nCreate a new handleSearch function, and add an onChange listener to the \\\u003cinput\\\u003e element.\n\nonChange will invoke handleSearch whenever the input value changes.\n\n2. Update the URL with the search params\n\nImport the useSearchParams hook from 'next/navigation', and assign it to a variable.\n\nInside handleSearch, create a new URLSearchParams instance using your new searchParams variable.\n\nURLSearchParams is a Web API that provides utility methods for manipulating the URL query parameters. Instead of creating a complex string literal, you can use it to get the params string like ?page=1\u0026query=a.\n\nNext, set the params string based on the user’s input. If the input is empty, you want to delete it.\n\nNow that you have the query string. You can use Next.js's useRouter and usePathname hooks to update the URL.\n\nImport useRouter and usePathname from 'next/navigation', and use the replace method from userRouter() inside handleSearch.\n\nHere's a breakdown of what's happening:\n\n`${pathname}` is the current path, in your case, `\"/dashboard/invoices\".`\nAs the user types into the search bar, `params.toString()` translates this input into a URL-friendly format.\nThe replace`(${pathname}?${params.toString()});` command updates the URL with the user's search data. For example, `/dashboard/invoices?query=lee` if the user searches for \"lee\".\nThe URL is updated without reloading the page, thanks to Next.js's client-side navigation\n\n```tsx\n'use client';\n\nimport { MagnifyingGlassIcon } from '@heroicons/react/24/outline';\nimport { useSearchParams, usePathname, useRouter } from 'next/navigation';\n\nexport default function Search({ placeholder }: { placeholder: string }) {\n  const searchParams = useSearchParams();\n  const pathname = usePathname();\n  const { replace } = useRouter();\n\n  const handleSearch = (e: React.ChangeEvent\u003cHTMLInputElement\u003e)=\u003e{\n    const params = new URLSearchParams(searchParams)\n    const term: string = e.target.value\n    if (term) {\n      params.set('query', term);\n    } else {\n      params.delete('query');\n    }\n    replace(`${pathname}?${params.toString()}`);\n  }\n  //...\n}\n```\n\n3. Keeping the URL and input in sync\n\nTo ensure the input field is in sync with the URL and will be populated when sharing, you can pass a defaultValue to input by reading from searchParams.\n\n```tsx\n\u003cinput\n  className=\"peer block w-full rounded-md border border-gray-200 py-[9px] pl-10 text-sm outline-2 placeholder:text-gray-500\"\n  placeholder={placeholder}\n  onChange={(e) =\u003e {\n    handleSearch(e.target.value);\n  }}\n  defaultValue={searchParams.get('query')?.toString()}\n/\u003e\n```\n\n\u003e defaultValue vs. value / Controlled vs. Uncontrolled\n\u003e\n\u003eIf you're using state to manage the value of an input, you'd use the value attribute to make it a controlled component. This means React would manage the input's state.\n\u003e\n\u003eHowever, since you're not using state, you can use defaultValue. This means the native input will manage its own state. This is okay since you're saving the search query to the URL instead of state.\n\n4. Updating the table\n\nFinally, you need to update the table component to reflect the search query.\n\nNavigate back to the invoices page.\n\nPage components accept a prop called searchParams, so you can pass the current URL params to the \\\u003cTable\\\u003e component.\n\nIf you navigate to the \\\u003cTable\\\u003e Component, you'll see that the two props, query and currentPage, are passed to the fetchFilteredInvoices() function which returns the invoices that match the query.\n\n\u003e ❓ When to use the useSearchParams() hook vs. the searchParams prop?\n\u003e\n\u003eYou might have noticed you used two different ways to extract search params. Whether you use one or the other depends on whether you're working on the client or the server.\n\u003e\n\u003e\\\u003cSearch\\\u003e is a Client Component, so you used the useSearchParams() hook to access the params from the client.\n\u003e\n\u003e\\\u003cTable\\\u003e is a Server Component that fetches its own data, so you can pass the searchParams prop from the page to the component.\n\u003e\n\u003eAs a general rule, if you want to read the params from the client, use the useSearchParams() hook as this avoids having to go back to the server.\n\n\n### Best practice: Debouncing\n\nCongratulations! You've implemented search with Next.js! But there's something you can do to optimize it.\n\nYou're updating the URL on every keystroke, and therefore querying your database on every keystroke! This isn't a problem as our application is small, but imagine if your application had thousands of users, each sending a new request to your database on each keystroke.\n\nDebouncing is a programming practice that limits the rate at which a function can fire. In our case, you only want to query the database when the user has stopped typing.\n\nHow Debouncing Works:\n\n1. Trigger Event: When an event that should be debounced (like a keystroke in the search box) occurs, a timer starts.\n2. Wait: If a new event occurs before the timer expires, the timer is reset.\n3. Execution: If the timer reaches the end of its countdown, the debounced function is executed.\n\nYou can implement debouncing in a few ways, including manually creating your own debounce function. To keep things simple, we'll use a library called use-debounce.\n\n```bash\nnpm i use-debounce\n```\n\nIn your \\\u003cSearch\\\u003e Component, import a function called useDebouncedCallback\nThis function will wrap the contents of handleSearch, and only run the code after a specific time once the user has stopped typing (300ms).\n\nBy debouncing, you can reduce the number of requests sent to your database, thus saving resources. 🖥️\n\n## Adding pagination\n\nAfter introducing the search feature, you'll notice the table displays only 6 invoices at a time. This is because the fetchFilteredInvoices() function in data.ts returns a maximum of 6 invoices per page.\n\nAdding pagination allows users to navigate through the different pages to view all the invoices. Let's see how you can implement pagination using URL params, just like you did with search.\n\nNavigate to the \\\u003cPagination/\\\u003e component and you'll notice that it's a Client Component. You don't want to fetch data on the client as this would expose your database secrets (remember, you're not using an API layer). Instead, you can fetch the data on the server, and pass it to the component as a prop.\n\nIn /dashboard/invoices/page.tsx, import a new function called fetchInvoicesPages and pass the query from searchParams as an argument.\n\nfetchInvoicesPages returns the total number of pages based on the search query. For example, if there are 12 invoices that match the search query, and each page displays 6 invoices, then the total number of pages would be 2.\n\nNext, pass the totalPages prop to the \\\u003cPagination/\\\u003e component.\nImport the usePathname and useSearchParams hooks. We will use this to get the current page and set the new page.\n\nHere's a breakdown of what's happening:\n\n- createPageURL creates an instance of the current search parameters.\n- Then, it updates the \"page\" parameter to the provided page number.\n- Finally, it constructs the full URL using the pathname and updated search parameters.\n\nFinally, when the user types a new search query, you want to reset the page number to 1. You can do this by updating the handleSearch function in your \\\u003cSearch\\\u003e component.\n\n### Summary\n\nCongratulations! You've just implemented search and pagination using URL Params and Next.js APIs.\n\nTo summarize, in this chapter:\n\n- You've handled search and pagination with URL search parameters instead of client state.\n- You've fetched data on the server.\n- You're using the useRouter router hook for smoother, client-side transitions.\n\nThese patterns are different from what you may be used to when working with client-side React, but hopefully, you now better understand the benefits of using URL search params and lifting this state to the server.\n\n---\n\n## Mutating Data\n\nLearn how to mutate data with Server Actions.\n\nIn the previous chapter, you implemented search and pagination using URL Search Params and Next.js APIs. Let's continue working on the Invoices page by adding the ability to create, update, and delete invoices!\n\nIn this chapter...\n\nHere are the topics we’ll cover\n\n- What React Server Actions are and how to use them to mutate data.\n\n- How to work with forms and Server Components.\n\n- Best practices for working with the native formData object, including type validation.\n\n- How to revalidate the client cache using the revalidatePath API.\n\n- How to create dynamic route segments with specific IDs.\n\n- How to use the React’s useFormStatus hook for optimistic updates.\n\n### What are Server Actions?\n\nReact Server Actions allow you to run asynchronous code directly on the server. They eliminate the need to create API endpoints to mutate your data. Instead, you write asynchronous functions that execute on the server and can be invoked from your Client or Server Components.\n\nSecurity is a top priority for web applications, as they can be vulnerable to various threats. This is where Server Actions come in. They offer an effective security solution, protecting against different types of attacks, securing your data, and ensuring authorized access. Server Actions achieve this through techniques like POST requests, encrypted closures, strict input checks, error message hashing, and host restrictions, all working together to significantly enhance your app's safety.\n\n### Using forms with Server Actions\n\nIn React, you can use the action attribute in the \\\u003cform\\\u003e element to invoke actions. The action will automatically receive the native formData object, containing the captured data.\n\nFor example:\n\n```tsx\n// Server Component\nexport default function Page() {\n  // Action\n  async function create(formData: FormData) {\n    'use server';\n \n    // Logic to mutate data...\n  }\n \n  // Invoke the action using the \"action\" attribute\n  return \u003cform action={create}\u003e...\u003c/form\u003e;\n}\n```\n\nAn advantage of invoking a Server Action within a Server Component is progressive enhancement - forms work even if JavaScript is disabled on the client.\n\n### Next.js with Server Actions\n\nServer Actions are also deeply integrated with Next.js caching. When a form is submitted through a Server Action, not only can you use the action to mutate data, but you can also revalidate the associated cache using APIs like **revalidatePath** and **revalidateTag.**\n\nLet's see how it all works together!\n\nCreating an invoice\nHere are the steps you'll take to create a new invoice:\n\n1. Create a form to capture the user's input.\n2. Create a Server Action and invoke it from the form.\n3. Inside your Server Action, extract the data from the formData object.\n4. Validate and prepare the data to be inserted into your database.\n5. Insert the data and handle any errors.\n6. Revalidate the cache and redirect the user back to invoices page.\n\n1. Create a new route and form\n\nTo start, inside the /invoices folder, add a new route segment called /create with a page.tsx file:\n\n```bash\n└── invoices\n    └── create\n        └── page.tsx\n```\n\nYour page is a Server Component that fetches customers and passes it to the \\\u003cForm\\\u003e component.\n\nNavigate to the \\\u003cForm\\\u003e component, and you'll see that the form:\n\n- Has two \\\u003cselect\\\u003e (dropdown) elements: customers and status.\n- Has one \\\u003cinput\\\u003e element for the amount with type=\"number\".\n- Has one button with type=\"submit\".\n\n![Form Invoices create](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Fcreate-invoice-page.png\u0026w=1080\u0026q=75\u0026dpl=dpl_FQk1vqJFzbvQf7ciyg5D7QiENdN4)\n\n2. Create a Server Action\n\nGreat, now let's create a Server Action that is going to be called when the form is submitted.\n\nNavigate to your lib directory and create a new file named actions.ts. At the top of this file, add the React use server directive.\n\nBy adding the 'use server', you mark all the exported functions within the file as server functions. These server functions can then be imported into Client and Server components, making them extremely versatile.\n\nYou can also write Server Actions directly inside Server Components by adding \"use server\" inside the action. But for this course, we'll keep them all organized in a separate file.\n\nIn your actions.ts file, create a new async function that accepts formData:\n\n```ts\n'use server';\n \nexport async function createInvoice(formData: FormData) {}\n```\n\nThen, in your \\\u003cForm\\\u003e component, import the createInvoice from your actions.ts file. Add a action attribute to the \\\u003cform\\\u003e element, and call the createInvoice action.\n\n\u003eGood to know: In HTML, you'd pass a URL to the action attribute. This URL would be the destination where your form data should be submitted (usually an API endpoint).\n\u003e\n\u003eHowever, in React, the action attribute is considered a special prop - meaning React builds on top of it to allow actions to be invoked. Rather than calling an API explicitly, you can pass a function to action.\n\u003e\n\u003eBehind the scenes, Server Actions create a POST API endpoint. This is why you don't need to create API endpoints manually when using Server Actions.\n\n3. Extract the data from formData\n\nBack in your actions.ts file, you'll need to extract the values of formData, there are a couple of methods you can use. For this example, let's use the .get(name) method.\n\n```ts\n'use server';\n \nexport async function createInvoice(formData: FormData) {\n  const rawFormData = {\n    customerId: formData.get('customerId'),\n    amount: formData.get('amount'),\n    status: formData.get('status'),\n  };\n  // Test it out:\n  console.log(rawFormData);\n}\n```\n\n\u003eTip: If you're working with forms that have many fields, you may want to consider using the entries() method with JavaScript's Object.fromEntries(). For example:\n\u003e\n\u003econst rawFormData = Object.fromEntries(formData.entries())\n\nTo check everything is connected correctly, go ahead and try out the form. After submitting, you should see the data you just entered into the form logged in your terminal.\n\nNow that your data is in the shape of an object, it'll be much easier to work with.\n\n```bash\n{\n  customerId: 'cc27c14a-0acf-4f4a-a6c9-d45682c144b9',\n  amount: '1000',\n  status: 'paid'\n}\n```\n\n4. Validate and prepare the data\n\nBefore sending the form data to your database, you want to ensure it's in the correct format and with the correct types. If you remember from earlier in the course, your invoices table expects data in the following format:\n\n```ts\nexport type Invoice = {\n  id: string; // Will be created on the database\n  customer_id: string;\n  amount: number; // Stored in cents\n  status: 'pending' | 'paid';\n  date: string;\n};\n```\n\nSo far, you only have the customer_id, amount, and status from the form.\n\n### Type validation and coercion\n\nIt's important to validate that the data from your form aligns with the expected types in your database. For instance, if you add a console.log inside your action...\n\nYou'll notice that amount is of type string and not number. This is because input elements with type=\"number\" actually return a string, not a number!\n\nTo handle type validation, you have a few options. While you can manually validate types, using a type validation library can save you time and effort. For your example, we'll use **Zod**, a TypeScript-first validation library that can simplify this task for you.\n\nIn your actions.ts file, import Zod and define a schema that matches the shape of your form object. This schema will validate the formData before saving it to a database.\n\n```ts\nimport { z } from 'zod';\n \nconst InvoiceSchema = z.object({\n  id: z.string(),\n  customerId: z.string(),\n  amount: z.coerce.number(),\n  status: z.enum(['pending', 'paid']),\n  date: z.string(),\n});\n\nconst CreateInvoice = InvoiceSchema.omit({ id: true, date: true });\n```\n\nThe amount field is specifically set to coerce (change) from a string to a number while also validating its type.\n\nYou can then pass your rawFormData to NewInvoice to validate the types:\n\n**Storing values in cents**\n\nIt's usually good practice to store monetary values in cents in your database to eliminate JavaScript floating-point errors and ensure greater accuracy.\n\nLet's convert the amount into cents:\n\n`const amountInCents = amount * 100;`\n\n**Creating new dates**\n\nFinally, let's create a new date with the format \"YYYY-MM-DD\" for the invoice's creation date:\n\n`const date = new Date().toISOString().split('T')[0];`\n\n5. Inserting the data into your database\n\nNow that you have all the values you need for your database, you can create an SQL query to insert the new invoice into your database and pass in the variables:\n\n```ts\nimport { sql } from '@vercel/postgres';\n// ...\nawait sql`\n    INSERT INTO invoices (customer_id, amount, status, date)\n    VALUES (${customerId}, ${amountInCents}, ${status}, ${date})\n  `;\n```\n\nRight now, we're not handling any errors. We'll do it in the next chapter. For now, let's move on to the next step.\n\n6. Revalidate and redirect\n\nNext.js has a **Client-side Router Cache** that stores the route segments in the user's browser for a time. Along with prefetching, this cache ensures that users can quickly navigate between routes while reducing the number of requests made to the server.\n\nSince you're updating the data displayed in the invoices route, you want to clear this cache and trigger a new request to the server. You can do this with the **revalidatePath** function from Next.js:\n\n```ts\nimport { revalidatePath } from 'next/cache';\n//...\n  revalidatePath('/dashboard/invoices');\n```\n\nOnce the database has been updated, the /dashboard/invoices path will be revalidated, and fresh data will be fetched from the server.\n\nAt this point, you also want to redirect the user back to the /dashboard/invoices page. You can do this with the redirect function from Next.js:\n\n```ts\nimport { redirect } from 'next/navigation';\n// ...\n  redirect('/dashboard/invoices');\n```\n\nCongratulations! You've just implemented your first Server Action. Test it out by adding a new invoice, if everything is working correctly:\n\n1. You should be redirected to the /dashboard/invoices route on submission.\n2. You should see the new invoice at the top of the table.\n\n### Updating an invoice\n\nThe updating invoice form is similar to the create an invoice form, except you'll need to pass the invoice id to update the record in your database. Let's see how you can get and pass the invoice id.\n\nThese are the steps you'll take to update an invoice:\n\n1. Create a new dynamic route segment with the invoice id.\n2. Read the invoice id from the page params.\n3. Fetch the specific invoice from your database.\n4. Pre-populate the form with the invoice data.\n5. Update the invoice data in your database.\n\u003e\n1. Create a Dynamic Route Segment with the invoice id\nNext.js allows you to create Dynamic Route Segments when you don't know the exact segment name and want to create routes based on data. This could be blog post titles, product pages, etc. You can create dynamic route segments by wrapping a folder's name in square brackets. For example, [id], [post] or [slug].\n\nIn your /invoices folder, create a new dynamic route called [id], then a new route called edit with a page.tsx file.\n\nIn your \\\u003cTable\\\u003e component, notice there's a \\\u003cUpdateInvoice /\\\u003e button that receives the invoice's id from the table records.\n\nNavigate to your \\\u003cUpdateInvoice /\\\u003e component, and update the href of the Link to accept the id prop. You can use template literals to link to a dynamic route segment.\n\n2. Read the invoice id from page params\n\nNotice how it's similar to your /create invoice page, except it imports a different form (from the edit-form.tsx file). This form should be pre-populated with a defaultValue for the customer's name, invoice amount, and status. To pre-populate the form fields, you need to fetch the specific invoice using id.\n\nIn addition to searchParams, page components also accept a prop called params which you can use to access the id. Update your \\\u003cPage\\\u003e component to receive the prop.\n\n3. Fetch the specific invoice\nThen:\n\n- Import a new function called fetchInvoiceById and pass the id as an argument\n- Import fetchCustomers to fetch the customer names for the dropdown.\n\nYou can use Promise.all to fetch both the invoice and customers in parallel.\n\nAfter navigation, you should see a form that is pre-populated with the invoice details:\n\n![Update Invoice](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Fedit-invoice-page.png\u0026w=1080\u0026q=75\u0026dpl=dpl_FQk1vqJFzbvQf7ciyg5D7QiENdN4)\n\nThe URL should also be updated with an id as follows: http://localhost:3000/dashboard/invoice/uuid/edit\n\n\u003e**UUIDs vs. Auto-incrementing Keys**\n\u003e\n\u003eWe use UUIDs instead of incrementing keys (e.g., 1, 2, 3, etc.). This makes the URL longer; however, UUIDs eliminate the risk of ID collision, are globally unique, and reduce the risk of enumeration attacks - making them ideal for large databases.\n\u003e\n\u003eHowever, if you prefer cleaner URLs, you might prefer to use auto-incrementing keys.\n\n4. Pass the id to the Server Action\n\nLastly, you want to pass the id to the Server Action so you can update the right record in your database. \nYou can pass id to the Server Action using JS bind. This will ensure that any values passed to the Server Action are encoded.\n\n\u003e **Note:** Using a hidden input field in your form also works (e.g. \\\u003cinput type=\"hidden\" name=\"id\" value={invoice.id} /\\\u003e). However, the values will appear as full text in the HTML source, which is not ideal for sensitive data like IDs.\n\n5. Update the invoice data in your database.\n\nThen, in your actions.ts file, create a new action, updateInvoice\n\n```ts\n// Use Zod to update the expected types\nconst UpdateInvoice = InvoiceSchema.omit({ date: true });\n \n// ...\n \nexport async function updateInvoice(id: string, formData: FormData) {\n  const { customerId, amount, status } = UpdateInvoice.parse({\n    customerId: formData.get('customerId'),\n    amount: formData.get('amount'),\n    status: formData.get('status'),\n  });\n \n  const amountInCents = amount * 100;\n \n  await sql`\n    UPDATE invoices\n    SET customer_id = ${customerId}, amount = ${amountInCents}, status = ${status}\n    WHERE id = ${id}\n  `;\n \n  revalidatePath('/dashboard/invoices');\n  redirect('/dashboard/invoices');\n}\n```\n\nSimilarly to the createInvoice action, here you are:\n\n1. Extracting the data from formData.\n2. Validating the types with Zod.\n3. Converting the amount to cents.\n3. Passing the variables to your SQL query.\n4. Calling revalidatePath to clear the client cache and make a new server request.\n5. Calling redirect to redirect the user to the invoice's page.\n\n## Deleting an invoice\n\nTo delete an invoice using a Server Action, wrap the delete button in a \u003cform\u003e element and pass the id to the Server Action using bind:\n\n\n```ts\nimport { deleteInvoice } from '@/app/lib/actions';\n// ...\n  const deleteInvoiceWithId = deleteInvoice.bind(null, id);\n```\n\nInside your actions.ts file, create a new action called deleteInvoice.\n\n```ts\nexport async function deleteInvoice(id: string) {\n  await sql`DELETE FROM invoices WHERE id = ${id}`;\n  revalidatePath('/dashboard/invoices');\n}\n```\n\nSince this action is being called in the /dashboard/invoices path, you don't need to call redirect. Calling revalidatePath will trigger a new server request and re-render the table.\n\n**Further reading**\nIn this chapter, you learned how to use Server Actions to mutate data. You also learned how to use the revalidatePath API to revalidate the Next.js cache and redirect to redirect the user to a new page.\n\nYou can also read more about security with Server Actions.\n\n---\n\n## Handling Errors\n\nLet's explore best practices for mutating data with forms, including error handling and accessibility.\n\nIn the previous chapter, you learned how to mutate data using Server Actions. Let's see how you can handle errors gracefully using JavaScript's try/catch statements and Next.js APIs.\n\nIn this chapter...\n\nHere are the topics we’ll cover\n\n- How to use the special error.tsx file to catch errors in your route segments, and show a fallback UI to the user.\n\n- How to use the notFound function and not-found file to handle 404 errors (for resources that don’t exist).\n\n\n### Adding try/catch to Server Actions\n\nFirst, let's add JavaScript's try/catch statements to your Server Actions to allow you to handle errors gracefully.\n\nNote how redirect is being called outside of the try/catch block. This is because redirect works by throwing an error, which would be caught by the catch block. To avoid this, you can call redirect after try/catch. redirect would only be reachable if try is successful.\n\n```ts\nexport async function deleteInvoice(id: string) {\n  try {\n    await sql`DELETE FROM invoices WHERE id = ${id}`;\n    revalidatePath('/dashboard/invoices');\n    return { message: 'Deleted Invoice.' };\n  } catch (error) {\n    return { message: 'Database Error: Failed to Delete Invoice.' };\n  }\n}\n```\n\nNow, let's check what happens when an error is thrown in your Server Action. You can do this by throwing an error earlier. For example, in the deleteInvoice action, throw an error at the top of the function:\n\n```ts\nexport async function deleteInvoice(id: string, formData: FormData) {\n  throw new Error('Failed to Delete Invoice');\n  //...\n```\n\nWhen you try to delete an invoice, you should see an error on localhost.\n\nSeeing these errors are helpful while developing as you can catch any potential problems early. However, you also want to show errors to the user to avoid an abrupt failure and allow your application to continue running.\n\nThis is where Next.js error.tsx file comes in.\n\n### Handling all errors with error.tsx\n\nThe error.tsx file can be used to define a UI boundary for a route segment. It serves as a catch-all for unexpected errors and allows you to display a fallback UI to your users.\n\nInside your /dashboard/invoices folder, create a new file called error.tsx\n\nThere are a few things you'll notice about the code above:\n\n- \"use client\" - error.tsx needs to be a Client Component.\n- It accepts two props:\n  - error: This object is an instance of JavaScript's native Error object.\n  - reset: This is a function to reset the error boundary. When executed, the function will try to re-render the route segment.\n\nWhen you try to delete an invoice again, you should see the following UI:\n\n![Error handling delete invoice](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Ferror-page.png\u0026w=1080\u0026q=75\u0026dpl=dpl_Dr17aKMkaNBEHortDHFuuK6oJKA5)\n\n\n### Handling 404 errors with the notFound function\n\nAnother way you can handle errors gracefully is by using the notFound function. While error.tsx is useful for catching all errors, notFound can be used when you try to fetch a resource that doesn't exist.\n\nFor example, visit http://localhost:3000/dashboard/invoices/2e94d1ed-d220-449f-9f11-f0bbceed9645/edit.\n\nThis is a fake UUID that doesn't exist in your database.\n\nYou'll immediately see error.tsx kicks in because this is a child route of /invoices where error.tsx is defined.\n\nHowever, if you want to be more specific, you can show a 404 error to tell the user the resource they're trying to access hasn't been found.\n\nNow that you know the invoice doesn't exist in your database, let's use notFound to handle it. Navigate to /dashboard/invoices/[id]/edit/page.tsx, and import { notFound } from 'next/navigation'.\n\n```ts\nimport { notFound } from 'next/navigation';\n//...\n  if (!invoice) {\n      notFound();\n    }\n```\n\nPerfect! \\\u003cPage\\\u003e will now throw an error if a specific invoice is not found. To show an error UI to the user. Create a not-found.tsx file inside the /edit folder.\n\nRefresh the route, and you should now see the following UI:\n\n![Not Found page](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2F404-not-found-page.png\u0026w=1080\u0026q=75\u0026dpl=dpl_Dr17aKMkaNBEHortDHFuuK6oJKA5)\n\n\nThat's something to keep in mind, notFound will take precedence over error.tsx, so you can reach out for it when you want to handle more specific errors!\n\nFurther reading\nTo learn more about error handling in Next.js, check out the following documentation:\n\n- [Error Handling](https://nextjs.org/docs/app/building-your-application/routing/error-handling)\n- [error.js API Reference](https://nextjs.org/docs/app/api-reference/file-conventions/error)\n- [notFound() API Reference](https://nextjs.org/docs/app/api-reference/functions/not-found)\n- [not-found.js API Reference](https://nextjs.org/docs/app/api-reference/file-conventions/not-found)\n\n---\n\n## Improving Accessibility\n\nLet's continue exploring ways to improve your user's experience. You'll learn about server-side form validation and improving accessibility.\n\nIn the previous chapter, we looked at how to catch errors (including 404 errors) and display a fallback to the user. However, we still need to discuss another piece of the puzzle: form validation. Let's see how to implement server-side validation with Server Actions, and how you can show form errors using **useFormState** hook - while keeping accessibility in mind!\n\nIn this chapter...\n\nHere are the topics we’ll cover\n\n- How to use eslint-plugin-jsx-a11y with Next.js to implement accessibility best practices.\n\n- How to implement server-side form validation.\n\n- How to use the React useFormState hook to handle form errors, and display them to the user.\n\n### What is accessibility?\n\nAccessibility refers to designing and implementing web applications that everyone can use, including those with disabilities. It's a vast topic that covers many areas, such as keyboard navigation, semantic HTML, images, colors, videos, etc.\n\nWhile we won't go in-depth into accessibility in this course, we'll discuss the accessibility features available in Next.js and some common practices to make your applications more accessible.\n\n### Using the ESLint accessibility plugin in Next.js\n\nBy default, Next.js includes the eslint-plugin-jsx-a11y plugin to help catch accessibility issues early. For example, this plugin warns if you have images without alt text, use the aria-* and role attributes incorrectly, and more.\n\nLet's see how this works!\n\nAdd next lint as a script in your package.json file:\n\n```json\n\"scripts\": {\n    \"build\": \"next build\",\n    \"dev\": \"next dev\",\n    \"seed\": \"node -r dotenv/config ./scripts/seed.js\",\n    \"start\": \"next start\",\n    \"lint\": \"next lint\"\n}\n```\n\nThen run npm run lint in your terminal, and you should get the next message:\n\n```bash\nnpm run lint\n\n\u003e lint\n\u003e next lint\n\n✔ No ESLint warnings or errors\n\n```\n\nThere are a couple of rules when using aria attributes, and if you're new to accessibility, it can be tricky to know if you're using them correctly. You can use this plugin to help you along the way.\n\n### Improving form accessibility\n\nThere are three things we're already doing to improve accessibility in our forms:\n\n- Semantic HTML: Using semantic elements (\\\u003cinput\\\u003e, \\\u003coption\\\u003e, etc) instead of \\\u003cdiv\\\u003e. This allows assistive technologies (AT) to focus on the input elements and provide appropriate contextual information to the user, making the form easier to navigate and understand.\n\n- Labelling: Including \\\u003clabel\\\u003e and the htmlFor attribute ensures that each form field has a descriptive text label. This improves AT support by providing context and also enhances usability by allowing users to click on the label to focus on the corresponding input field.\n\n- Focus Outline: The fields are properly styled to show an outline when they are in focus. This is critical for accessibility as it visually indicates the active element on the page, helping both keyboard and screen reader users to understand where they are on the form. You can verify this by pressing tab.\n\nThese practices lay a good foundation for making your forms more accessible to many users. However, they don't address form validation and errors.\n\n## Form validation\n\nGo to http://localhost:3000/dashboard/invoices/create, and submit an empty form. What happens?\n\nYou get an error! This is because you're sending empty form values to your Server Action. You can prevent this by validating your form on the client or the server.\n\n### Client-Side validation\n\nThere are a couple of ways you can validate forms on the client. The simplest would be to rely on the form validation provided by the browser by adding the required attribute to the \\\u003cinput\\\u003e and \\\u003cselect\\\u003e elements in your forms.\n\n```tsx\n  \u003cinput\n    id=\"amount\"\n    name=\"amount\"\n    type=\"number\"\n    placeholder=\"Enter USD amount\"\n    className=\"peer block w-full rounded-md border border-gray-200 py-2 pl-10 text-sm outline-2 placeholder:text-gray-500\"\n    required\n  /\u003e\n```\n\nSubmit the form again, and you should now see the browser a warning if you try to submit a form with empty values.\n\nThis approach is generally okay because some ATs support browser validation.\n\nAn alternative to client-side validation is server-side validation. Let's see how you can implement it in the next section. For now, delete the required attributes if you added them.\n\n### Server-Side validation\n\nBy validating forms on the server, you can:\n\n- Ensure your data is in the expected format before sending it to your database.\n- Reduce the risk of malicious users bypassing client-side validation.\n- Have one source of truth for what is considered valid data.\n\nIn your create-form.tsx component, import the useFormState hook from react-dom. Since useFormState is a hook, you will need to turn your form into a Client Component using \"use client\" directive:\n\n```tsx\n'use client';\n \n// ...\nimport { useFormState } from 'react-dom';\n```\n\nInside your Form Component, the useFormState hook:\n\nTakes two arguments: (action, initialState).\nReturns two values: [state, dispatch] - the form state, and a dispatch function (similar to useReducer)\nPass your createInvoice action as an argument of useFormState, and inside your \\\u003cform action={}\\\u003e attribute, call dispatch.\n\n```tsx\n// ...\nimport { useFormState } from 'react-dom';\n \nexport default function Form({ customers }: { customers: CustomerField[] }) {\n  const [state, dispatch] = useFormState(createInvoice, initialState);\n \n  return \u003cform action={dispatch}\u003e...\u003c/form\u003e;\n}\n```\n\nThe initialState can be anything you define, in this case, create an object with two empty keys: message and errors.\n\nThis may seem confusing initially, but it'll make more sense once you update the server action. Let's do that now.\n\nIn your action.ts file, you can use Zod to validate form data. Update your InvoiceSchema as follows:\n\n```ts\nconst InvoiceSchema = z.object({\n  id: z.string(),\n  customerId: z.string({\n    invalid_type_error: 'Please select a customer.',\n  }),\n  amount: z.coerce\n    .number()\n    .gt(0, { message: 'Please enter an amount greater than $0.' }),\n  status: z.enum(['pending', 'paid'], {\n    invalid_type_error: 'Please select an invoice status.',\n  }),\n  date: z.string(),\n});\n```\n\n- `customerId` - Zod already throws an error if the customer field is empty as it expects a type string. But let's add a friendly message if the user doesn't select a customer.\n- `amount` - Since you are coercing the amount type from string to number, it'll default to zero if the string is empty. Let's tell Zod we always want the amount greater than 0 with the .gt() function.\n- `status` - Zod already throws an error if the status field is empty as it expects \"pending\" or \"paid\". Let's also add a friendly message if the user doesn't select a status.\n\nNext, update your createInvoice action to accept two parameters:\n\n- `formData ` - same as before.\n- `prevState` - contains the state passed from the useFormState hook. You won't be using it in the action in this example, but it's a required prop.\n\nThen, change the Zod parse() function to safeParse()\n\n```ts\nexport async function createInvoice(prevState: State, formData: FormData) {\n  // Validate form fields using Zod\n  const validatedFields = CreateInvoice.safeParse({\n    customerId: formData.get('customerId'),\n    amount: formData.get('amount'),\n    status: formData.get('status'),\n  });\n \n  // ...\n}\n```\n\nsafeParse() will return an object containing either a success or error field. This will help handle validation more gracefully without having put this logic inside the try/catch block.\n\nBefore sending the information to your database, check if the form fields were validated correctly with a conditional\n\n```ts\n// If form validation fails, return errors early. Otherwise, continue.\n  if (!validatedFields.success) {\n    return {\n      errors: validatedFields.error.flatten().fieldErrors,\n      message: 'Missing Fields. Failed to Create Invoice.',\n    };\n  }\n```\n\nIf validatedFields isn't successful, we return the function early with the error messages from Zod.\n\nGreat, now let's display the errors in your form component. Back in the create-form.tsx component, you can access the errors using the form state.\n\n\nIn the code above, you're also adding the following aria labels:\n\n- **aria-describedby=\"customer-error\":** This establishes a relationship between the select element and the error message container. It indicates that the container with id=\"customer-error\" describes the select element. Screen readers will read this description when the user interacts with the select box to notify them of errors.\n\n- **id=\"customer-error\":** This id attribute uniquely identifies the HTML element that holds the error message for the select input. This is necessary for aria-describedby to establish the relationship.\n\n- **aria-live=\"polite\":** The screen reader should politely notify the user when the error is updated. When the content changes (e.g. when a user corrects an error), the screen reader will announce these changes, but only when the user is idle so as not to interrupt them.\n\nUsing the example above, add errors to your remaining form fields. You should also show a message at the bottom of the form if any fields are missing. Your UI should look like this:\n\n![Create invoice form showing error messages for each field.](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Fform-validation-page.png\u0026w=1080\u0026q=75\u0026dpl=dpl_AGrSzwth6hUFvw45h9RdJK62uM3B)\n\nOnce you're ready, run `npm run lint` to check if you're using the aria labels correctly.\n\nIf you'd like to challenge yourself, take the knowledge you've learned in this chapter and add form validation to the edit-form.tsx component.\n\nYou'll need to:\n\n- Add useFormState to your edit-form.tsx component.\n- Edit the updateInvoice action to handle validation errors from Zod.\n- Display the errors in your component, and add aria labels to improve accessibility.\n\n---\n\n## Adding Authentication\n\nYour application is almost ready, in the next chapter, you'll learn how to add authentication to your application using **NextAuth.js**.\n\nIn this chapter...\n\nHere are the topics we’ll cover\n\n- What is authentication.\n\n- How to add authentication to your app using NextAuth.js.\n\n- How to use Middleware to redirect users and protect your routes.\n\n- How to use React's `useFormStatus` and `useFormState` to handle pending states and form errors.\n\n### What is authentication?\n\nAuthentication is a key part of many web applications today. It's how a system checks if the user is who they say they are.\n\nA secure website often uses multiple ways to check a user's identity for enhanced security. For instance, after entering your username and password, the site may send a verification code to your device or use an external app like Google Authenticator. This 2-factor authentication (2FA) helps increase security. Even if someone learns your password, they can't access your account without your unique token.\n\n### Authentication vs. Authorization\n\nIn web development, authentication and authorization serve different roles:\n\nAuthentication is about making sure the user is who they say they are. You're proving your identity with something you have like a username and password.\nAuthorization is the next step. Once a user's identity is confirmed, authorization decides what parts of the application they are allowed to use.\nSo, authentication checks who you are, and authorization determines what you can do or access in the application.\n\n### Creating the login route\n\nStart by creating a new route in your application called /login and paste the following code:\n\n```tsx\nimport AcmeLogo from '@/app/ui/acme-logo';\nimport LoginForm from '@/app/ui/login-form';\n \nexport default function LoginPage() {\n  return (\n    \u003cmain className=\"flex items-center justify-center md:h-screen\"\u003e\n      \u003cdiv className=\"relative mx-auto flex w-full max-w-[400px] flex-col space-y-2.5 p-4 md:-mt-32\"\u003e\n        \u003cdiv className=\"flex h-20 w-full items-end rounded-lg bg-blue-500 p-3 md:h-36\"\u003e\n          \u003cdiv className=\"w-32 text-white md:w-36\"\u003e\n            \u003cAcmeLogo /\u003e\n          \u003c/div\u003e\n        \u003c/div\u003e\n        \u003cLoginForm /\u003e\n      \u003c/div\u003e\n    \u003c/main\u003e\n  );\n}\n```\n\n### NextAuth.js\n\nWe will be using NextAuth.js to add authentication to your application. NextAuth.js abstracts away much of the complexity involved in managing sessions, sign-in and sign-out, and other aspects of authentication. While you can manually implement these features, the process can be time-consuming and error-prone. NextAuth.js simplifies the process, providing a unified solution for auth in Next.js applications.\n\n**Setting up NextAuth.js**\n\nLet's set up NextAuth.js in your project. Run the following command in your terminal:\n\n```bash\nnpm install next-auth@beta bcrypt\n```\n\nHere, you're installing the beta version of NextAuth.js, which is compatible with Next.js 14. You're also installing bcrypt, which is a library that will help you hash passwords.\n\nNext, generate a secret key for your application. This key is used to encrypt cookies, ensuring the security of user sessions. You can do this by running the following command in your terminal:\n\n```bash\nopenssl rand -base64 32\n```\n\nThen, in your .env file, you'll notice two variables: `AUTH_SECRET` and `AUTH_URL`.\n\nAdd your generated key to `AUTH_SECRET`:\n\n```bash\nAUTH_SECRET=your-secret-key\nAUTH_URL=http://localhost:3000/api/auth\n```\n\nFor auth to work in production, you'll need to update your environment variables in your Vercel project too. Check out this guide on how to add environment variables on Vercel.\n\nAdding the pages option\nCreate an auth.config.ts file at the root of our project that exports an authConfig object. This object will contain the configuration options for NextAuth.js. For now, it will only contain the pages option:\n\n```ts\nimport type { NextAuthConfig } from 'next-auth';\n \nexport const authConfig = {\n  pages: {\n    signIn: '/login',\n  },\n};\n```\n\nYou can use the pages option to specify the route for custom sign-in, sign-out, and error pages. It is not required, but if you don't provide it, NextAuth.js will use its default sign-in, sign-out, and error pages. By adding signIn: '/login' into our pages option, the user will be redirected to our custom login page, rather than the NextAuth.js default page.\n\n### Protecting your routes with Next.js Middleware\n\nNext, add the logic to protect your routes. This will prevent users from accessing the dashboard pages unless they are logged in.\n\n```ts\nimport type { NextAuthConfig } from 'next-auth';\n \nexport const authConfig = {\n  providers: [],\n  pages: {\n    signIn: '/login',\n  },\n  callbacks: {\n    authorized({ auth, request: { nextUrl } }) {\n      const isLoggedIn = !!auth?.user;\n      const isOnDashboard = nextUrl.pathname.startsWith('/dashboard');\n      if (isOnDashboard) {\n        if (isLoggedIn) return true;\n        return false; // Redirect unauthenticated users to login page\n      } else if (isLoggedIn) {\n        return Response.redirect(new URL('/dashboard', nextUrl));\n      }\n      return true;\n    },\n  },\n} satisfies NextAuthConfig;\n\n```\n\nThe authorized callback is used to verify if the request is authorized to access a page via Next.js Middleware. It is called before a request is completed, and it receives an object with the auth and request properties. The auth property contains the user's session, and the request property contains the incoming request.\n\nNext, you will need to import the authConfig object into a Middleware file. In the root of your project, create a file called middleware.ts and paste the following code:\n\n```ts\nimport NextAuth from 'next-auth';\nimport { authConfig } from './auth.config';\n \nexport default NextAuth(authConfig).auth;\n \nexport const config = {\n  // https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher\n  matcher: ['/((?!api|_next/static|_next/image|.png).*)'],\n};\n```\nHere you're initializing NextAuth.js with the authConfig object and exporting the auth property. You're also using the matcher option from Middleware to specify that it should run on specific paths.\n\nThe advantage of employing Middleware for this task is that the protected routes will not even start rendering until the Middleware verifies the authentication, enhancing both the security and performance of your application.\n\n### Password hashing\n\nTo store passwords securely, you will need to hash them. This process converts the password into a fixed-length string of characters, which appears random, providing a layer of security even if the hash is exposed.\n\nIn your seed.js file, we've used bcrypt to hash the password before storing it in the database. You can use bcrypt to compare that the password entered by the user matches the one in the database.\n\nHowever, bcrypt relies on Node.js APIs not available in Next.js Middleware. To solve this, you will need to create a separate file that imports bcrypt. The new file will not be imported into your Middleware file.\n\nCreate a new file called auth.ts that spreads your authConfig object:\n\n```ts\nimport NextAuth from 'next-auth';\nimport { authConfig } from './auth.config';\n \nexport const { auth, signIn, signOut } = NextAuth({\n  ...authConfig,\n});\n```\n\n### Adding Credentials provider\n\nNext, you will need to add the providers option for NextAuth.js. providers is an array where you list different login options such as Google or GitHub. For this course, we will focus on using the Credentials provider only.\n\nThe Credentials provider allows users to log in with a username and a password.\n\n\u003eGood to know:\n\u003e\n\u003eAlthough we're using the Credentials provider, it's generally recommended to use alternative providers such as OAuth or email providers. See the NextAuth.js docs for a full list of options.\n\n### Adding the sign in functionality\n\nYou can use the authorize function to handle the authentication logic. Similarly to Server Actions, you can use zod to validate the email and password before checking if the user exists in the database and after validating the credentials, create a new getUser function that queries the user from the database.\n\nThen, call bcrypt.compare to check if the passwords match.\n\nFinally, if the passwords match you want to return the user, otherwise, return null to prevent the user from logging in.\n\n### Updating the login form\n\nNow you need to connect the auth logic with your login form. In your actions.ts file, create a new action called authenticate. This action should import the signIn function from auth.ts.\n\nIf there's a 'CredentialSignin' error, you want to return it so that you can show an appropriate error message.\n\nFinally, in your login-form.tsx component, you can use React's useFormState to call the server action and handle form errors, and use useFormStatus to handle the pending state of the form.\n\n### Adding the logout functionality\n\nTo add the logout functionality, call the signOut function from auth.ts in your \\\u003cSideNav\\\u003e component.\n\n**Try it out**\n\nNow, try it out. You should be able to log in and out of your application using the following credentials:\n\n- Email: user@nextmail.com\n- Password: 123456\n\n---\n\n## Adding Metadata\n\nFinish your application by learning how to add metadata in preparation for sharing.\n\nMetadata is crucial for SEO and shareability. In this chapter, we'll discuss how you can add metadata to your Next.js application.\n\nIn this chapter...\n\nHere are the topics we’ll cover\n\n- What metadata is.\n\n- Types of metadata.\n\n- How to add an Open Graph image using metadata.\n\n- How to add a favicon using metadata.\n\n### What is metadata?\n\nIn web development, metadata provides additional details about a webpage. Metadata is not visible to the users visiting the page. Instead, it works behind the scenes, embedded within the page's HTML, usually within the \u003chead\u003e element. This hidden information is crucial for search engines and other systems that need to understand your webpage's content better.\n\n### Why is metadata important?\n\nMetadata plays a significant role in enhancing a webpage's SEO, making it more accessible and understandable for search engines and social media platforms. Proper metadata helps search engines effectively index webpages, improving their ranking in search results. Additionally, metadata like Open Graph improves the appearance of shared links on social media, making the content more appealing and informative for users.\n\n## Types of metadata\n\nThere are various types of metadata, each serving a unique purpose. Some common types include:\n\nTitle Metadata: Responsible for the title of a webpage that is displayed on the browser tab. It's crucial for SEO as it helps search engines understand what the webpage is about.\n\n```html\n\u003ctitle\u003ePage Title\u003c/title\u003e\n```\n\nDescription Metadata: This metadata provides a brief overview of the webpage content and is often displayed in search engine results.\n\n```html\n\u003cmeta name=\"description\" content=\"A brief description of the page content.\" /\u003e\n```\n\nKey","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flllariogonzalez%2Fnextjs-dashboard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flllariogonzalez%2Fnextjs-dashboard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flllariogonzalez%2Fnextjs-dashboard/lists"}