Iurii RoguliaIurii Rogulia
AboutServicesPricingProjectsStackReviewsPhrasesBlog
Contact
Iuriiย ships.

Iurii Rogulia, IT partner for business & fractional CTO. Professionally building software since 2001.

Think of a number
PricingQuality checklistPrivacy PolicyCookie Policy

Business

TMI Iurii Rogulia
VAT ID: FI29845875
DUNS: 368664211
Lappeenranta, Finland ๐Ÿ‡ซ๐Ÿ‡ฎ

[email protected]
  1. Home
  2. /
  3. Blog
  4. /
  5. Next.js SaaS Checklist: Launch Production-Ready in 8 Weeks

Iuriiย Builds: Next.js SaaS Checklist: Launch Production-Ready in 8 Weeks

How I build a production-ready SaaS in 2 months โ€” the 40+ point checklist I actually use

January 19, 2026ยท 17 min read

40+ point production SaaS checklist: auth, Stripe billing, PostgreSQL, rate limiting, email, monitoring, and security โ€” with honest 8-week build estimates.

Stack

Next.jsTypeScript

Libraries

Drizzle ORMBetter AuthBullMQZodReact Email

Databases

PostgreSQLRedis

Services

StripeVercelResendSentry

Topics

SaaSArchitectureAuth
Next.js SaaS Checklist: Launch Production-Ready in 8 Weeks

On this page

  • The Stack I Start With
  • Auth Checklist
  • Billing Checklist
  • Database Checklist
  • API Checklist
  • Email Checklist
  • Monitoring and Observability Checklist
  • Security Checklist
  • SEO and Analytics Checklist
  • What to Build vs. What to Buy
  • Vercel: What Works and What Doesn't
  • Honest Time Estimates
  • The Complete Checklist (Summary)
  • Auth
  • Billing
  • Database
  • API
  • Email
  • Monitoring
  • Security
  • SEO and Analytics

I've built several SaaS products. Each time I run through the same checklist. Not because I'm following a template โ€” but because I've paid for skipping items with production incidents, angry customers, and weekends spent fixing what should have been done at the start.

vatnode.dev โ€” EU VAT validation SaaS, running in production with 95% Redis cache hit rate. htpbe.tech โ€” PDF forensics SaaS, 5-layer analysis in under 9 seconds. pi-pi.ee โ€” B2B e-commerce across 32 EU markets. All of them went from zero to production in 6โ€“8 weeks. Here's exactly what that takes.

The Stack I Start With

Before the checklist, the decision I never revisit: the default stack.

  • Next.js 15 (App Router) for the web application โ€” Server Components, Server Actions, API routes in one framework
  • Hono 4 when I need a dedicated API server (separate deployment, higher performance needs, BullMQ workers)
  • PostgreSQL โ€” the only database I trust for production SaaS
  • Drizzle ORM โ€” type-safe, no magic, migrations I understand
  • Better Auth โ€” auth library that handles sessions, OAuth, magic links properly
  • Stripe โ€” payments, full stop
  • Resend โ€” transactional email
  • Redis (Upstash or self-hosted) โ€” rate limiting, queues, caching
  • BullMQ โ€” background job queue on top of Redis
  • Vercel โ€” hosting, with caveats I'll get to
  • Sentry โ€” error tracking

Every project deviation from this stack has cost me time. I now treat any deviation as a decision that requires justification, not exploration.

Auth Checklist

Related service

MVP Development

I build the full SaaS foundation โ€” auth, billing, database, background jobs, monitoring โ€” so you can focus on the product, not the plumbing.

More about this service โ†’

Auth is where most SaaS projects spend too much time if they roll their own, and too little time if they just copy a tutorial.

  • Email + password with bcrypt โ€” minimum 12 rounds, store only the hash
  • Magic links โ€” better UX for B2B tools where users don't want another password
  • OAuth: Google and GitHub โ€” covers 80% of developer and startup users
  • Session management โ€” use database sessions, not JWT-only (you need the ability to revoke)
  • Rate limiting on auth endpoints โ€” login, register, password reset, magic link send
  • Email verification โ€” required before first login, not optional
  • Password reset flow โ€” expiring tokens, single-use
  • Account deletion โ€” GDPR requirement, not an afterthought

