One language from browser to backend, typed end to end.
TypeScript and Node.js engineers who build typed services — API layers, event consumers and back-ends-for-front-end on the stack behind Odeontech’s own products.
- TypeScript
- Node.js
- NestJS
- Fastify
- zod
- Prisma
TypeScript is the default language at Odeontech.
It runs the API layers and web front-ends of the platforms we operate ourselves, and it is the first thing we reach for when a client’s product needs a service, a worker or a UI that has to stay honest for years. The reason is not fashion. One language across the whole surface means one set of types, one test runner, one review culture — and a shared contract between the engineer who writes the endpoint and the one who calls it.
Node.js earns its place for I/O-bound work: HTTP APIs, gateways, event consumers, webhooks, real-time channels. Its event loop is built for many concurrent connections, and the ecosystem covers almost every integration an enterprise estate throws at you.
We are equally clear about where it stops. CPU-heavy batch work, long-running numeric jobs and strict-latency trading paths go to Java, Go or Python — and we say so in the architecture review rather than forcing a runtime to do what it was not built for.
Six things we ship in TypeScript.
The same patterns on our own products and on client work.
Typed REST and GraphQL APIs
NestJS or Fastify services with schemas declared once — in zod or OpenAPI — and enforced at the boundary, in the tests and in the generated client. The contract is the code, not a wiki page.
Back-ends-for-front-end
A thin BFF in front of legacy and third-party systems, shaping payloads for web and mobile, owning auth and caching, and hiding the estate’s rough edges from the product team.
Event consumers and workers
Kafka and RabbitMQ consumers with idempotent handlers, dead-letter queues and back-pressure. The parts that are easy to get wrong on the first attempt — retries, partial writes, ordering — reviewed before the first deploy.
Integration layers
Adapters for SAP, Salesforce, payment providers and carrier APIs, wrapped in typed clients with circuit breakers and structured error mapping, so downstream failures are visible and contained.
Shared monorepos
pnpm or Turborepo workspaces with front-end, API and shared packages in one place, so a type change in the domain model breaks the build — not production.
Internal tooling and CLIs
Admin consoles, migration scripts, data back-fills and operational tooling written in the same language as the product, reviewed to the same standard, and owned by the same team.
When to choose it.
Choose TypeScript on Node.js when the work is dominated by I/O — APIs, integrations, orchestration, real-time features — and when the same team owns the front-end. It is a direct path from a product decision to a running service, and the ecosystem gives you a well-supported answer for authentication, queues, observability and testing without writing them yourself. It suits product teams that ship often and want one hiring profile across the stack.
Do not choose it for CPU-bound computation, heavy numeric pipelines or workloads that need fine control over memory and threads. If your organisation is standardised on the JVM or .NET with the operational tooling to match, a Node.js island adds cost without adding much. And if strict latency under sustained CPU load is the requirement, Go or Java will serve you better — we will tell you which.
A route whose contract lives in the code.
const Redeem = z.object({
memberId: z.string().uuid(),
points: z.number().int().positive(),
idempotencyKey: z.string().min(16),
});
app.post('/redeem', { schema: { body: Redeem } }, async (req, reply) => {
// handler receives a typed body — no manual parsing, no any
const result = await ledger.redeem(req.body, { traceId: req.id });
return reply.code(result.duplicate ? 200 : 201).send(result);
});Where Node.js sits in an Odeontech estate.
What the TypeScript codebases we hand over look like.
- Strict mode, no escape hatches.
strict: true, noanyoutside a declared adapter boundary, and lint rules that fail the build rather than warn. - Schemas at the edge. Every inbound payload is validated with zod or JSON Schema; every outbound contract is generated from the same source and published to consumers.
- Tests that mirror the risk. Unit tests for domain logic, contract tests for every integration, and Playwright or k6 where the failure would be visible to a customer.
- Observability from the first commit. OpenTelemetry traces, structured JSON logs with request ids, and a dashboard with SLOs before the service takes traffic.
- Idempotent by default. Handlers that touch money, points or stock accept an idempotency key and survive a retry without a duplicate write.
- Dependency hygiene. Lockfiles committed, Renovate or Dependabot on, and SCA scanning in CI — the supply chain is part of the security review.
- Performance measured, not assumed. Event-loop lag, heap and p95 latency are on the dashboard, and load tests run before a release that changes a hot path.
- Twelve-factor deployment. Config from the environment, secrets from a vault, containers built once and promoted through environments unchanged.
What usually ships alongside it.
- A NestJS or Fastify service template with auth, validation, tracing and health checks already wired
- OpenAPI or GraphQL schema, generated typed clients, and contract tests for every consumer
- A monorepo layout with shared domain types between web, mobile and API
- CI pipeline with type-check, lint, tests, SCA scan and container build as separate gates
- Runbooks and dashboards for the service — the on-call engineer can read the trace before reading the code
Frequently asked questions
Neighbouring technologies and the services that use them.
Need TypeScript engineers for production systems?
Tell us the shape of the work — a new service, a BFF, an integration layer — and the seniority you need. We will propose a named team or place specialists into yours.