Build An Invoice App With Next.js 14, App Router, Server Actions, And Custom JWT Auth

by StackCamp Team 86 views

In today's fast-paced digital world, efficient invoice management is crucial for businesses of all sizes. Building an invoice application can streamline financial processes, improve cash flow, and enhance overall business operations. In this comprehensive guide, we'll explore how to create a modern invoice application using Next.js 14, leveraging its powerful features such as the App Router, Server Actions, and Custom JWT Authentication. By the end of this article, you'll have a solid understanding of the key concepts and techniques involved in building a robust and secure invoice management system.

Embracing Next.js 14 for Invoice App Development

Next.js 14, the latest iteration of the popular React framework, provides a rich set of features and capabilities that make it an excellent choice for building modern web applications, including invoice management systems. The App Router, a cornerstone of Next.js 14, enables a file-system-based routing system that simplifies the organization and navigation of your application. Server Actions, another key feature, allow you to execute server-side logic directly from your React components, enhancing performance and security. Custom JWT Authentication provides a secure and flexible way to manage user authentication and authorization within your application. By harnessing these features, you can create an invoice app that is not only functional but also scalable, maintainable, and secure. The App Router simplifies the routing mechanism, while Server Actions allow for direct execution of server-side logic from React components, enhancing performance and security. Custom JWT Authentication ensures secure user management within the application.

Key Features of Our Invoice App

Before diving into the implementation details, let's outline the key features of our invoice application:

  • User Authentication and Authorization: Securely manage user accounts and access control using Custom JWT Authentication.
  • Invoice Creation and Management: Allow users to create, edit, and delete invoices with ease.
  • Client Management: Maintain a database of clients with relevant information.
  • Invoice Generation: Generate professional-looking invoices in PDF format.
  • Payment Tracking: Track invoice payments and generate reports.
  • Dashboard: Provide an overview of key metrics, such as outstanding invoices and total revenue.

These features form the foundation of a comprehensive invoice management system. Throughout this article, we'll delve into the technical aspects of implementing each feature using Next.js 14, Server Actions, and Custom JWT Authentication. Our focus will be on creating a user-friendly interface that simplifies invoice management for businesses of all sizes. User authentication will be handled using Custom JWT Authentication, ensuring secure access control. Invoice creation and management will be streamlined, allowing users to easily generate, edit, and delete invoices. Client management will enable users to maintain a database of clients with relevant details. Invoice generation will produce professional-looking PDFs, and payment tracking will help monitor invoice payments and generate reports. The dashboard will provide a comprehensive overview of key metrics, such as outstanding invoices and total revenue.

Setting Up Your Next.js 14 Project

To begin, let's set up a new Next.js 14 project. Open your terminal and run the following command:

npx create-next-app@latest invoice-app

This command will create a new Next.js project named "invoice-app". Navigate into the project directory:

cd invoice-app

Next, install the necessary dependencies:

npm install bcrypt jsonwebtoken @prisma/client prisma
  • bcrypt: For password hashing.
  • jsonwebtoken: For generating and verifying JWT tokens.
  • @prisma/client and prisma: For database access using Prisma.

Once the dependencies are installed, initialize Prisma:

npx prisma init --datasource-provider postgresql

This command will create a prisma directory in your project, containing the Prisma schema file (schema.prisma) and the .env file. Update the .env file with your PostgreSQL database connection URL:

DATABASE_URL="postgresql://user:password@host:port/database?schema=public"

Replace user, password, host, port, and database with your actual database credentials. Next, define your database schema in the schema.prisma file. For our invoice app, we'll need models for users, clients, and invoices. Setting up your Next.js 14 project involves creating a new project, installing dependencies, and initializing Prisma for database access. The bcrypt library is used for secure password hashing, while jsonwebtoken facilitates JWT token generation and verification. @prisma/client and prisma enable database interactions using Prisma. After installing dependencies, Prisma is initialized, and the database connection URL is configured in the .env file. The Prisma schema is then defined, outlining the structure of the database models for users, clients, and invoices.

Implementing Custom JWT Authentication

Creating User Model

First, let's define the User model:

model User {
 id        String   @id @default(uuid())
 email     String   @unique
 password  String
 name      String?
 invoices  Invoice[]
 createdAt DateTime @default(now())
 updatedAt DateTime @updatedAt
}

Creating Client Model

Next, create the Client model:

