All Logs

Building InvoicePedia: My Journey into Full-Stack Invoicing with Next.js, PostgreSQL, and Stripe

May 2, 2025 (1y ago)

For the past few weeks I have been building InvoicePedia, a full-stack app that lets people create and send invoices either as themselves or through an organization, and get paid through Stripe.

It is my first time using PostgreSQL and my first time doing server-side rendering at any real scale with Next.js 15. Both of those turned out to be the interesting part.

What It Does

  • Sign in with Clerk
  • Create and manage invoices as an individual
  • Create and manage invoices inside an organization
  • Accept payments through Stripe
  • Look up customer details and invoice history

The UI is deliberately plain right now. I wanted the functionality correct before I spent time on how it looks. That order will flip in the next version.

Tech Stack and Why

TechWhy I Used It
Next.js 15Full-stack React in one codebase
Tailwind CSSFast, responsive styling
shadcn/uiAccessible components I did not have to build
ClerkAuth and organizations without writing either
PostgreSQLRelational database, my first time
Drizzle ORMType-safe queries
StripePayments
XataCloud data layer, used selectively
VercelDeployment

Learning PostgreSQL

Basic queries were fine. Where it got difficult was dynamic relational joins with filters, especially once personal invoices and organization invoices had to be queried through the same code paths.

Drizzle helped because the types caught a lot of my mistakes before runtime did. But the actual shift was learning to think relationally instead of reaching for the shape of data I wanted and forcing it.

Stripe

Smoother than I expected. I used Stripe Checkout to keep the surface area small and avoid handling anything sensitive myself. The user generates an invoice, the recipient pays on a Stripe-hosted page, and the app never touches card details.

Personal vs Organization Accounts

The one genuinely non-obvious piece of the app. A user can act on their own or inside an organization.

  • Acting as an individual, their invoices are scoped to their user ID.
  • Acting inside an organization, their invoices are scoped to that organization.

This is where I ran into multi-tenant patterns for the first time. Clerk handles most of the organization membership logic through its API, which saved me from designing that myself while I was still figuring out the data model.

Where I Am Stuck

Deployment.

Everything works in development. On Vercel, my page.tsx files throw type errors on the params of dynamic routes, and the production build will not complete. I am still debugging it. There is no live demo until this is sorted.

What's Next

The app is feature-complete and looks it. Planned:

  • Rework the UI properly
  • PDF download and export for invoices
  • Recurring invoices
  • A dashboard worth looking at
  • Fix the build and ship a live version

Where This Leaves Me

This is the most complete thing I have built. Data modelling in PostgreSQL, user and organization management, and real money moving through Stripe, all in one project instead of three separate tutorials.

The code is on GitHub. If you have hit the same Next.js 15 dynamic route type errors on Vercel, I would like to know how you got past them.