I use Better Auth on all current projects. It handles sessions, OAuth providers, email verification, and password reset in one library. NextAuth.js is fine, but Better Auth has better TypeScript ergonomics and the plugin model is cleaner.

Here's the core Better Auth setup I start every project with:

// lib/auth.ts
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "@/packages/db";
import { magicLink } from "better-auth/plugins";
 
export const auth = betterAuth({
  database: drizzleAdapter(db, {
    provider: "pg",
  }),
  emailAndPassword: {
    enabled: true,
    requireEmailVerification: true,
  },
  socialProviders: {
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    },
    github: {
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    },
  },
  plugins: [
    magicLink({
      sendMagicLink: async ({ email, url }) => {
        // Resend integration โ€” see Email section below
        await sendMagicLinkEmail({ email, url });
      },
    }),
  ],
  rateLimit: {
    enabled: true,
    window: 60, // 60 second window
    max: 10, // 10 requests per window per IP
  },
});
 
export type Session = typeof auth.$Infer.Session;

Better Auth generates the database schema automatically. Run npx better-auth generate and it outputs the Drizzle migration.

When to choose NextAuth.js instead: if you're on an older Next.js Pages Router project or the team already knows NextAuth.js deeply. For new projects starting today, Better Auth is the better choice.

Billing Checklist

Stripe is the only option I consider for EU SaaS. The European payment method support, VAT handling, and customer portal are not features you want to rebuild yourself.

  • Stripe Customer created on signup โ€” immediately, even before the first payment
  • Subscription or one-time payments โ€” decide upfront, the data model differs significantly
  • Webhook handler with idempotency โ€” Stripe retries for 72 hours; duplicates are guaranteed without this
  • Redis deduplication layer โ€” fast check before hitting the database
  • Self-service portal โ€” stripe.billingPortal.sessions.create() handles plan changes, cancellations, invoice history
  • Free trial logic โ€” trial_period_days in the subscription, enforce feature gating on trial expiry
  • Upgrade/downgrade flow โ€” use stripe.subscriptions.update() with proration_behavior: 'create_prorations'
  • Invoice generation and delivery โ€” Stripe sends invoice PDFs automatically; for custom invoices, see the PDF generation work I described in the order automation pipeline
  • Failed payment recovery (dunning) โ€” three-email sequence, don't immediately revoke access
  • Webhook events to handle: payment_intent.succeeded, customer.subscription.updated, customer.subscription.deleted, invoice.payment_failed, invoice.payment_succeeded

The idempotency pattern I use โ€” covering both Redis fast-check and PostgreSQL durable record โ€” is documented in detail in Stripe Webhooks Done Right. I won't repeat it here, but it's non-negotiable: ship it or ship duplicate orders.

Database Checklist

  • PostgreSQL โ€” I've used MySQL and SQLite on old projects; I don't anymore
  • Drizzle ORM โ€” type-safe queries, plain SQL migrations, no ORM magic
  • created_at and updated_at on every table โ€” non-negotiable for debugging production issues
  • Soft deletes โ€” deleted_at timestamp column instead of hard DELETE; makes recovery and audit possible
  • Migration strategy โ€” Drizzle migrations committed to the repo, run on deploy
  • Backup policy โ€” daily automated backups, tested restore at least once before launch
  • Connection pooling โ€” PgBouncer or Neon's built-in pooling; raw PostgreSQL connections don't survive serverless

Here's the table structure I start every project with:

// packages/db/schema/base.ts
import { timestamp, uuid } from "drizzle-orm/pg-core";
import { sql } from "drizzle-orm";
 
// Reusable column set โ€” spread into every table definition
export const baseColumns = {
  id: uuid("id")
    .primaryKey()
    .default(sql`gen_random_uuid()`),
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
  updatedAt: timestamp("updated_at", { withTimezone: true })
    .notNull()
    .defaultNow()
    .$onUpdate(() => new Date()),
  deletedAt: timestamp("deleted_at", { withTimezone: true }),
};
 
