stack-1-angular-nodejs
Stack 1: Angular + TypeScript Backend (Express + Prisma)
Stack 1 is the "best balance of speed and TwinSpires comfort" option: it keeps Angular (which TwinSpires already knows), introduces a TypeScript backend on Node/Express, and standardizes on a single web language (TypeScript). While Stack 3 would be fastest for a React/Node delivery team, Stack 1 offers the best compromise between build speed and TwinSpires engineering team comfort.
1. Overview
- Frontend: Angular (TypeScript)
- Backend: NodeJS (TypeScript): Express or NestJS (see Node.js backend options comparison for details)
- Database: PostgreSQL (or MySQL) via Prisma
- Infra (example):
- Frontend: S3 + CloudFront (static hosting)
- Backend: Lambda + API Gateway (Serverless Framework) or containerized on ECS/Kubernetes
- CI/CD: GitHub Actions (separate pipelines for frontend and backend)
This stack keeps the clear FE/BE separation TwinSpires is used to from Spring Boot + Angular, but with a lighter, more web-native toolchain.
2. Architecture
At a high level:
- Angular SPA is built and deployed as static assets behind CloudFront.
- Express API runs in a serverless function or container, exposing JSON endpoints.
- Prisma manages database access and migrations with a type-safe schema.
3. Sample repo structure
Using the example repo in this project:
stack1-angular-express/
frontend/ # Angular app
src/app/
components/
services/
pages/
backend/ # NodeJS(express)
src/
routes/
services/
repositories/
db/
prisma/
schema.prisma
tech-stack-docs/ # Docusaurus documentation (this site)
Key points:
routes/: Express route handlers, thin controllers that map HTTP → service calls.services/: Business logic, orchestrating repositories and external APIs.repositories/: Data access layer using Prisma, isolated from services.db//prisma/: Database client, schema, and migrations.
4. Example vertical slice
This slice shows a simple “Todos” list: Angular calls a REST endpoint implemented in Express, which reads from the DB via Prisma.
Backend – Express route
// backend/src/routes/todos.ts
import { Router } from "express";
import { listTodos } from "../services/todoService";
const router = Router();
router.get("/", async (_req, res) => {
const todos = await listTodos();
res.json(todos);
});
export default router;
Backend – Service + Prisma
// backend/src/services/todoService.ts
import { prisma } from "../db/client";
export async function listTodos() {
return prisma.todo.findMany();
}
Frontend – Angular service
// frontend/src/app/services/todo.service.ts
import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable } from "rxjs";
export interface Todo {
id: number;
title: string;
completed: boolean;
}
@Injectable({ providedIn: "root" })
export class TodoService {
private readonly baseUrl = "/api/todos";
constructor(private http: HttpClient) {}
getTodos(): Observable<Todo[]> {
return this.http.get<Todo[]>(this.baseUrl);
}
}
Full example repo: https://github.com/MillionOnMars/tech-stack-comparison/tree/main/stack1-angular-express
This pattern scales naturally: each vertical slice adds:
- One or more Angular components/routes
- One Angular service method
- One Express route (and perhaps service + repository methods)
- Potential schema and migration changes in Prisma
5. CI/CD outline (example with GitHub Actions)
We can keep separate pipelines for frontend and backend while still treating each feature as a vertical slice.
Frontend (Angular)
- On pull request:
npm cinpm run lintnpm testnpm run build- Deploy a preview environment (e.g., temporary S3/CloudFront distro).
- On merge to
main:- Build production assets.
- Upload to S3.
- Invalidate CloudFront cache.
Backend (Express + Prisma)
- On pull request:
npm cinpm run lintnpm test- Build and run integration tests (optionally with a disposable database).
- On merge to
main:- Run Prisma migrations against a staging database.
- Build and publish Docker image or Lambda package.
- Deploy via Serverless Framework or IaC (CloudFormation/Terraform).
Vertical slice flow
- A feature branch can update both
frontend/andbackend/. - CI validates both sides together.
- Once merged, both pipelines deploy, resulting in an end-to-end slice being live.
6. Pros
- Single language across the web stack:
- TypeScript in both Angular and Express.
- Easier shared understanding of types, DTOs, and error handling.
- Minimizes frontend change:
- Keeps Angular, which the team already knows well.
- Backend is new (Node/Express), but the language (TypeScript) is conceptually close to Java: classes, interfaces, generics.
- Fast iteration:
- Express and Prisma are lightweight and productive.
- Thin vertical slices can be developed and deployed quickly.
- Serverless- and container-friendly:
- Same codebase can run on Lambda or in long-lived containers.
7. Cons & risks
- New backend runtime for Twinspires Team(Node/Express)
- Less “batteries-included” than Spring/Nest:
- More decisions about conventions (e.g., error handling, validation) unless we add small libraries.
- Medium ML/AI fit:
- TypeScript is not the primary language for ML/AI; tight integration will rely on calling out to Python services or jobs.
8. Fit for BrisNet
Stack 1 is the best fit when:
- Build speed for our team (React/Node specialists):
- High: NodeJS / Express is directly in our wheelhouse. Angular won't be the most comfortable for our team, I think, but we can adapt if necessary.
- Comfort for TwinSpires engineering (Angular/Java specialists):
- Frontend: High, because Angular remains the primary UI framework.
- Backend: Moderate, because the runtime (Node/Express) is new even if the language (TypeScript) feels familiar to Java developers.
- Want fast delivery with minimal frontend change.
- Want a single main language (TypeScript) for web development.
In other words: Stack 1 is a compromise solution if TwinSpires doesn't agree with Stack 3 (which is the most appropriate option for our team and long-term strategic vision). If the priority is speed + low risk for TwinSpires' learning curve, Stack 1 is the most straightforward path to a modern BrisNet. It keeps Angular (familiar frontend), but introduces Node.js/Express on the backend (new runtime, though TypeScript language is conceptually similar to Java). This minimizes frontend change while modernizing the backend.