As an independent developer with over two years of experience in the freelance wilderness, I've learned that the tech stack you choose can make or break your project—and sometimes your sanity. Today, I want to share why I built Apex Kit, my full-stack monorepo template that has become my secret weapon for client projects.
Picture this: I landed a gig with a client who needed a solution for a specific problem. The catch? They specifically requested Vue because they wanted to use technology they understood. Fair enough—client comfort is important!
The project had a clear division of responsibilities:
This is where things got interesting. As the TypeScript enthusiast I am, I found myself defining types for everything in the API. And every time the backend changed (which was... often), guess who had to update their code? Yep, yours truly.
I needed a better way to handle this dance between frontend and backend. That's when I decided to create a stack specifically designed for this scenario—and honestly, for my own peace of mind.
When the client requested Vue, I was actually pretty happy about it. Vue has always been a solid choice for several reasons:
But the most important factor? The client understood it. When working as a freelancer, it's not just about using the coolest tech—it's about creating solutions that clients can maintain long after your contract ends.
"But wait," you might ask, "why not use Nuxt? Isn't it the go-to meta-framework for Vue?"
That's a valid question! Nuxt is awesome, and I've used it on other projects. But for this particular case, I made a conscious decision to skip it for a few reasons:
Overhead vs. Need: Nuxt adds a layer of complexity that wasn't necessary for what we were building. Why add extra weight when a simpler solution would do?
Control and Flexibility: I wanted granular control over the project structure without being locked into Nuxt's conventions. Sometimes you just need to organize things your way!
Backend Integration: Since we were building a custom API integration with clear type definitions, many of Nuxt's advantages in data fetching and server-side logic weren't as relevant.
Build Performance: With Vite directly, builds were lightning-fast without the additional abstraction layer.
Don't get me wrong—Nuxt is fantastic for many projects! But as a freelancer, I've learned to be pragmatic about choosing the right tool for each specific job rather than defaulting to the full-featured option.
This is where things get interesting! I decided to structure the project as a monorepo using Turborepo. If you haven't explored monorepos yet, they're a game-changer for projects with interconnected pieces.
Here's why this approach was a lifesaver:
The key insight was realizing I could keep all API types in a tRPC client/server that both frontend and backend reference. This meant:
apex-kit/
├── apps/
│ ├── backend/ # Hono API running on Cloudflare Workers
│ └── frontend/ # Vue 3 application
└── packages/
└── .../ # Common configs and utilities
This structure eliminated the constant back-and-forth of "the API changed, now I need to update my frontend types." With tRPC in the mix, type safety became automatic!
Turborepo made it incredibly simple to run both frontend and backend simultaneously with a single command:
pnpm run dev
This would kick off both services with proper watching of shared code. When I made changes to shared types, both frontend and backend would update automatically. And of course, it will show type errors.
Turborepo's caching is absurdly efficient. After the first build, subsequent builds only processed what changed—cutting build times dramatically. For a freelancer juggling multiple tasks, this time-saving is golden.
The beautiful trio that made this stack special:
Vue 3 was already client-mandated, but I was thrilled about it. The Composition API made component logic cleaner and more reusable. Combined with TypeScript, it provided an excellent foundation for a maintainable frontend.
For the backend, I needed something lightweight yet powerful. Hono running on Cloudflare Workers was the perfect fit:
Finally, the piece that tied everything together: tRPC. If you haven't used tRPC yet, it's a game-changer for type-safe APIs. Here's what made it invaluable:
Here's a quick example of how clean the code becomes:
// Backend procedure definition
const appRouter = apex({
greeting: publicProcedure
.input(
z.object({
name: z.string(),
}),
)
.query(({ input }) => {
return {
message: `Hello, ${input.name}!`,
timestamp: new Date(),
};
}),
});
// Frontend usage with full type inference
const result = await trpc.greeting.query({ name: "Client" });
console.log(result.value.message); // TypeScript knows this exists!
Need to handle complex data fetching? No problem! TanStack Query is a game-changer for data fetching in Vue.
<script setup lang="ts">
import { trpc } from "@/api/trpc";
import { useQuery } from "@tanstack/vue-query";
const {
data: greeting,
error,
isLoading,
} = useQuery({
queryFn: async () => {
const result = await trpc.greeting.query({ name: "Client" });
return result;
},
queryKey: ["greeting"],
});
</script>
<template>
<div v-if="isLoading">Loading post...</div>
<div v-else-if="error" class="text-red-500">Error loading greeting: {{ error }}</div>
<div v-else>{{ greeting?.message }} - {{ greeting?.timestamp }}</div>
</template>
This stack fundamentally changed how I communicated with the client's backend team. Instead of the constant cycle of:
We now had a much smoother workflow:
This approach caught countless potential bugs before they happened. When the API contract changed, TypeScript immediately flagged every place in the frontend that needed updates. No more runtime surprises!
What started as a solution for one client project evolved into Apex Kit—my go-to template for freelance projects. It's designed specifically for the freelancer scenario where you need:
The best part? This stack isn't just theoretical—it's battle-tested in real client work. It's saved me countless hours of debugging and made maintenance so much easier.
As a freelancer, this project reinforced some key principles:
The stack I built wasn't just about technical elegance—it was about creating a sustainable workflow that made my life as a freelancer easier while delivering excellent results for clients.
And isn't that what good technology choices should do?
If you're curious about trying this stack yourself, check out Apex Kit on GitHub. I'd love to hear your thoughts or questions in the comments!
Related Posts