// packages/db/schema/users.ts
import { pgTable, text, boolean } from "drizzle-orm/pg-core";
import { baseColumns } from "./base";
 
export const users = pgTable("users", {
  ...baseColumns,
  email: text("email").notNull().unique(),
  emailVerified: boolean("email_verified").notNull().default(false),
  stripeCustomerId: text("stripe_customer_id").unique(),
  plan: text("plan").notNull().default("free"),
  planActivatedAt: timestamp("plan_activated_at", { withTimezone: true }),
});

Drizzle vs Prisma: I made the switch after Prisma's migration behaviour caused a production incident (it tried to recreate an indexed column instead of adding a new one). Drizzle generates raw SQL migrations you can read and verify before running. That's the property I care about most in production. Prisma is friendlier for beginners; Drizzle is what I trust with real data.

API Checklist

  • Rate limiting per IP โ€” protect against unauthenticated abuse
  • Rate limiting per API key โ€” per-plan limits for authenticated users
  • Error responses with machine-readable codes โ€” not just HTTP status codes, but { "error": { "code": "VAT_NUMBER_INVALID", "message": "..." } }
  • Cursor-based pagination โ€” offset pagination breaks on large datasets and concurrent writes
  • API versioning strategy โ€” even if v1 is the only version, establish the URL pattern now (/api/v1/)
  • OpenAPI documentation โ€” Hono has built-in OpenAPI support; use it
  • Request validation with Zod โ€” validate inputs before touching the database
  • Standard rate limit headers โ€” X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset

For vatnode's public API, I use a two-tier rate limit: 30 requests per minute per API key (enforced per plan in the database), and a hard 60 requests per minute per IP regardless of auth status. The Redis sliding window implementation is worth a dedicated article โ€” and I wrote one: Redis Rate Limiting for APIs. Building a public-facing API is also part of my API integrations work.

A consistent error response format matters more than most developers realize. When a client's code breaks at 2 AM, machine-readable error codes are what makes automated retry logic possible:

// lib/api-error.ts
export class ApiError extends Error {
  constructor(
    public readonly code: string,
    public readonly message: string,
    public readonly statusCode: number = 400,
    public readonly details?: Record<string, unknown>
  ) {
    super(message);
  }
}
 
// lib/api-response.ts
export function errorResponse(error: ApiError) {
  return Response.json(
    {
      error: {
        code: error.code,
        message: error.message,
        ...(error.details ? { details: error.details } : {}),
      },
    },
    { status: error.statusCode }
  );
}
 
// Usage:
throw new ApiError("SUBSCRIPTION_REQUIRED", "This endpoint requires an active subscription.", 402);

Email Checklist

  • Resend or Mailgun for delivery โ€” never send transactional email from your own SMTP
  • Welcome / onboarding sequence โ€” triggered on signup, not a bulk newsletter
  • Email verification โ€” required before first meaningful action
  • Payment confirmation โ€” immediately after invoice.payment_succeeded
  • Failed payment recovery โ€” day 1, day 3, day 7; vary the subject and body
  • Subscription cancellation confirmation โ€” acknowledge it, include the end date and data export instructions
  • Branded HTML templates โ€” React Email for component-based email templates
  • Plain text fallback โ€” some clients don't render HTML; always include it

I use Resend with React Email on all current projects. The developer experience is significantly better than Mailgun templates, and the deliverability is comparable. Here's the email client setup:

// lib/email.ts
import { Resend } from "resend";
import { MagicLinkEmail } from "@/emails/magic-link";
import { PaymentConfirmationEmail } from "@/emails/payment-confirmation";
 
const resend = new Resend(process.env.RESEND_API_KEY!);
 
export async function sendMagicLinkEmail({ email, url }: { email: string; url: string }) {
  await resend.emails.send({
    from: "[email protected]",
    to: email,
    subject: "Your sign-in link",
    react: MagicLinkEmail({ url }),
  });
}
 
