NestJS Fundamentals: Day1
Quick Setup
Install NestJS CLI
npm install -g @nestjs/cli
Create project
nest new todo-app
Start development server
npm run start:dev
Generate resource (creates Controller, Service, Module, DTO, Entity)
nest generate resource todos
Basic API Structure
The generated files provide a complete CRUD API:
- GET /todos - Get all todos
- POST /todos - Create new todo
- GET /todos/:id - Get single todo
- PATCH /todos/:id - Update todo
- DELETE /todos/:id - Delete todo
Day 2: Service Layer and Data Validation
Install Validation Dependencies
npm install class-validator class-transformer
Entity Definition
// src/todos/entities/todo.entity.ts export class Todo { id: number; title: string; description?: string; completed: boolean; priority: 'low' | 'medium' | 'high'; dueDate?: Date; createdAt: Date; updatedAt: Date; constructor(partial: Partial) { Object.assign(this, partial); this.completed = this.completed || false; this.priority = this.priority || 'medium'; this.createdAt = this.createdAt || new Date(); this.updatedAt = new Date(); } }
Create Todo DTO
// src/todos/dto/create-todo.dto.ts import { IsString, IsNotEmpty, IsOptional, IsEnum, IsDateString, MaxLength } from 'class-validator'; export class CreateTodoDto { @IsString() @IsNotEmpty({ message: 'Title is required' }) @MaxLength(100, { message: 'Title must be 100 characters or less' }) title: string; @IsString() @IsOptional() @MaxLength(500, { message: 'Description must be 500 characters or less' }) description?: string; @IsEnum(['low', 'medium', 'high'], { message: 'Priority must be low, medium, or high' }) @IsOptional() priority?: 'low' | 'medium' | 'high'; @IsDateString({}, { message: 'Please provide a valid date format' }) @IsOptional() dueDate?: string; }
Update Todo DTO
// src/todos/dto/update-todo.dto.ts import { PartialType } from '@nestjs/mapped-types'; import { IsBoolean, IsOptional } from 'class-validator'; import { CreateTodoDto } from './create-todo.dto'; export class UpdateTodoDto extends PartialType(CreateTodoDto) { @IsBoolean({ message: 'Completed status must be true or false' }) @IsOptional() completed?: boolean; }
Service Implementation
// src/todos/todos.service.ts import { Injectable, NotFoundException } from '@nestjs/common'; import { CreateTodoDto } from './dto/create-todo.dto'; import { UpdateTodoDto } from './dto/update-todo.dto'; import { Todo } from './entities/todo.entity'; @Injectable() export class TodosService { private todos: Todo[] = []; private currentId = 1; create(createTodoDto: CreateTodoDto): Todo { const todo = new Todo({ id: this.currentId++, title: createTodoDto.title, description: createTodoDto.description, priority: createTodoDto.priority || 'medium', dueDate: createTodoDto.dueDate ? new Date(createTodoDto.dueDate) : undefined, }); this.todos.push(todo); return todo; } findAll(): Todo[] { return this.todos; } findOne(id: number): Todo { const todo = this.todos.find(todo => todo.id === id); if (!todo) { throw new NotFoundException(`Todo with ID ${id} not found`); } return todo; } update(id: number, updateTodoDto: UpdateTodoDto): Todo { const todoIndex = this.todos.findIndex(todo => todo.id === id); if (todoIndex === -1) { throw new NotFoundException(`Todo with ID ${id} not found`); } const existingTodo = this.todos[todoIndex]; const updatedTodo = new Todo({ ...existingTodo, ...updateTodoDto, dueDate: updateTodoDto.dueDate ? new Date(updateTodoDto.dueDate) : existingTodo.dueDate, updatedAt: new Date(), }); this.todos[todoIndex] = updatedTodo; return updatedTodo; } remove(id: number): { message: string } { const todoIndex = this.todos.findIndex(todo => todo.id === id); if (todoIndex === -1) { throw new NotFoundException(`Todo with ID ${id} not found`); } this.todos.splice(todoIndex, 1); return { message: `Todo with ID ${id} has been deleted` }; } findByStatus(completed: boolean): Todo[] { return this.todos.filter(todo => todo.completed === completed); } findByPriority(priority: 'low' | 'medium' | 'high'): Todo[] { return this.todos.filter(todo => todo.priority === priority); } getStats(): { total: number; completed: number; pending: number } { const total = this.todos.length; const completed = this.todos.filter(todo => todo.completed).length; const pending = total - completed; return { total, completed, pending }; } }
Controller Implementation
import { Controller, Get, Post, Body, Patch, Param, Delete, Query, ParseIntPipe, ValidationPipe } from '@nestjs/common'; import { TodosService } from './todos.service'; import { CreateTodoDto, Priority } from './dto/create-todo.dto'; import { UpdateTodoDto } from './dto/update-todo.dto'; @Controller('todos') export class TodosController { constructor(private readonly todosService: TodosService) {} @Post() create(@Body(ValidationPipe) createTodoDto: CreateTodoDto) { // @Body(ValidationPipe) does several things: // 1. Extracts JSON from request body // 2. Validates it against CreateTodoDto rules // 3. Transforms it to CreateTodoDto instance // 4. If validation fails, automatically returns 400 error return this.todosService.create(createTodoDto); } @Get() findAll(@Query("status")status?: string, @Query("priority")priority?: Priority) { if (status === "completed") { return this.todosService.findByStatus(true); } if (status === "pending") { return this.todosService.findByStatus(false); } if (priority && ['low', 'medium', 'high'].includes(priority)) { return this.todosService.findByPriority(priority); } return this.todosService.findAll(); } @Get('stats') getStats() { return this.todosService.getByStats(); } @Get(":id") findOne(@Param('id', ParseIntPipe) id: number) { // @Param('id') extracts the :id from URL // ParseIntPipe converts string "123" to number 123 // If conversion fails, automatically returns 400 error return this.todosService.findOne(+id); } @Patch(':id') update(@Param('id', ParseIntPipe) id: number, @Body(ValidationPipe) updateTodoDto: UpdateTodoDto) { return this.todosService.update(+id, updateTodoDto); } @Delete(':id') remove(@Param('id', ParseIntPipe) id: number) { return this.todosService.remove(+id); } }
NestJS with Prisma
NestJS with Prisma Setup Guide
Prisma is a next-generation ORM that easily accesses databases with an auto-generated query builder for TypeScript and Node.js. This guide will walk you through setting up Prisma in a NestJS application.
Installation and Initialization
1. Installing Prisma
To get started, install Prisma and the Prisma client:
# Install Prisma as a development dependency npm i @prisma --save-dev # Install Prisma client npm i @prisma/client
2. Initialize Prisma
Initialize Prisma in your project by running:
# This will create a new Prisma directory with schema.prisma file npx prisma init
3. Configuration
The prisma/schema.prisma file is the main configuration file for Prisma. Here is an example configuration for connecting to a MySQL database:
// schema.prisma datasource db { provider = "mysql" url = env("DATABASE_URL") } generator client { provider = "prisma-client-js" }
Note: The DATABASE_URL environment variable should be defined in your .env file, and it typically looks something like this:
DATABASE_URL="mysql://root:password@localhost:3306/[database-name]"
4. Migrations
To create an initial migration and apply it to your database:
# Create a new migration and apply it to the database npx prisma migrate dev --name init
- Every time you make changes to
schema.prisma, you need to runnpx prisma generateto regenerate the Prisma client. - If the changes involve structural alterations to the database schema (like adding/removing tables, modifying columns, changing data types, etc.), you should also run
npx prisma migrate dev --name [name]to create a new migration file. - If you only change annotations (e.g., @id, @default) or validation rules in your schema without affecting the underlying database structure, you generally don't need to create a new migration. However, running
npx prisma generateis still required to ensure that the Prisma client reflects the latest schema configuration.
Setting Up Prisma in NestJS
5. Creating a Database Module
First, create a new resource for the database service:
# Generate a new resource named 'database' which includes a module, controller, and service nest g resource database
Modify the generated database.module.ts to provide the Prisma service:
// database.module.ts import { Module } from '@nestjs/common'; import { DatabaseService } from './database.service'; @Module({ providers: [DatabaseService], exports: [DatabaseService], // Exporting so it can be used in other modules }) export class DatabaseModule {}
Define the Prisma service in database.service.ts:
// database.service.ts import { Injectable, OnModuleInit } from '@nestjs/common'; import { PrismaClient } from '@prisma/client'; @Injectable() export class DatabaseService extends PrismaClient implements OnModuleInit { async onModuleInit() { await this.$connect(); // Connects to the database on module initialization } }
// Prisma Schema configuration // prisma/schema.prisma datasource db { provider = "mysql" url = env("DATABASE_URL") } generator client { provider = "prisma-client-js" } model Employee { id Int @id @default(autoincrement()) // Primary key with auto-increment name String email String @unique // Unique constraint on email field role Role // Enum type Role createdAt DateTime @default(now()) // Set to current time on creation updatedAt DateTime @updatedAt // Automatically updates on record modification } enum Role { ADMIN ENGINEER INTERN }
6. Using the Prisma Client in Other Services
To use the Prisma client, import the DatabaseModule in the module where you want to use it.
// employees.module.ts import { Module } from '@nestjs/common'; import { EmployeesService } from './employees.service'; import { EmployeesController } from './employees.controller'; import { DatabaseModule } from 'src/database/database.module'; @Module({ imports: [DatabaseModule], // Import the DatabaseModule controllers: [EmployeesController], providers: [EmployeesService], }) export class EmployeesModule {}
7. Creating a Service with Prisma Integration
Basic EmployeesService using Prisma for database operations:
// employees.service.ts import { Injectable } from '@nestjs/common'; import { Prisma, Role } from '@prisma/client'; import { DatabaseService } from 'src/database/database.service'; @Injectable() export class EmployeesService { constructor(private readonly databaseService: DatabaseService) {} async create(createEmployeeDto: Prisma.EmployeeCreateInput) { return this.databaseService.employee.create({ data: createEmployeeDto }); } async findAll(role?: Role) { if (role) { return this.databaseService.employee.findMany({ where: { role } }); } return this.databaseService.employee.findMany(); } async findOne(id: number) { return this.databaseService.employee.findUnique({ where: { id } }); } async update(id: number, updateEmployeeDto: Prisma.EmployeeUpdateInput) { return this.databaseService.employee.update({ where: { id }, data: updateEmployeeDto, }); } async remove(id: number) { return this.databaseService.employee.delete({ where: { id } }); } }
8. Create Controller
The corresponding controller would handle incoming requests and use the service methods:
// employees.controller.ts import { Controller, Get, Post, Body, Patch, Param, Delete, ParseIntPipe, Query, ValidationPipe, } from '@nestjs/common'; import { EmployeesService } from './employees.service'; import { Prisma, Role } from '@prisma/client'; @Controller('employees') export class EmployeesController { constructor(private readonly employeesService: EmployeesService) {} @Post() create(@Body(ValidationPipe) createEmployeeDto: Prisma.EmployeeCreateInput) { return this.employeesService.create(createEmployeeDto); } @Get() findAll(@Query('role') role?: Role) { return this.employeesService.findAll(role); } @Get(':id') findOne(@Param('id', ParseIntPipe) id: number) { return this.employeesService.findOne(id); } @Patch(':id') update( @Param('id', ParseIntPipe) id: number, @Body() updateEmployeeDto: Prisma.EmployeeUpdateInput, ) { return this.employeesService.update(id, updateEmployeeDto); } @Delete(':id') remove(@Param('id', ParseIntPipe) id: number) { return this.employeesService.remove(id); } }
NestJS notes
NestJS Notes
NestJS is a progressive Node.js framework for building efficient, reliable, and scalable server-side applications. It's built with TypeScript and uses strong design patterns like modules, controllers, and services.
Installation and Setup
To get started with NestJS, you'll need to install the CLI and create a new project:
# Install NestJS CLI globally npm i -g @nestjs/cli # Create a new NestJS project nest new [project-name] # Create a new module nest g module [module-name] # Create a new controller nest g controller [controller-name] # Create a new service nest g service [service-name] # Create a new resource (module, controller, and service together) nest g resource [resource-name]
Core Concepts
1. Decorators
Decorators in NestJS (indicated by the @ symbol) are special functions that can modify classes and methods. They are used to define a class as a module, controller, or service.
// A basic controller using a decorator import { Controller, Get } from '@nestjs/common'; @Controller('users') // Defines the route as /users export class UsersController { @Get() // Handles GET requests to /users getAllUsers() { return "This will return all users"; } }
2. Controllers
Controllers are responsible for handling incoming requests and returning responses to the client. They usually define the routes of your application.
3. Services
Services contain the business logic of the application. They can be injected into controllers to handle complex tasks, such as database operations.
// A simple service import { Injectable } from '@nestjs/common'; @Injectable() export class UsersService { private users = []; createUser(user) { this.users.push(user); } getUsers() { return this.users; } }
4. Decorators for Request Handling
Decorators can also be used inside methods to extract request parameters, body, or query data.
@Param('id')extracts a route parameter.@Body()extracts the body of the request.@Query('name')extracts query parameters.
// Example using different decorators import { Controller, Get, Param, Body, Query } from '@nestjs/common'; @Controller('users') export class UsersController { @Get(':id') getUserById(@Param('id') id: string) { return `This action returns a user with ID ${id}`; } @Get() getUserByName(@Query('name') name: string) { return `This action returns users with name ${name}`; } @Post() createUser(@Body() user: any) { return `This action creates a user with name ${user.name}`; } }
5. Pipes
Pipes transform or validate the data before it is handled by the route handler.
ParseIntPipeensures that a parameter is an integer.ValidationPipevalidates incoming data against the defined DTO (Data Transfer Object) schema.
// Example of using ParseIntPipe @Get(':id') getUserById(@Param('id', ParseIntPipe) id: number) { return `This action returns a user with ID ${id}`; }
6. DTOs (Data Transfer Objects)
DTOs define the shape of the data that flows in and out of the application. They can include validation rules using libraries like class-validator.
// Example DTO with validation import { IsString, IsEmail } from 'class-validator'; export class CreateUserDto { @IsString() name: string; @IsEmail() email: string; }
7. Validation and Transformation
To use DTO validation with class-validator and transformation with class-transformer:
# Install the required packages npm i class-validator class-transformer
// Example of using class-transformer and class-validator import { Transform } from 'class-transformer'; import { IsString, IsEmail } from 'class-validator'; export class CreateUserDto { @IsString() name: string; @IsEmail() email: string; @Transform(({ value }) => value.toLowerCase()) password: string; }
8. Exception Handling
NestJS has built-in support for exception handling. You can create custom exceptions by extending HttpException or use built-in ones like NotFoundException.
// users.service.ts import { NotFoundException } from '@nestjs/common'; ... findAll(role?: 'INTERN' | 'ENGINEER' | 'ADMIN') { if (role) { const roleArray = this.users.filter((user) => user.role === role); if (roleArray.length === 0) { throw new NotFoundException('Role not found'); } return roleArray; } return this.users; }
9. Global Prefix
A global prefix can be set for all routes in the application, making it easier to version APIs.
// Setting a global prefix for all routes // main.ts ... const app = await NestFactory.create(AppModule); app.setGlobalPrefix('api'); // All routes will be prefixed with /api await app.listen(3000);
With the global prefix set, the route defined as:
@Controller('notes') export class NotesController {}
Will become /api/notes.
Example:
// users.module.ts import { Module } from '@nestjs/common'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; @Module({ imports: [], controllers: [UsersController], providers: [UsersService], }) export class UsersModule {}
// users.service.ts import { Injectable, NotFoundException } from '@nestjs/common'; import { CreateUserDto } from './dto/create-user.dto'; import { UpdateUserDto } from './dto/update-user.dto'; @Injectable() // This decorator is used to define a class as a provider. export class UsersService { private users = [ { id: 1, name: 'Leanne Graham', email: 'Sincere@april.biz', role: 'INTERN', }, { id: 2, name: 'Ervin Howell', email: 'Shanna@melissa.tv', role: 'INTERN', }, { id: 3, name: 'Clementine Bauch', email: 'Nathan@yesenia.net', role: 'ENGINEER', }, { id: 4, name: 'Patricia Lebsack', email: 'Julianne.OConner@kory.org', role: 'ENGINEER', }, { id: 5, name: 'Chelsey Dietrich', email: 'Lucio_Hettinger@annie.ca', role: 'ADMIN', }, ]; findAll(role?: 'INTERN' | 'ENGINEER' | 'ADMIN') { if (role) { const roleArray = this.users.filter((user) => user.role === role); if (roleArray.length === 0) { throw new NotFoundException('Role not found'); } return roleArray; } return this.users; } findOne(id: number) { const user = this.users.find((user) => user.id === id); if (!user) { throw new NotFoundException('User not found'); } return user; } findOneByName(name: string) { return this.users.find((user) => user.name === name); } create(createUserDto: CreateUserDto) { // get the user with the highest id const usersByHighestId = [...this.users].sort((a, b) => b.id - a.id); const newUser = { id: usersByHighestId[0].id + 1, ...createUserDto, }; this.users.push(newUser); return newUser; } update(id: number, updateUserDto: UpdateUserDto) { this.users = this.users.map((user) => { if (user.id === id) { return { ...user, ...updateUserDto }; } return user; }); return this.findOne(id); } remove(id: number) { const removeUser = this.findOne(id); this.users = this.users.filter((user) => user.id !== id); return removeUser; } }
// users.controller.ts import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Query, ValidationPipe, } from '@nestjs/common'; import { UsersService } from './users.service'; import { CreateUserDto } from './dto/create-user.dto'; import { UpdateUserDto } from './dto/update-user.dto'; @Controller('users') export class UsersController { constructor(private readonly usersService: UsersService) {} @Get() findAll(@Query('role') role?: 'INTERN' | 'ENGINEER' | 'ADMIN') { return this.usersService.findAll(role); } @Get(':id') findOne(@Param('id', ParseIntPipe) id: number) { return this.usersService.findOne(id); } @Post() create( @Body(ValidationPipe) createUserDto: CreateUserDto, ) { return this.usersService.create(createUserDto); } @Patch(':id') update( @Param('id', ParseIntPipe) id: number, @Body(ValidationPipe) updateUserDto: UpdateUserDto, ) { return this.usersService.update(id, updateUserDto); } @Delete(':id') remove(@Param('id', ParseIntPipe) id: number) { return this.usersService.remove(id); } }
// dto/create-user.dto.ts import { IsEmail, IsEnum, IsNotEmpty, IsString } from 'class-validator'; export class CreateUserDto { @IsString() @IsNotEmpty() name: string; @IsEmail() email: string; @IsEnum(['INTERN', 'ENGINEER', 'ADMIN']) role: 'INTERN' | 'ENGINEER' | 'ADMIN'; } // dto/update-user.dto.ts import { CreateUserDto } from './create-user.dto'; import { PartialType } from '@nestjs/mapped-types'; export class UpdateUserDto extends PartialType(CreateUserDto) {}
MySQL Notes
MySQL Terminal Notes
1. Keys
Primary Key
A primary key is a column or a set of columns that uniquely identifies each row in a table. A table can have only one primary key.
Example:
CREATE TABLE my_table (id INT PRIMARY KEY, name VARCHAR(255), age INT);
Foreign Keys
A foreign key is a column or set of columns that establishes a link between two tables by referencing the primary key of another table.
Example:
CREATE TABLE my_table ( id INT PRIMARY KEY, name VARCHAR(255), age INT, department_id INT, FOREIGN KEY (department_id) REFERENCES departments(id) );
Composite Keys
A composite key is a primary key that consists of multiple columns. It is used when a single column is not sufficient to uniquely identify a row.
Example:
CREATE TABLE my_table ( id INT, name VARCHAR(255), age INT, PRIMARY KEY (id, name) );
2. Database Operations
Create Database
To create a new database, use the following command:
Example:
CREATE DATABASE my_database;
Delete a database
To delete a database, use the following command:
Example:
DROP DATABASE my_database;
Select a Database
To select a database, use the following command:
Example:
USE my_database;
Display All Databases
To display all databases, use the following command:
Example:
SHOW DATABASES;
3. Table Operations
Create Table
To create a new table, use the following command:
Example:
CREATE TABLE my_table ( id INT PRIMARY KEY, name VARCHAR(255), age INT );
Create Table with Constraints
To create a table with constraints, use the following command:
Example:
CREATE TABLE my_table ( id INT PRIMARY KEY, name VARCHAR(255) NOT NULL, age INT CHECK (age >= 18) ); -- type of constraints: PRIMARY KEY, UNIQUE, NOT NULL, CHECK, DEFAULT, FOREIGN KEY
Delete Table
To delete a table, use the following command:
Example:
DROP TABLE my_table;
Display All Tables
To display all tables in a database, use the following command:
Example:
SHOW TABLES;
Insert Data
To insert data into a table, use the following command:
Example:
INSERT INTO my_table VALUES (1, 'John Doe', 30);
Insert Data to Specific Columns
To insert data into specific columns of a table, use the following command:
Example:
INSERT INTO my_table (name, age) VALUES ('Jane Doe', 25);
Add a New Column
To add a new column to a table, use the following command:
Example:
ALTER TABLE my_table ADD email VARCHAR(255);
Delete a Column
To delete a column from a table, use the following command:
Example:
ALTER TABLE my_table DROP COLUMN email;
4. Data Manipulation
Update Data in a Table
To update data in a table, use the following command:
Example:
UPDATE my_table SET age = 26 WHERE name = 'John';
Update Multiple Columns
To update multiple columns in a table, use the following command:
Example:
UPDATE my_table SET age = 26, name = 'Jane' WHERE id = 1;
Delete Data from a Table
To delete data from a table, use the following command:
Example:
DELETE FROM my_table WHERE id = 1;
Retrieve Data from a Table
To retrieve data from a table, use the following command:
Example:
SELECT * FROM my_table;
Retrieve Specific Columns
To retrieve specific columns from a table, use the following command:
Example:
SELECT name, age FROM my_table;
Retrieve Data in Order
To retrieve data from a table in a specific order, use the following command:
Example:
SELECT * FROM my_table ORDER BY age DESC;
5. Queries with Conditions
WHERE Clause
The WHERE clause is used to filter records based on a specified condition.
Example:
SELECT * FROM my_table WHERE age > 25;
Using Order By and Limit
To retrieve a limited number of records from a table in a specific order, use the following command:
Example:
SELECT * FROM my_table ORDER BY age DESC LIMIT 5;
6. Joins
Inner Joins
An inner join is used to combine rows from two or more tables based on a related column between them.
Example:
SELECT orders.order_id, customers.customer_name FROM orders JOIN customers ON orders.customer_id = customers.customer_id;
Left Joins
A left join returns all rows from the left table and the matched rows from the right table. The result is NULL from the right side if there is no match.
Example:
SELECT orders.order_id, customers.customer_name FROM orders LEFT JOIN customers ON orders.customer_id = customers.customer_id;
output: order_id | customer_name 1 | John Doe 2 | Jane Doe 3 | NULL
Right Joins
A right join returns all rows from the right table and the matched rows from the left table. The result is NULL from the left side if there is no match.
Example:
SELECT orders.order_id, customers.customer_name FROM orders RIGHT JOIN customers ON orders.customer_id = customers.customer_id;
output: order_id | customer_name 1 | John Doe 2 | Jane Doe NULL | Alice Smith
Full Outer Joins
A full outer join returns all rows when there is a match in either the left or right table. The result is NULL from both sides when there is no match.
Example:
SELECT orders.order_id, customers.customer_name FROM orders FULL OUTER JOIN customers ON orders.customer_id = customers.customer_id;
output: order_id | customer_name 1 | John Doe 2 | Jane Doe NULL | Alice Smith 3 | NULL
7. Aggregate Functions
Count
The COUNT function is used to count the number of rows in a table.
Example:
SELECT COUNT(*) FROM my_table;
Sum
The SUM function is used to calculate the sum of a column.
Example:
SELECT SUM(salary) FROM employees;
Average
The AVG function is used to calculate the average value of a column.
Example:
SELECT AVG(salary) FROM employees;
Min and Max
The MIN and MAX functions are used to find the minimum and maximum values in a column, respectively.
Example:
SELECT MIN(salary) FROM employees; SELECT MAX(salary) FROM employees;
8. Group By
The GROUP BY clause is used to group rows that have the same values into summary rows.
Example:
SELECT department, COUNT(*) FROM employees GROUP BY department;
output: department | count HR | 5 IT | 10
9. Subqueries
A subquery is a query within another query. It can be used to return values that are used in the main query.
Example:
SELECT * FROM employees WHERE salary > (SELECT AVG(salary) FROM employees);
10. Wildcards
The % wildcard is used to match any sequence of characters.
Example:
SELECT * FROM employees WHERE name LIKE 'J%';
The _ wildcard is used to match any single character.
Example:
SELECT * FROM employees WHERE name LIKE 'J_n';
10. Unions
The UNION operator is used to combine the result sets of two or more SELECT statements.
Example:
-- Find a list of employee and branch names SELECT employee.first_name AS Employee_Branch_Names FROM employee UNION SELECT branch.branch_name FROM branch; -- Find a list of all clients & branch suppliers' names SELECT client.client_name AS Non-Employee_Entities FROM client UNION SELECT branch_supplier.supplier_name FROM branch_supplier;
11. Nested Queries
Example:
SELECT * FROM employees WHERE department_id IN (SELECT id FROM departments WHERE name = 'IT');
12. Triggers
A trigger is a set of SQL statements that are automatically executed when a specified event occurs.
Example:
Delimiter // CREATE TRIGGER my_trigger BEFORE INSERT ON my_table FOR EACH ROW BEGIN SET NEW.created_at = NOW(); END // Delimiter ;
Basic Query Syntax
-- Find all employees SELECT * FROM employee; -- Find all clients SELECT * FROM clients; -- Find all employees ordered by salary SELECT * from employee ORDER BY salary ASC/DESC; -- Find all employees ordered by sex then name SELECT * from employee ORDER BY sex, name; -- Find the first 5 employees in the table SELECT * from employee LIMIT 5; -- Find the first and last names of all employees SELECT first_name, employee.last_name FROM employee; -- Find the forename and surnames names of all employees SELECT first_name AS forename, employee.last_name AS surname FROM employee; -- Find out all the different genders SELECT DISCINCT sex FROM employee; -- Find all male employees SELECT * FROM employee WHERE sex = 'M'; -- Find all employees at branch 2 SELECT * FROM employee WHERE branch_id = 2; -- Find all employee's id's and names who were born after 1969 SELECT emp_id, first_name, last_name FROM employee WHERE birth_day >= 1970-01-01; -- Find all female employees at branch 2 SELECT * FROM employee WHERE branch_id = 2 AND sex = 'F'; -- Find all employees who are female & born after 1969 or who make over 80000 SELECT * FROM employee WHERE (birth_day >= '1970-01-01' AND sex = 'F') OR salary > 80000; -- Find all employees born between 1970 and 1975 SELECT * FROM employee WHERE birth_day BETWEEN '1970-01-01' AND '1975-01-01'; -- Find all employees named Jim, Michael, Johnny or David SELECT * FROM employee WHERE first_name IN ('Jim', 'Michael', 'Johnny', 'David'); -- Find the number of employees SELECT COUNT(super_id) FROM employee; -- Find the average of all employee's salaries SELECT AVG(salary) FROM employee; -- Find the sum of all employee's salaries SELECT SUM(salary) FROM employee; -- Find out how many males and females there are SELECT COUNT(sex), sex FROM employee GROUP BY sex -- Find the total sales of each salesman SELECT SUM(total_sales), emp_id FROM works_with GROUP BY emp_id; -- Find the total amount of money spent by each client SELECT SUM(total_sales), client_id FROM works_with GROUP BY client_id; -- Find the first_name, last_name, and total amount of sales for each employee SELECT employee.first_name, employee.last_name, SUM(works_with.total_sales) FROM employee JOIN works_with ON works_with.emp_id = employee.emp_id GROUP BY works_with.emp_id;
Vocabulary: Model-View-Controller (MVC)
MVC (Model-View-Controller):
MVC is a design pattern commonly used in software development to organize the structure of an application. It divides an application's components into three main parts:
Model: The Model represents the data and the logic to manipulate that data. It's responsible for managing the application's data and business logic. This can include data storage, retrieval, validation, and other operations that deal with the data itself.
View: The View is responsible for presenting the data to the user. It displays the information and interacts with the user interface. The View is often concerned with how the data is displayed and doesn't contain the business logic.
Controller: The Controller acts as an intermediary between the Model and the View. It receives input from the user through the View, processes that input by interacting with the Model, and updates the View accordingly. The Controller handles user interactions and orchestrates the flow of data and actions between the Model and the View.
(from ChatGPT)
Vocabulary: Call signature
A call signature in TypeScript is a way to define the type of a function or method within an interface or type. It specifies the shape of a function, including the parameter types and the return type. This allows you to describe the signature of functions that can be called, including their arguments and return values. (from ChatGPT)
interface MyFunction { (param1: number, param2: string): boolean; } const myFunc: MyFunction = (num, str) => { return num > parseInt(str); }; console.log(myFunc(5, '3')); // Output: true
Vocabulary: Index signature
In TypeScript, an index signature (also known as an indexable type) allows you to define the types of properties that are not known in advance but can be accessed using square bracket notation. Index signatures are particularly useful when working with objects that have dynamic property names. (from ChatGPT)
interface MyObject { [key: string]: number; } const myData: MyObject = { apple: 5, banana: 3, }; console.log(myData.apple); // Output: 5 console.log(myData.banana); // Output: 3