Developer Tools

Building a FastAPI + Vue 3 App in 2026: Lessons From Shipping a Production CRM

{
“title”: “FastAPI + Vue 3 in 2026: Lessons From Shipping a Production CRM”,
“meta_description”: “Real lessons from building a production CRM with FastAPI and Vue 3 on homelab infrastructure. Performance, security, and deployment insights.”,
“content_html”: “

Two years ago, I was staring at a HubSpot free-tier wall. Contact limits, feature gates, and a pricing page that made my head spin. So I did what any self-respecting homelab nerd would do: I built our own CRM.

nn

The stack? FastAPI on the backend, Vue 3 on the frontend, PostgreSQL for storage, and a Mac mini orchestrator running everything from our homelab. No venture capital. No enterprise budget. Just a problem that needed solving and a stubborn refusal to accept someone else’s limitations.

nn

That CRM is now handling real contacts, real deals, and real automation workflows for our business. And along the way, I learned a lot about what works, what doesn’t, and what I’d absolutely do differently next time.

nn

Why FastAPI + Vue 3 in 2026?

nn

The framework landscape has shifted dramatically since the last time I made this call. FastAPI’s async-native design was always its killer feature, but the 2024-2026 updates made it genuinely production-ready for teams that care about developer experience.

nn

Pydantic v2, now the serialization standard in FastAPI 0.115+, delivered massive performance gains. We’re talking 2-4x faster request parsing on large payloads. For a CRM importing hundreds of contacts at once, that difference between “works fine” and “actually fast” is real.

nn

On the frontend, Vue 3’s Composition API with <script setup> is no longer the experimental option. It’s the standard. Combined with Pinia for state management and VueUse’s 150+ composables, the boilerplate that used to kill productivity has largely disappeared.

nn

Here’s the cost reality that pushed us toward custom: SaaS CRM alternatives run $200-500/month for SMBs. Our homelab deployment? Electricity and a Mac mini we already owned. Gartner reported in 2025 that 65% of mid-market companies are building custom internal tools for exactly this reason — data privacy, integration needs, and cost control.

nn

But this stack isn’t for everyone. If you need heavy admin UIs, complex drag-and-drop workflows, or enterprise-grade component libraries, React or Angular might serve you better. FastAPI + Vue 3 shines when you want speed of development, clean APIs, and a frontend that doesn’t fight you.

nn

Architecture Decisions — What We Got Right (and Wrong)

nn

The biggest win was choosing PostgreSQL over SQLite from day one. SQLite works for prototypes. For production, you need concurrency handling, backup tooling, and extensions like full-text search. PostgreSQL delivers all three without the overhead of something like MongoDB for a relational workload.

nn

FastAPI’s dependency injection pattern became our backbone. Database sessions, authentication context, configuration values — all injected through Depends(). It keeps code testable and makes swapping implementations trivial.

nn

On the frontend, we structured Vue 3 around composables rather than mixins. Composables are just functions that encapsulate reactive state and logic. They compose cleanly, test independently, and don’t suffer from mixin’s namespace collision problems. Pinia stores handle global state, and Vue Router manages navigation with lazy-loaded routes.

nn

Here’s where we messed up: authentication. We initially built a JWT refresh token system — the kind you see in every tutorial. It was over-engineered for a CRM used by three people in one office. We switched to session-based auth with httpOnly cookies. Simpler, more secure, and honestly, what we should have done first.

nn

Another win: the Pydantic v2 migration. Bulk contact imports saw ~40% request latency reduction after the switch. That’s not a micro-optimization. That’s the difference between a tool people enjoy using and one they tolerate.

nn

The nginx reverse proxy deserves its own mention. TLS termination, rate limiting, subdomain routing — all handled cleanly. Our CRM lives at crm.helix.internal, and nginx routes everything correctly without the app ever knowing about the infrastructure.

nn

Production Deployment — Homelab to Real Traffic

nn

Running a production app on homelab infrastructure sounds like a joke until it isn’t. Our stack: PM2 for process management, Uvicorn as the ASGI server, nginx as the reverse proxy, and PostgreSQL on a dedicated worker node.

nn

PM2’s cluster mode handles process management beautifully. Auto-restart on failure, log rotation, zero-downtime reloads. For a team of one, this is the difference between “the app went down at 2 AM” and “I woke up to a notification and a fixed system.”p>nn

Uvicorn worker configuration matters more than most guides admit. We run with --workers set to match our Mac mini’s core count, using uvicorn.workers.UvicornWorker for async support. The default sync workers will bottleneck you on I/O-bound endpoints.

nn

WebSocket support through nginx required specific configuration — proxy_http_version 1.1, proxy_set_header Upgrade, and proxy_set_header Connection headers. Real-time notifications for deal updates and contact changes work reliably now.

nn

Database backups use pg_dump on a cron schedule with offsite sync to our Second Brain infrastructure. Monitoring includes a /api/health endpoint, structured logging, and error tracking. SSL/TLS runs through Let’s Encrypt with certbot automation, HSTS headers, and we’re evaluating certificate pinning for the next iteration.

nn

Performance Lessons From Real Usage

nn

N+1 query problems hit us hard in the early days. SQLAlchemy doesn’t save you from yourself. We implemented query detection and switched to eager loading strategies — joinedload for relationships we always fetch together, selectinload for collections.

nn

Frontend performance got a massive boost from route-level code splitting. Vue Router’s dynamic imports reduced our initial bundle from 2.1MB to 680KB. That’s not just a number — it’s the difference between a snappy app and one that feels sluggish on first load.

nn

FastAPI response caching through a custom @cache_control decorator handles our read-heavy endpoints. Contact lists, deal stages, workflow definitions — things that don’t change every second. The decorator adds cache headers and checks a simple in-memory cache before hitting the database.

nn

We chose Server-Sent Events over WebSockets for CRM notifications. Simpler to implement, more reliable over unstable connections, and sufficient for our use case. WebSockets are

Stay in the loop

Get the next deep dive before it hits search.

RodyTech publishes practical writing on AI systems, infrastructure, and software that teams can actually ship. Subscribe for new posts without waiting for an algorithm to surface them.

  • One useful email when a new article is worth your time
  • Hands-on notes from real builds, deployments, and ops work
  • No generic growth funnel copy, just the writing
Browse all articles More in Developer Tools

Rody

Founder & CEO · RodyTech LLC

Founder of RodyTech LLC — building AI agents, automation systems, and software for businesses that want to move faster. Based in Iowa. I write about what I actually build and deploy, not theory.

Next step

Turn one article into a working reading loop.

Keep the context warm: subscribe for new writing, revisit the archive, or stay inside the same topic while the thread is still fresh.

Explore the archive More Developer Tools
Keep reading
Nginx Reverse Proxy on Steroids: How I Route 10 Internal Services From One Server Why Every Developer Should Learn Ansible in 2026 (And How to Start in a Weekend)

No comments yet

Leave a comment

Your email address will not be published. Required fields are marked *