export async function sendPaymentConfirmation({
  email,
  invoiceUrl,
  amount,
  currency,
}: {
  email: string;
  invoiceUrl: string;
  amount: number;
  currency: string;
}) {
  const formatted = new Intl.NumberFormat("en-US", {
    style: "currency",
    currency: currency.toUpperCase(),
  }).format(amount / 100); // Stripe amounts are in cents
 
  await resend.emails.send({
    from: "[email protected]",
    to: email,
    subject: `Payment confirmed โ€” ${formatted}`,
    react: PaymentConfirmationEmail({ invoiceUrl, amount: formatted }),
  });
}

Monitoring and Observability Checklist

  • Sentry โ€” error tracking with source maps; configure SENTRY_DSN in environment
  • Uptime monitoring โ€” BetterUptime or UptimeRobot on all public endpoints; alert threshold 1 minute
  • Structured logging โ€” JSON logs with correlation IDs so you can trace a request across services
  • Performance monitoring โ€” Vercel Analytics or self-hosted Plausible for the frontend; Sentry performance for the API
  • Health check endpoint โ€” GET /api/health that checks DB connectivity and returns 200 or 503
  • Alert channels โ€” Telegram bot for critical errors, email for daily summaries

Correlation IDs are the monitoring item that developers consistently skip and consistently regret. When a user reports an error, "I got a 500 error at around 3 PM" is not debuggable without a correlation ID tying the frontend request to the backend logs.

// middleware.ts (Next.js)
import { NextRequest, NextResponse } from "next/server";
import { v4 as uuidv4 } from "uuid";
 
export function middleware(request: NextRequest) {
  const correlationId = request.headers.get("x-correlation-id") ?? uuidv4();
 
  const response = NextResponse.next();
  // Pass through to Server Components and API routes
  response.headers.set("x-correlation-id", correlationId);
 
  return response;
}

Then in every Server Action or API route:

const correlationId = request.headers.get("x-correlation-id");
console.log(JSON.stringify({ level: "info", correlationId, event: "order.created", orderId }));

Security Checklist

  • HTTPS enforced โ€” redirect HTTP to HTTPS at the infrastructure level, not in application code
  • Content Security Policy headers โ€” use next.config.ts headers configuration, start with report-only mode
  • SQL injection protection โ€” Drizzle ORM parameterizes all queries; never interpolate user input into raw SQL
  • XSS protection โ€” React escapes by default; audit any dangerouslySetInnerHTML usage
  • CSRF protection โ€” Better Auth handles this for session-based auth; verify for any custom endpoints
  • Secrets management โ€” environment variables only, never committed to the repo; use Vercel env vars or Doppler
  • Dependency audit โ€” npm audit in CI on every PR
  • Rate limiting on all mutating endpoints โ€” not just auth; account update, file upload, anything that changes state
  • Input validation on the server โ€” Zod schemas on all API inputs; never trust the client
// next.config.ts โ€” security headers
import type { NextConfig } from "next";
 
const nextConfig: NextConfig = {
  async headers() {
    return [
      {
        source: "/(.*)",
        headers: [
          {
            key: "X-Frame-Options",
            value: "DENY",
          },
          {
            key: "X-Content-Type-Options",
            value: "nosniff",
          },
          {
            key: "Referrer-Policy",
            value: "strict-origin-when-cross-origin",
          },
          {
            key: "Permissions-Policy",
            value: "camera=(), microphone=(), geolocation=()",
          },
          // Start with report-only, then enforce once you're confident
          {
            key: "Content-Security-Policy-Report-Only",
            value: [
              "default-src 'self'",
              "script-src 'self' 'unsafe-inline'", // Tighten this after audit
              "style-src 'self' 'unsafe-inline'",
              "img-src 'self' data: https:",
              "connect-src 'self' https://api.stripe.com",
            ].join("; "),
          },
        ],
      },
    ];
  },
};
 
export default nextConfig;