model Client {
 id        String   @id @default(uuid())
 name      String
 email     String?
 phone     String?
 address   String?
 invoices  Invoice[]
 createdAt DateTime @default(now())
 updatedAt DateTime @updatedAt
}

Creating Invoice Model

Finally, define the Invoice model:

model Invoice {
 id          String   @id @default(uuid())
 invoiceNumber String @unique
 clientId    String
 client      Client   @relation(fields: [clientId], references: [id])
 userId      String
 user        User     @relation(fields: [userId], references: [id])
 issueDate   DateTime
 dueDate     DateTime
 amount      Float
 status      String
 createdAt   DateTime @default(now())
 updatedAt   DateTime @updatedAt
}

Run the following command to generate the Prisma client and apply the migrations:

npx prisma migrate dev

This command will create the database tables based on your schema. With the database schema defined, we can now implement the Custom JWT Authentication. This involves creating functions for user registration, login, and token verification. We'll utilize bcrypt for password hashing and jsonwebtoken for creating and verifying JWT tokens. Implementing Custom JWT Authentication begins with defining the User, Client, and Invoice models in the Prisma schema. The User model includes fields for ID, email, password, name, invoices, and timestamps. The Client model contains fields for ID, name, email, phone, address, invoices, and timestamps. The Invoice model includes fields for ID, invoice number, client ID, user ID, issue date, due date, amount, status, and timestamps. After defining the schema, the Prisma client is generated, and migrations are applied to create the database tables. This sets the stage for implementing user registration, login, and token verification using bcrypt for password hashing and jsonwebtoken for JWT token management.

Implementing Server Actions for Invoice Operations

Server Actions in Next.js 14 allow you to execute server-side logic directly from your React components. This is particularly useful for handling form submissions, database interactions, and other server-side operations. For our invoice app, we'll use Server Actions to implement the following functionalities:

  • Creating Invoices: Create a new invoice in the database.
  • Updating Invoices: Update an existing invoice.
  • Deleting Invoices: Delete an invoice from the database.
  • Fetching Invoices: Retrieve invoices from the database.

To create a Server Action, you simply define an async function with the "use server" directive at the top. This tells Next.js to execute the function on the server. For example, let's create a Server Action to create a new invoice:

// app/actions.ts
"use server";

import { db } from "@/lib/db";
import { Invoice } from "@prisma/client";

export async function createInvoice(data: Omit<Invoice, "id" | "createdAt" | "updatedAt">): Promise<Invoice> {
 const invoice = await db.invoice.create({
 data,
 });
 return invoice;
}

This Server Action takes invoice data as input and creates a new invoice in the database using Prisma. Similarly, you can create Server Actions for updating, deleting, and fetching invoices. These Server Actions can then be called directly from your React components, providing a seamless way to interact with the server. Implementing Server Actions for invoice operations is a crucial step in building a dynamic invoice app. Server Actions in Next.js 14 enable the execution of server-side logic directly from React components. This approach is ideal for handling form submissions, database interactions, and other server-side tasks. In our invoice app, Server Actions are used to create, update, delete, and fetch invoices. To define a Server Action, an async function is created with the "use server" directive at the top. This directive instructs Next.js to execute the function on the server. For instance, a Server Action can be created to handle invoice creation, taking invoice data as input and creating a new invoice in the database using Prisma. Similarly, Server Actions can be implemented for updating, deleting, and fetching invoices, providing a seamless way to interact with the server from React components.

Building the User Interface with React Components

The user interface of our invoice app will be built using React components. We'll create components for various parts of the application, such as:

  • Invoice List: Displays a list of invoices.
  • Invoice Form: Allows users to create and edit invoices.
  • Client List: Displays a list of clients.
  • Client Form: Allows users to create and edit clients.
  • Dashboard: Provides an overview of key metrics.

Each component will be responsible for rendering a specific part of the UI and handling user interactions. For example, the Invoice Form component will render a form with fields for invoice details, such as invoice number, client, issue date, due date, and amount. It will also handle form submissions and call the appropriate Server Actions to create or update invoices. By breaking down the UI into reusable components, we can create a modular and maintainable application. Each component can be developed and tested independently, making it easier to manage the complexity of the application. Building the user interface with React components is a fundamental aspect of creating a user-friendly invoice app. The UI is structured using reusable components, each responsible for rendering a specific part of the application and handling user interactions. Components are created for displaying invoice lists, invoice forms, client lists, client forms, and the dashboard. The Invoice Form component, for instance, renders a form with fields for invoice details and handles form submissions, calling the appropriate Server Actions to create or update invoices. This component-based approach promotes modularity and maintainability, allowing for independent development and testing of individual components, thereby simplifying the management of application complexity.

