Modern web applications demand an intricate blend of scalability, rapid development, and rich user experiences. Integrating a diverse set of technologies effectively can be a challenge, yet it's often the key to unlocking powerful, robust solutions. This post demonstrates how to architect a full-stack TypeScript application by harmonizing Moleculer for microservices, Google Cloud Run for serverless deployment, PocketBase for backend simplicity, the Google Gemini API for AI capabilities, alongside advanced frontend state management, build tools, and Progressive Web App (PWA) features.
The Integrated Architecture: An AI-Powered Creative Assistant
Imagine building an "AI Creative Assistant" PWA that helps users generate content ideas, summaries, or code snippets. Our architecture for this application would look like this:
- Frontend (PWA): A modern UI built with a framework like React/Vue/Svelte, bundled by Vite, offering an installable, offline-capable experience, managing state with Zustand/TanStack Query.
- API Gateway (Moleculer Service): A Moleculer service acting as the primary entry point for the frontend, routing requests to various backend microservices.
- Backend Microservices (Moleculer Services):
ai-service: Responsible for interacting with the Google Gemini API.data-service: Handles all CRUD operations with PocketBase for user data, generated content, and settings.auth-service: Manages user authentication, leveraging PocketBase's built-in capabilities.
- Persistence (PocketBase): A lightweight, self-hostable backend that provides a database, authentication, and real-time subscriptions.
- AI Engine (Google Gemini API): The core intelligence providing content generation and understanding.
- Deployment (Google Cloud Run): All Moleculer services and PocketBase deployed as scalable, serverless containers.
Service Mesh with Moleculer on Cloud Run
Moleculer is a powerful, TypeScript-friendly microservices framework that brings resilience, auto-discovery, and event-driven communication to your backend. Running Moleculer services on Google Cloud Run offers unparalleled scalability and cost efficiency.
Each Moleculer service (ai-service, data-service, auth-service, api-gateway) is a separate container. They communicate via a Transporter (e.g., NATS, Redis Stream) that Cloud Run instances can all access. For simplicity in Cloud Run, NATS is a strong candidate, deployed as a separate, persistent service (e.g., on a Compute Engine VM or managed NATS service).
Here's a basic ai-service structure:
// services/ai.service.ts
import { Service, ServiceBroker } from 'moleculer';
import { GoogleGenerativeAI } from '@google/generative-ai';
interface AIServiceSettings {
apiKey: string;
}
export default class AIService extends Service<AIServiceSettings> {
private genAI: GoogleGenerativeAI;
public constructor(broker: ServiceBroker) {
super(broker);
this.parseServiceSchema({
name: 'ai',
settings: {
apiKey: process.env.GEMINI_API_KEY || '',
},
created: this.serviceCreated,
actions: {
generateContent: {
params: {
prompt: 'string',
},
handler: this.generateContent,
},
},
});
}
private serviceCreated() {
if (!this.settings.apiKey) {
this.logger.error('GEMINI_API_KEY is not set!');
throw new Error('Missing Gemini API Key');
}
this.genAI = new GoogleGenerativeAI(this.settings.apiKey);
}
private async generateContent(ctx: any): Promise<string> {
const { prompt } = ctx.params;
this.logger.info(`Generating content for prompt: ${prompt}`);
const model = this.genAI.getGenerativeModel({ model: "gemini-pro" });
const result = await model.generateContent(prompt);
const response = result.response;
return response.text();
}
}
Each Moleculer service is containerized using Docker and deployed to Cloud Run, scaling independently based on demand. The API Gateway service exposes HTTP endpoints that proxy requests to these internal services.
Persistence with PocketBase
PocketBase simplifies backend development by offering a single Go binary that provides a database (SQLite), file storage, real-time subscriptions, and a robust authentication system. For a production Cloud Run setup, PocketBase should be deployed on a separate, persistent Compute Engine VM, or a dedicated instance with persistent disk, accessible securely by your Moleculer data-service.
The data-service in Moleculer acts as an abstraction layer, handling all interactions with PocketBase:
// services/data.service.ts
import { Service, ServiceBroker } from 'moleculer';
import PocketBase from 'pocketbase';
interface DataServiceSettings {
pocketbaseUrl: string;
}
export default class DataService extends Service<DataServiceSettings> {
private pb: PocketBase;
public constructor(broker: ServiceBroker) {
super(broker);
this.parseServiceSchema({
name: 'data',
settings: {
pocketbaseUrl: process.env.POCKETBASE_URL || 'http://localhost:8090',
},
created: this.serviceCreated,
actions: {
getContents: this.getContents,
createContent: {
params: {
userId: 'string',
title: 'string',
content: 'string',
},
handler: this.createContent,
},
},
});
}
private serviceCreated() {
this.pb = new PocketBase(this.settings.pocketbaseUrl);
// Consider admin login here if actions require admin privileges
// await this.pb.admins.authWithPassword('admin@example.com', 'password123');
}
private async getContents(ctx: any): Promise<any[]> {
// Assuming 'contents' is a collection in PocketBase
return await this.pb.collection('contents').getFullList();
}
private async createContent(ctx: any): Promise<any> {
const { userId, title, content } = ctx.params;
return await this.pb.collection('contents').create({ userId, title, content });
}
}
This separation keeps PocketBase logic isolated and allows for flexible data access patterns within your microservices.
AI Smarts with Google Gemini API
The Google Gemini API provides advanced generative AI capabilities. Our ai-service integrates directly with it. By centralizing AI interactions in a dedicated Moleculer service, you manage API keys securely and apply rate limiting or caching strategies transparently.
Calling the ai.generateContent action from your frontend (via the API Gateway) or other Moleculer services becomes straightforward:
// Example of calling from another Moleculer service or API Gateway
// ctx.call('ai.generateContent', { prompt: 'Write a blog post about web development trends in 2025.' });
// Example using a simple fetch from frontend (via API Gateway)
async function generateIdea(prompt: string) {
const response = await fetch('/api/ai/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt }),
});
if (!response.ok) throw new Error('AI generation failed');
return response.json();
}
Frontend Experience: PWA, State Management & Build Tools
Our frontend, built with a framework and TypeScript, utilizes modern tooling to deliver a superior user experience.
- Build Tools & Bundlers (Vite): Vite offers lightning-fast development, HMR, and optimized production builds thanks to ES modules and Rollup. It handles TypeScript out-of-the-box, ensuring a smooth developer experience.
- Progressive Web Apps (PWA): By adding a Web App Manifest and a Service Worker, your application becomes installable, offering offline capabilities, push notifications, and an app-like feel. This significantly enhances user engagement and retention. A simple
vite-plugin-pwacan automate much of this setup. - State Management (Zustand/TanStack Query): For managing complex async data and UI state:
- TanStack Query (React Query, Vue Query, etc.) excels at server state management: fetching, caching, synchronizing, and updating server data. It simplifies optimistic updates, background refetching, and error handling.
- Zustand (or a similar lightweight global state manager) can handle client-side UI state not directly tied to server data, such as modal visibility or form inputs.
// store/contentStore.ts (using Zustand for UI state example)
import { create } from 'zustand';
interface ContentState {
isLoading: boolean;
editorContent: string;
setEditorContent: (content: string) => void;
// ... other UI states
}
export const useContentStore = create<ContentState>((set) => ({
isLoading: false,
editorContent: '',
setEditorContent: (content) => set({ editorContent: content }),
}));
// hooks/useContents.ts (using TanStack Query for server state example)
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { createContent, getContents } from '../api'; // Your API functions
export function useContents() {
const queryClient = useQueryClient();
const { data: contents, isLoading } = useQuery({
queryKey: ['contents'],
queryFn: getContents,
});
const createContentMutation = useMutation({
mutationFn: createContent,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['contents'] }); // Refetch after successful creation
},
});
return { contents, isLoading, createContentMutation };
}
This combination provides a highly responsive, performant, and maintainable frontend.
Bringing It All Together
This integrated architecture offers significant advantages:
- Scalability & Resilience: Moleculer's microservices on Cloud Run ensure services scale independently and are resilient to failures.
- Developer Experience: TypeScript across the stack provides type safety, while PocketBase simplifies backend data management.
- Cost Efficiency: Cloud Run's pay-per-use model keeps infrastructure costs low.
- Rich User Experience: PWA features and modern state management deliver an app-like feel with high performance.
- Innovation: Easy integration of powerful AI capabilities with the Google Gemini API.
By carefully selecting and integrating these technologies, you can build a robust, high-performing, and developer-friendly application capable of handling complex requirements and delivering exceptional user value.
Conclusion
The landscape of web development is constantly evolving, with serverless, microservices, AI, and advanced frontend techniques leading the charge. By strategically combining tools like Moleculer, Google Cloud Run, PocketBase, and the Google Gemini API within a TypeScript-first PWA, developers can craft sophisticated applications that are not only powerful and scalable but also a joy to build and maintain. This integrated approach empowers teams to focus on innovation and deliver cutting-edge user experiences, setting a high bar for modern web development in 2025 and beyond.