SEO and Analytics Checklist

  • Server-side rendering for all landing pages โ€” Next.js App Router does this by default
  • sitemap.xml generated dynamically โ€” app/sitemap.ts with all public routes
  • robots.txt โ€” block /api/*, /dashboard/*, allow everything else
  • Open Graph tags โ€” title, description, image on every public page
  • GA4 server-side tracking โ€” client-side tracking loses 30โ€“60% of conversions to adblockers and iOS
  • Structured data (JSON-LD) โ€” at minimum WebSite and Organization schemas on the landing page
  • Canonical URLs โ€” especially if you have locale-prefixed routes

For the GA4 server-side tracking implementation I use in production, including the Measurement Protocol setup and deduplication with Meta CAPI, that's a full topic on its own โ€” I cover it in the Server-Side Tracking article.

What to Build vs. What to Buy

This is where projects waste the most time. Building auth, email delivery, or payment processing from scratch is not a competitive advantage. It's technical debt with no upside.

CategoryDecisionWhy
AuthBetter Auth libraryRolling your own means managing sessions, hashing, OAuth, CSRF โ€” months of work and still wrong
Email deliveryResend or MailgunSPF/DKIM/DMARC setup, deliverability reputation, bounce handling โ€” use a service
File storageS3 or Cloudflare R2R2 has no egress fees, compatible S3 API; use it
PaymentsStripe onlyEuropean payment methods, VAT handling, dispute management โ€” nothing else comes close
SearchAlgolia or TypesenseOnly if you need full-text search; most SaaS don't need it at launch
Email templatesReact EmailComponent-based email that renders in all clients
Background jobsBullMQBattle-tested, Redis-backed, excellent TypeScript support
MonitoringSentry + UptimeRobotBoth have generous free tiers; set up on day one

The places I do build custom:

  • Business logic specific to the domain (EU VAT rules, PDF forensics analysis, order pipeline orchestration)
  • API integrations not covered by existing libraries (PostNord shipping API, Netvisor accounting API)
  • Rate limiting with product-specific tiers (covered in the Redis article)

Vercel: What Works and What Doesn't

I deploy Next.js apps to Vercel by default. The DX is excellent โ€” preview deployments, edge functions, automatic SSL. But there are real constraints to plan for:

Works well:

  • Server Components and Server Actions with standard rendering
  • API routes that complete in under 25 seconds
  • Edge middleware for auth and redirects
  • Static assets and image optimization

Requires workarounds:

  • Long-running background jobs โ€” use BullMQ on a separate VPS (I run a Vultr instance for workers)
  • Puppeteer for PDF generation โ€” bundle size hits function limits; consider a dedicated service or @sparticuz/chromium
  • Large file processing โ€” Vercel's request body limit is 4.5 MB; use presigned S3 URLs for direct browser-to-S3 uploads instead

For vatnode, the Next.js frontend and API routes run on Vercel; the BullMQ workers and long-running processes run on a separate Node.js service deployed to Vultr โ€” as described in detail in the self-hosting on a VPS article.

Honest Time Estimates

These are real numbers from real projects, not optimistic planning estimates.

Week 1โ€“2: Foundation

  • Auth (Better Auth setup, email verification, OAuth): 3โ€“4 days
  • Database schema (users, subscriptions, core tables): 2 days
  • Stripe integration (customer creation, subscription, webhook handler with idempotency): 3โ€“4 days
  • Base UI (layout, nav, dashboard shell, auth pages): 2 days

Week 3โ€“6: Core Features This is the variable part. For vatnode, the core feature (VAT validation with Redis caching, rate limiting, VIES integration) took 2 weeks. For HTPBE?, the 5-layer PDF forensics engine took 3 weeks to get right across 7 iterations of the algorithm. Your product's complexity determines this range.

Week 7โ€“8: Production Readiness

  • Error monitoring (Sentry integration, alert setup): 1 day
  • Performance audit (Core Web Vitals, database query optimization): 2 days
  • Security review (CSP headers, dependency audit, secrets audit): 1 day
  • Email sequences (welcome, payment confirmation, failed payment recovery): 2 days
  • Documentation (internal runbook, API documentation): 1 day

Total to a production-ready MVP: 6โ€“8 weeks.

Not 2 weeks like some frameworks promise. Not 6 months like agencies quote. 6โ€“8 weeks for a solid foundation plus a working core feature set, if you're a senior developer who's done it before.

The Complete Checklist (Summary)

Auth

  • Email + password with bcrypt (12+ rounds)
  • Magic links
  • OAuth: Google, GitHub
  • Database sessions (not JWT-only)
  • Rate limiting on all auth endpoints
  • Email verification before first login
  • Password reset with expiring single-use tokens
  • Account deletion (GDPR)

Billing

  • Stripe Customer on signup
  • Subscription or one-time payment model
  • Webhook idempotency (Redis + PostgreSQL)
  • Self-service billing portal
  • Free trial with feature gating
  • Upgrade/downgrade with proration
  • Dunning sequence for failed payments
  • All five critical webhook events handled

Database

  • PostgreSQL with Drizzle ORM
  • created_at, updated_at, deleted_at on every table
  • Soft deletes
  • Migrations in version control
  • Automated daily backups with tested restore
  • Connection pooling

API

  • Rate limiting per IP and per API key
  • Machine-readable error codes
  • Cursor-based pagination
  • API versioning (/api/v1/)
  • OpenAPI documentation
  • Zod validation on all inputs
  • Standard rate limit headers

Email

  • Resend or Mailgun for delivery
  • Welcome sequence
  • Email verification
  • Payment confirmation
  • Failed payment recovery (3-email sequence)
  • Cancellation confirmation
  • React Email HTML templates with plain text fallback

Monitoring

  • Sentry error tracking with source maps
  • Uptime monitoring with 1-minute alert threshold
  • Structured JSON logs with correlation IDs
  • Health check endpoint
  • Alert channel (Telegram or similar)

Security

  • HTTPS enforced at infrastructure level
  • Content Security Policy (start report-only)
  • Parameterized queries (ORM)
  • Secrets in environment variables only
  • npm audit in CI
  • Rate limiting on all mutating endpoints
  • Server-side input validation

SEO and Analytics

  • Server-side rendered landing pages
  • Dynamic sitemap.xml
  • robots.txt
  • Open Graph tags on all public pages
  • GA4 server-side tracking
  • Structured data (JSON-LD)
  • Canonical URLs

If you're building a SaaS for the EU market, you'll run into every item on this list โ€” plus the EU-specific ones like VAT compliance and GDPR that didn't make it into the generic sections. I've shipped this stack across vatnode, HTPBE?, pi-pi, and pikkuna.fi. The checklist isn't theory; it's what I actually verify before I consider a product launch-ready.

If you need a senior developer who can own this end-to-end โ€” architecture through launch and beyond โ€” get in touch. I build production-ready products, not MVPs that need to be rewritten in six months. Available for freelance projects and long-term engagements.

Iurii Rogulia

Working on something like this?

MVP Development

Need a production-ready SaaS built from scratch? I deliver the full stack โ€” auth, billing, background jobs, and deployment โ€” in 6โ€“8 weeks.

More about this service

Relevant client work

View all projects
vatnode โ€” EU VAT Validation API
vatnode โ€” EU VAT Validation API
January 19, 2026
vatnode โ€” EU VAT Validation API

Developer-first SaaS API for EU VAT validation via VIES with Redis caching, change monitoring, and webhook notifications.

HTPBE? โ€” Has This PDF Been Edited?
HTPBE? โ€” Has This PDF Been Edited?
September 25, 2024
HTPBE? โ€” Has This PDF Been Edited?

SaaS platform for PDF authenticity verification with a public REST API.

HTPBE? โ€” Internal Admin Dashboard
HTPBE? โ€” Internal Admin Dashboard
March 15, 2026
HTPBE? โ€” Internal Admin Dashboard

Role-gated admin dashboard for the HTPBE? SaaS platform โ€” real-time KPIs, per-user quota tracking, and a zero-dependency bar chart, all server-rendered via

What clients say

โ€œ

I had a validated idea and a deadline tied to an accelerator demo day, but no product. Iurii helped me cut the scope down to what actually needed to exist for launch and pushed back hard on the features I thought I needed but didn't. We shipped a working MVP in six weeks with real users on it by demo day. The honest scoping at the start is what saved the timeline โ€” it would have been easy to let me build twice as much and miss the date.

Aino Virtanen ๐Ÿ‡ซ๐Ÿ‡ฎ

Founder

Stack

Next.jsTypeScript

Databases

PostgreSQL

Topics

MVPProductScopeSaaS
โ€œ

I'd built most of our MVP with Cursor and it looked finished โ€” it compiled, the tests were green, the demo worked. It just wouldn't survive real users. Iurii took it over and found what I couldn't see: authentication handled four different ways, tests that only asserted what the code already did, and a dependency list half of which was unused. He didn't rewrite it from scratch โ€” he told me honestly what was salvageable, ripped out the dead code, and got it to something a real team could build on. Two weeks and it went from 'looks done' to actually shippable.

Sebastian Falk ๐Ÿ‡ธ๐Ÿ‡ช

Founder

Stack

Next.jsTypeScript

Topics

Technical DebtAICode ReviewArchitecture
โ€œ

We wanted to add an AI feature that turns messy user notes into structured records, but our first attempt returned unpredictable JSON that broke the app half the time. Iurii rebuilt it using structured outputs against the OpenAI API with proper validation, so the data is always shaped the way our database expects. He also added a fallback path for when the model is unsure instead of letting it guess. It's been running in production for a month with no manual cleanup.

Bram de Vries ๐Ÿ‡ณ๐Ÿ‡ฑ

Product Lead

Stack

Next.jsTypeScript

Services

OpenAI

Topics

AILLMStructured Outputs

Related articles

Stripe Webhooks: Idempotency, Retries, and Queue Setup
January 2, 2026ยท 11 min
Stripe Webhooks: Idempotency, Retries, and Queue Setup

Stripe webhook production architecture: idempotency keys in PostgreSQL and Redis, BullMQ queue, signature verification in Next.js App Router โ€” with full

Stack

Next.jsTypeScriptNode.js

Libraries

BullMQDrizzle ORMioredis

Databases

PostgreSQLRedis

Services

Stripe

Topics

SaaSWebhooksArchitecturePayments
Turborepo Monorepo: Next.js and Hono in One Repo With Shared Types
August 4, 2025ยท 13 min
Turborepo Monorepo: Next.js and Hono in One Repo With Shared Types

Turborepo monorepo with Next.js and Hono, sharing one set of TypeScript types across frontend and backend.

Stack

Next.jsTypeScriptHonoTurborepoNode.jsDocker

Libraries

Drizzle ORMZod

Databases

PostgreSQLRedis

Services

Vercel

Topics

ArchitectureMonorepoSaaS
JWT vs Sessions vs OAuth: Which to Use for SaaS Auth
May 14, 2026ยท 15 min
JWT vs Sessions vs OAuth: Which to Use for SaaS Auth

JWT vs sessions vs OAuth for SaaS: token invalidation, mobile clients, refresh token rotation, and a decision matrix to pick the right auth pattern.

Stack

Next.jsTypeScriptNode.js

Libraries

Better Auth

Databases

PostgreSQLRedis

Topics

SaaSAuthArchitectureSecurity
Real-Time Dashboard in Next.js with TanStack Query + Zustand
April 16, 2026ยท 12 min
Real-Time Dashboard in Next.js with TanStack Query + Zustand

Real-time Next.js admin dashboard: TanStack Query polling, Zustand for filter state, Sentry error boundaries per panel, and a zero-dependency bar chart.

Stack

Next.jsReactTypeScript

Libraries

TanStack QueryZustandDrizzle ORM

Services

SentryStripe

Topics

Admin DashboardSaaSData VisualizationArchitecture
Preventing Overselling: Inventory Locks Under Concurrent Checkouts
July 31, 2026ยท 13 min
Preventing Overselling: Inventory Locks Under Concurrent Checkouts

Prevent overselling under concurrent checkouts: reservations vs hard decrements, SELECT FOR UPDATE, deadlock-safe multi-line carts, and the payment window.

Stack

Next.jsTypeScriptNode.js

Databases

PostgreSQLRedis

Topics

E-commercePaymentsArchitectureSaaS