Generating PDF Invoices

Generating professional-looking invoices in PDF format is a crucial feature for any invoice application. There are several libraries available for generating PDFs in Node.js, such as pdfmake and html-pdf. For this example, we'll use pdfmake, which allows us to define the PDF document structure using JavaScript objects.

First, install the pdfmake package:

npm install pdfmake

Next, create a Server Action to generate the PDF invoice:

// app/actions.ts
"use server";

import { db } from "@/lib/db";
import { Invoice } from "@prisma/client";
import pdfMake from "pdfmake/build/pdfmake";
import pdfFonts from "pdfmake/build/vfs_fonts";

pdfMake.vfs = pdfFonts.pdfMake.vfs;

export async function generateInvoicePdf(invoiceId: string): Promise<string> {
 const invoice = await db.invoice.findUnique({
 where: {
 id: invoiceId,
 },
 include: {
 client: true,
 },
 });

 if (!invoice) {
 throw new Error("Invoice not found");
 }

 const documentDefinition = {
 content: [
 { text: "Invoice", style: "header" },
 { text: `Invoice Number: ${invoice.invoiceNumber}`, style: "subheader" },
 { text: `Issue Date: ${invoice.issueDate.toLocaleDateString()}` },
 { text: `Due Date: ${invoice.dueDate.toLocaleDateString()}` },
 { text: `Client: ${invoice.client.name}` },
 { text: `Amount: ${invoice.amount}` },
 ],
 styles: {
 header: { fontSize: 22, bold: true },
 subheader: { fontSize: 18, bold: true },
 },
 };

 const pdfDoc = pdfMake.createPdfKitDocument(documentDefinition);

 return new Promise((resolve, reject) => {
 const chunks: Uint8Array[] = [];
 pdfDoc.on("data", (chunk) => chunks.push(chunk));
 pdfDoc.on("end", () => resolve(Buffer.concat(chunks).toString("base64")));
 pdfDoc.on("error", reject);
 pdfDoc.end();
 });
}

This Server Action fetches the invoice from the database, defines the PDF document structure using pdfmake, and generates the PDF document as a base64-encoded string. You can then use this string to display the PDF in the browser or download it. Generating PDF invoices is a vital feature for any invoice application, ensuring professional-looking documents. Libraries like pdfmake and html-pdf facilitate PDF generation in Node.js. In this example, pdfmake is used, allowing the definition of the PDF document structure using JavaScript objects. The pdfmake package is installed, and a Server Action is created to generate the PDF invoice. This Server Action fetches the invoice from the database, defines the PDF document structure using pdfmake, and generates the PDF document as a base64-encoded string. This string can then be used to display the PDF in the browser or facilitate downloading. The use of pdfmake simplifies the process of creating structured and visually appealing PDF invoices.

Conclusion

In this article, we've explored how to build a modern invoice application using Next.js 14, leveraging its powerful features such as the App Router, Server Actions, and Custom JWT Authentication. We've covered key aspects of the development process, including setting up the project, implementing user authentication, creating Server Actions for invoice operations, building the user interface with React components, and generating PDF invoices. By following these steps, you can create a robust and scalable invoice management system that meets the needs of your business. In conclusion, building a modern invoice application using Next.js 14 offers numerous advantages, leveraging features such as the App Router, Server Actions, and Custom JWT Authentication. This article has provided a comprehensive guide to the development process, covering project setup, user authentication implementation, Server Action creation for invoice operations, user interface construction with React components, and PDF invoice generation. By adhering to these steps, a robust and scalable invoice management system can be created to meet specific business requirements. The use of Next.js 14 and its associated features streamlines the development process and enhances the overall functionality and user experience of the invoice application.

This is a starting point, and you can further enhance the application by adding features such as payment integration, recurring invoices, and reporting. Remember to always prioritize security and user experience when building web applications. As a next step, consider integrating payment gateways, implementing recurring invoice functionality, and adding comprehensive reporting capabilities. Throughout the development process, prioritizing security and user experience is paramount. Further enhancements can be made to the application based on specific business needs and user feedback. Continuous improvement and adaptation are key to creating a successful and user-centric invoice management system.