This is the multi-page printable view of this section. Click here to print.
Seminars
1 - 2025-03-16 | Modern Full-Stack Development with TypeScript
A Comprehensive Seminar on Angular, Nest.js, PostgreSQL, Prisma, Swagger, and Docker
Introduction (10 minutes)
- Angular - For building dynamic client-side applications
- Nest.js - For creating structured and scalable server-side applications
- PostgreSQL - As our reliable and powerful database system
- Prisma - For type-safe database access
- Swagger - For API documentation and testing
- Docker - For containerization and deployment consistency
By the end of this seminar, you’ll understand how these technologies complement each other and how to leverage their combined power to build modern web applications efficiently.
Part 1: Understanding the Stack Architecture (15 minutes)
The Big Picture
Let’s start by understanding how these technologies fit together:
- Angular: Handles the presentation layer, user interactions, and client-side logic
- Nest.js: Manages server-side business logic, API endpoints, and server operations
- PostgreSQL: Stores and manages application data
- Prisma: Bridges the gap between our TypeScript code and the database
- Swagger: Documents our API for easier consumption and testing
- Docker: Packages everything into consistent, deployable containers
Why This Stack?
This stack offers several advantages:
- TypeScript Everywhere: From frontend to backend to database access
- Strong Typing: Catch errors at compile time rather than runtime
- Modular Architecture: Components are loosely coupled but highly cohesive
- Scalability: Each piece can scale independently
- Developer Experience: Consistent patterns and tooling across the stack
Real-World Applications
This stack is ideal for:
- Enterprise applications
- Complex business systems
- Applications requiring scalability
- Teams with specialized frontend and backend developers
- Projects emphasizing code quality and maintainability
Part 2: Frontend with Angular (30 minutes)
Angular Overview
Angular is a platform and framework for building single-page client applications using HTML and TypeScript.
Key concepts:
- Component-based architecture
- Dependency injection
- Reactive programming with RxJS
- Strong typing with TypeScript
Essential Angular Features
- Components: The building blocks of Angular applications
- Services: Singleton objects for shared functionality
- Modules: Organizing code into functional cohesive blocks
- Routing: Navigation between different views
- Forms: Template-driven and reactive approaches
- HttpClient: Communication with backend services
Demo: Building an Angular Component
// product.component.ts
import { Component, OnInit } from '@angular/core';
import { ProductService } from '../services/product.service';
import { Product } from '../models/product.model';
@Component({
selector: 'app-product-list',
templateUrl: './product-list.component.html',
styleUrls: ['./product-list.component.scss']
})
export class ProductListComponent implements OnInit {
products: Product[] = [];
loading = false;
error: string | null = null;
constructor(private productService: ProductService) {}
ngOnInit(): void {
this.loadProducts();
}
loadProducts(): void {
this.loading = true;
this.productService.getProducts()
.subscribe({
next: (data) => {
this.products = data;
this.loading = false;
},
error: (err) => {
this.error = 'Failed to load products';
this.loading = false;
console.error(err);
}
});
}
}
Best Practices for Angular
- Lazy loading modules for better performance
- State management with NgRx for complex applications
- Reactive programming patterns
- Component testing with Jasmine and Karma
- Style isolation and component encapsulation
Part 3: Backend with Nest.js (30 minutes)
Nest.js Overview
Nest.js is a progressive Node.js framework for building efficient, reliable, and scalable server-side applications.
Key concepts:
- Heavily inspired by Angular architecture
- Modular design
- Dependency injection
- Decorators for metadata
- Middleware support
Essential Nest.js Features
- Controllers: Handle incoming requests
- Providers/Services: Implement business logic
- Modules: Organize application structure
- Pipes: Transform and validate input data
- Guards: Control access to routes
- Interceptors: Transform responses and handle errors
Demo: Building a Nest.js Controller and Service
// products.controller.ts
import { Controller, Get, Post, Body, Param, UseGuards } from '@nestjs/common';
import { ProductsService } from './products.service';
import { CreateProductDto } from './dto/create-product.dto';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
@ApiTags('products')
@Controller('products')
export class ProductsController {
constructor(private readonly productsService: ProductsService) {}
@Get()
@ApiOperation({ summary: 'Get all products' })
@ApiResponse({ status: 200, description: 'Return all products.' })
findAll() {
return this.productsService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.productsService.findOne(+id);
}
@Post()
@UseGuards(JwtAuthGuard)
create(@Body() createProductDto: CreateProductDto) {
return this.productsService.create(createProductDto);
}
}
// products.service.ts
import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateProductDto } from './dto/create-product.dto';
@Injectable()
export class ProductsService {
constructor(private prisma: PrismaService) {}
async findAll() {
return this.prisma.product.findMany();
}
async findOne(id: number) {
return this.prisma.product.findUnique({
where: { id },
});
}
async create(data: CreateProductDto) {
return this.prisma.product.create({
data,
});
}
}
Best Practices for Nest.js
- Exception filters for consistent error handling
- Validation using class-validator
- Environment configuration management
- Testing with Jest
- Logging strategies
2 - 2025-03-23 | Modern Full-Stack Development with TypeScript (Cont.)
Introduction
This seminar is the second part of the Modern Full-Stack Development with TypeScript seminar.
Part 4: Database Layer with PostgreSQL and Prisma (25 minutes)
PostgreSQL Overview
PostgreSQL is a powerful, open-source object-relational database system with over 30 years of active development.
Key features:
- Strong standards compliance
- Extensibility
- Robust transaction support
- Multi-version concurrency control
- Advanced data types and indexing
Prisma: Modern Database Access
Prisma is an open-source database toolkit that includes:
- Prisma Client: Auto-generated and type-safe query builder
- Prisma Migrate: Declarative data modeling and migration system
- Prisma Studio: GUI to view and edit data
Demo: Defining a Data Model with Prisma
// schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
password String
role Role @default(USER)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
orders Order[]
}
model Product {
id Int @id @default(autoincrement())
name String
description String?
price Decimal @db.Decimal(10, 2)
stock Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
orderItems OrderItem[]
}
model Order {
id Int @id @default(autoincrement())
userId Int
user User @relation(fields: [userId], references: [id])
status OrderStatus @default(PENDING)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
items OrderItem[]
}
model OrderItem {
id Int @id @default(autoincrement())
orderId Int
order Order @relation(fields: [orderId], references: [id])
productId Int
product Product @relation(fields: [productId], references: [id])
quantity Int
price Decimal @db.Decimal(10, 2)
}
enum Role {
USER
ADMIN
}
enum OrderStatus {
PENDING
PROCESSING
SHIPPED
DELIVERED
CANCELLED
}
Connecting Prisma to Nest.js
// prisma.service.ts
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
async onModuleInit() {
await this.$connect();
}
async onModuleDestroy() {
await this.$disconnect();
}
}
Best Practices for Database Management
- Transaction management
- Database migration strategies
- Query optimization
- Connection pooling
- Data validation and integrity
- Seeding strategies for development and testing
Part 5: API Documentation with Swagger (15 minutes)
Swagger Overview
Swagger (OpenAPI) provides a standard, language-agnostic interface to RESTful APIs.
Benefits:
- Interactive documentation
- Client SDK generation
- Standardized API description
- Testing capabilities
Integrating Swagger with Nest.js
// main.ts
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Global validation pipe
app.useGlobalPipes(new ValidationPipe({ transform: true }));
// Swagger setup
const config = new DocumentBuilder()
.setTitle('E-commerce API')
.setDescription('The e-commerce API description')
.setVersion('1.0')
.addTag('e-commerce')
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
await app.listen(3000);
}
bootstrap();
Documenting API Endpoints
- Using decorators to document controllers and methods
- Describing data transfer objects (DTOs)
- Adding authentication requirements
- Managing API versions
Part 6: Containerization with Docker (20 minutes)
Docker Overview
Docker provides a way to package applications with all their dependencies into standardized units called containers.
Benefits:
- Consistent environments
- Isolation
- Portability
- Efficiency
- Scalability
Dockerizing Our Application
# Dockerfile
FROM node:16 AS builder
WORKDIR /app
# Copy package files and install dependencies
COPY package*.json ./
RUN npm ci
# Copy source files and build application
COPY . .
RUN npm run build
# Production stage
FROM node:16-alpine
WORKDIR /app
# Copy built application from builder stage
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./
# Set environment variables
ENV NODE_ENV production
ENV PORT 3000
# Expose application port
EXPOSE 3000
# Start the application
CMD ["node", "dist/main"]
Docker Compose for Multi-Container Setup
# docker-compose.yml
version: '3.8'
services:
api:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- '3000:3000'
depends_on:
- postgres
environment:
- DATABASE_URL=postgresql://postgres:postgres@postgres:5432/myapp
- NODE_ENV=production
- JWT_SECRET=your_jwt_secret
restart: always
client:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- '80:80'
depends_on:
- api
restart: always
postgres:
image: postgres:14
ports:
- '5432:5432'
environment:
- POSTGRES_PASSWORD=postgres
- POSTGRES_USER=postgres
- POSTGRES_DB=myapp
volumes:
- postgres-data:/var/lib/postgresql/data
restart: always
volumes:
postgres-data:
Best Practices for Containerization
- Multi-stage builds for smaller images
- Using specific versions for stability
- Security considerations
- Resource constraints
- Container orchestration with Kubernetes
Part 7: Integration and Deployment (15 minutes)
Bringing Everything Together
CI/CD Pipeline Setup
- GitHub Actions or GitLab CI
- Build and test automation
- Docker image creation and pushing
- Deployment to cloud platforms
Environment Management
- Managing environment variables
- Development, staging, and production configurations
- Secrets management
Monitoring and Logging
- Health checks and monitoring
- Centralized logging
- Performance metrics
Deployment Options
- AWS, Google Cloud, or Azure
- Kubernetes clusters
- Cloud database services
- Managed Kubernetes services
Conclusion and Q&A (15 minutes)
Summary of Key Points
- Angular and Nest.js provide a consistent TypeScript experience
- Prisma simplifies database interactions with type safety
- PostgreSQL offers a robust and reliable data store
- Swagger enhances API documentation and testing
- Docker ensures consistency across environments
Resource List
- Official documentation links
- GitHub repositories with sample code
- Recommended books and courses
- Community resources and forums
Open Floor for Questions
Thank you for attending! I’m now open to questions about any aspect of the stack we’ve covered today.
Additional Workshop Materials (Optional Add-ons)
Hands-on Lab Instructions
Step-by-step guides for:
- Setting up the development environment
- Creating an Angular application
- Building a Nest.js API
- Configuring Prisma with PostgreSQL
- Integrating Swagger documentation
- Containerizing with Docker
Code Templates and Starter Projects
- GitHub repository links to starter templates
- Common patterns and solutions
- Best practices implementation examples
Angular Series
- 100 days of Angular: https://github.com/angular-vietnam/100-days-of-angular
3 - 2025-03-30 | Modern Full-Stack Development with TypeScript (Cont.)
Introduction
This seminar is the final part of our Modern Full-Stack Development with TypeScript series.
Part 8: Deploy Docker using Ansible (25 minutes)
Ansible Overview
Ansible is an open-source automation tool that simplifies application deployment, configuration management, and task automation.
Key benefits:
- Agentless architecture (only requires SSH)
- YAML-based playbooks for easy readability
- Idempotent operations (safe to run multiple times)
- Extensive module library
- Infrastructure as Code approach
Why Ansible for Docker Deployment?
- Consistency: Ensure identical deployments across environments
- Automation: Reduce manual steps and human error
- Scalability: Easily deploy to multiple servers
- Orchestration: Coordinate complex deployment sequences
- Configuration Management: Handle environment-specific settings
Setting Up Ansible
# inventory.yml
all:
hosts:
production:
ansible_host: 192.168.1.100
ansible_user: deploy
staging:
ansible_host: 192.168.1.101
ansible_user: deploy
vars:
ansible_python_interpreter: /usr/bin/python3
Creating an Ansible Playbook for Docker Deployment
# deploy-docker.yml
---
- name: Deploy Application with Docker
hosts: all
become: true
vars:
app_name: fullstack-app
docker_compose_dir: /opt/{{ app_name }}
env: "{{ lookup('env', 'DEPLOY_ENV') | default('staging', true) }}"
tasks:
- name: Install required packages
apt:
name:
- docker.io
- docker-compose
- python3-pip
state: present
update_cache: yes
- name: Ensure Docker service is running
service:
name: docker
state: started
enabled: yes
- name: Create application directory
file:
path: "{{ docker_compose_dir }}"
state: directory
mode: '0755'
- name: Copy docker-compose file
template:
src: templates/docker-compose.{{ env }}.yml.j2
dest: "{{ docker_compose_dir }}/docker-compose.yml"
- name: Copy environment variables
template:
src: templates/.env.{{ env }}.j2
dest: "{{ docker_compose_dir }}/.env"
mode: '0600'
- name: Pull latest Docker images
command:
cmd: docker-compose pull
chdir: "{{ docker_compose_dir }}"
- name: Deploy with docker-compose
command:
cmd: docker-compose up -d
chdir: "{{ docker_compose_dir }}"
- name: Prune unused Docker images
command: docker image prune -af
register: prune_result
changed_when: "'Total reclaimed space:' in prune_result.stdout"
Environment-Specific Configuration Templates
# templates/docker-compose.production.yml.j2
version: '3.8'
services:
api:
image: {{ docker_registry }}/{{ app_name }}-api:{{ api_version }}
restart: always
ports:
- "3000:3000"
environment:
NODE_ENV: production
DATABASE_URL: ${DATABASE_URL}
JWT_SECRET: ${JWT_SECRET}
depends_on:
- postgres
networks:
- app-network
deploy:
replicas: 2
update_config:
parallelism: 1
delay: 10s
client:
image: {{ docker_registry }}/{{ app_name }}-client:{{ client_version }}
restart: always
ports:
- "80:80"
depends_on:
- api
networks:
- app-network
postgres:
image: postgres:14
volumes:
- postgres-data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USER}
POSTGRES_DB: ${DB_NAME}
networks:
- app-network
restart: always
networks:
app-network:
driver: bridge
volumes:
postgres-data:
driver: local
Ansible Roles for Reusable Components
Organize your Ansible code into roles for better maintainability:
roles/
├── docker/
│ ├── tasks/
│ │ └── main.yml
│ └── handlers/
│ └── main.yml
├── nginx/
│ ├── tasks/
│ │ └── main.yml
│ ├── templates/
│ │ └── nginx.conf.j2
│ └── handlers/
│ └── main.yml
└── app/
├── tasks/
│ └── main.yml
├── templates/
│ ├── docker-compose.yml.j2
│ └── .env.j2
└── defaults/
└── main.yml
Continuous Deployment with Ansible and CI/CD
# .github/workflows/deploy.yml
name: Deploy Application
on:
push:
branches: [main]
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install Ansible
run: |
python -m pip install --upgrade pip
pip install ansible
- name: Set up SSH key
uses: webfactory/ssh-agent@v0.7.0
with:
ssh-private-key: ${{ secrets.DEPLOY_SSH_KEY }}
- name: Run Ansible playbook
run: |
ansible-playbook -i inventory.yml deploy-docker.yml
env:
DEPLOY_ENV: production
ANSIBLE_HOST_KEY_CHECKING: False
Best Practices for Ansible Deployments
Vault for Secrets: Use Ansible Vault to encrypt sensitive data
ansible-vault create secrets.yml ansible-playbook deploy.yml --ask-vault-passDynamic Inventories: Use cloud provider plugins for dynamic server discovery
Idempotency: Ensure playbooks can be run multiple times without side effects
Tags: Use tags to run specific parts of your playbook
ansible-playbook deploy.yml --tags "update,restart"Handlers: Use handlers for actions that should only run when a change occurs
Testing: Test playbooks with Molecule before production deployment
Monitoring and Maintenance
- Set up regular health checks using Ansible
- Create playbooks for common maintenance tasks:
- Database backups
- Log rotation
- Certificate renewal
- Security updates
Rollback Strategies
# rollback.yml
---
- name: Rollback to previous version
hosts: all
become: true
vars:
app_name: fullstack-app
docker_compose_dir: /opt/{{ app_name }}
previous_version: "{{ lookup('env', 'PREVIOUS_VERSION') }}"
tasks:
- name: Update docker-compose with previous version
lineinfile:
path: "{{ docker_compose_dir }}/docker-compose.yml"
regexp: "image: .*/{{ app_name }}-api:.*"
line: " image: {{ docker_registry }}/{{ app_name }}-api:{{ previous_version }}"
- name: Restart with previous version
command:
cmd: docker-compose up -d
chdir: "{{ docker_compose_dir }}"
Conclusion and Next Steps (15 minutes)
Summary of the Full-Stack TypeScript Journey
- Angular for dynamic frontend experiences
- Nest.js for structured backend development
- PostgreSQL and Prisma for type-safe data management
- Swagger for API documentation
- Docker for containerization
- Ansible for automated deployment
Emerging Trends and Future Directions
- Serverless architectures
- Edge computing
- WebAssembly
- Micro-frontends
- GraphQL adoption
- AI-assisted development
Continuous Learning Resources
- Community forums and Discord servers
- Advanced courses and certifications
- Open-source contribution opportunities
- Tech conferences and meetups
Q&A Session
Open floor for questions about any aspect of the full-stack TypeScript ecosystem we’ve covered throughout this seminar series.