When There Are Too Many Microservices
\n\nYou wrote your first microservice in TypeScript. It works. Then a second, third, tenth. And suddenly you notice that more time is spent not on writing business logic, but on making sure the microservices can even communicate with each other. Timeouts, retries, load balancing, traffic encryption, metrics — all of this needs to be implemented in each service separately.
\n\nThis is where Service Mesh enters the scene — a technology that moves all communication infrastructure out of the code into a separate layer. And yes, it works great with TypeScript microservices. In this article, we'll break down how it works in practice, with real code examples.
\n\nWhat is Service Mesh in Simple Terms
\n\nImagine each of your TypeScript microservices is an employee in a large office. Without Service Mesh, each employee has to lay their own cables, set up their phone, and make sure they aren't being eavesdropped on. With Service Mesh, a special IT department handles this, connecting a "personal assistant" (sidecar proxy) to each employee.
\n\nIn the technology world, this assistant is called Envoy Proxy, and an orchestrator like Istio or Linkerd manages the entire infrastructure. The best part is that your TypeScript code remains virtually untouched.
\n\nKey Capabilities of Service Mesh
\n\n- \n
- Traffic Management — intelligent routing, A/B testing, canary releases \n
- Security — automatic mTLS encryption between services \n
- Observability — metrics, logs, tracing out of the box \n
- Resilience — retries, circuit breaker, timeouts at the infrastructure level \n
Why TypeScript Microservices Particularly Benefit from Service Mesh
\n\nTypeScript is a language with a powerful type system, but its primary runtime environment (Node.js) is not always known for I/O performance. When you write a microservice in TypeScript, every extra HTTP request, every manual implementation of retries or TLS handshake adds additional load to the Event Loop.
\n\nService Mesh takes this load upon itself. Your TypeScript code remains clean: it simply receives requests and responds to them, while Envoy handles all the "plumbing." This is especially important when you have dozens of microservices — each with its own retry logic and timeouts.
\n\nPractical Example: TypeScript Microservice Under Service Mesh
\n\nLet's imagine we have two TypeScript microservices: user-service and order-service. Order-service needs to get user data. Without Service Mesh, we would write something like:
// Without Service Mesh — manual HTTP management\nimport axios from 'axios';\n\nclass UserClient {\n private baseUrl = 'http://user-service:3000';\n private retries = 3;\n\n async getUser(id: string): Promise<User> {\n for (let i = 0; i < this.retries; i++) {\n try {\n const response = await axios.get(`${this.baseUrl}/users/${id}`, {\n timeout: 5000,\n httpsAgent: new https.Agent({ rejectUnauthorized: true })\n });\n return response.data;\n } catch (error) {\n if (i === this.retries - 1) throw error;\n await delay(1000 * Math.pow(2, i));\n }\n }\n }\n}\n\nAnd with Service Mesh, this becomes:
\n\n// With Service Mesh — just an HTTP request\nimport axios from 'axios';\n\nclass UserClient {\n async getUser(id: string): Promise<User> {\n const response = await axios.get(`http://user-service/users/${id}`);\n return response.data;\n }\n}\n\nNotice the difference? All the complexity — retries, timeouts, encryption — is