- backend Π½Π° Π±Π°Π·Π΅ NestJS, Π² ΠΊΠ°ΡΠ΅ΡΡΠ²Π΅ Π±Π°Π·Ρ Π΄Π°Π½Π½ΡΡ
Π²ΡΡΡΡΠΏΠ°Π΅Ρ PostgreSQL,
Π΄Π»Ρ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ Π±Π°Π·ΠΎΠΉ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ TypeORM, Π² ΠΊΠ°ΡΠ΅ΡΡΠ²Π΅ ΡΠ·ΡΠΊΠ° Π·Π°ΠΏΡΠΎΡΠ° Ρ backend ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ GraphQL. - frontend Π½Π° Π±Π°Π·Π΅ Angular.
ΠΡΠ΅ ΡΡΠΎ ΡΠΏΠ°ΠΊΠΎΠ²Π°Π½ΠΎ Π² docker-compose!
Π‘ΠΊΠΎΠΏΠΈΡΡΠΉΡΠ΅ ΠΊ ΡΠ΅Π±Π΅ ΡΠ΅ΠΏΠΎΠ·ΠΈΡΠΎΡΠΈΠΉ:
https://github.com/vitalyvitmens/AngularNestJS_Full-stack
API_PORT=3001
API_HOST=http://localhost:
TYPEORM_CONNECTION=postgres
TYPEORM_USERNAME=admin
TYPEORM_PASSWORD=123456
TYPEORM_DATABASE=AngularNestJS_Full-stack
TYPEORM_PORT=5432
TYPEORM_HOST=localhost
docker-compose up
-d - Π΄Π»Ρ Π·Π°ΠΏΡΡΠΊΠ° Π² ΡΠΎΠ½Π΅
--build - Π΄Π»Ρ ΠΏΠΎΠ²ΡΠΎΡΠ½ΠΎΠΉ ΠΏΠ΅ΡΠ΅ΡΠ±ΠΎΡΠΊΠΈ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠΎΠ²
docker ps - ΠΏΠΎΡΠΌΠΎΡΡΠ΅ΡΡ Π·Π°ΠΏΡΡΠ΅Π½Π½ΡΠ΅ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΡ
cd backend/
yarn install
yarn start
cd frontend/
yarn install
yarn start
npm i -g @nestjs/cli
npm install --global yarn
Π‘ΠΎΠ·Π΄Π°ΡΠΌ ΠΏΠ°ΠΏΠΊΡ Ρ Π½Π°ΡΠΈΠΌ ΠΏΡΠΎΠ΅ΠΊΡΠΎΠΌ, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ AngularNestJS_Full-stack ΠΈ ΠΏΠ΅ΡΠ΅Ρ ΠΎΠ΄ΠΈΠΌ Π² Π½Π΅Π΅ Π΄Π»Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΡΠ΅ΡΠ²Π΅ΡΠ½ΠΎΠΉ ΡΠ°ΡΡΠΈ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ (Π² Π΄Π°Π½Π½ΠΎΠΌ ΡΠ»ΡΡΠ°Π΅ ΡΡΠΎ ΠΏΠ°ΠΏΠΊΠ° backend):
nest new backend --package-manager=yarn
ΠΡΠΊΡΡΠ²Π°Π΅ΠΌ ΠΏΡΠΎΠ΅ΠΊΡ Π² ΡΠ²ΠΎΠ΅ΠΉ ΡΡΠ΅Π΄Π΅ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠΈ, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ Visual Studio Code ΠΈ ΡΡΠ°ΡΡΡΠ΅ΠΌ backend ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅, ΡΡΠΎΠ±Ρ ΠΏΡΠΎΠ²Π΅ΡΠΈΡΡ Π΅Π³ΠΎ ΡΠ°Π±ΠΎΡΠΎΡΠΏΠΎΡΠΎΠ±Π½ΠΎΡΡΡ Π½Π° Π΄Π°Π½Π½ΠΎΠΌ ΡΡΠ°ΠΏΠ΅:
cd backend/
yarn start
Π ΡΠ»ΡΡΠ°Π΅ ΡΡΠΏΠ΅Ρ
Π°, ΠΎΡΠΈΠ±ΠΎΠΊ Π² ΡΠ΅ΡΠΌΠΈΠ½Π°Π»Π΅ Π½Π΅ Π±ΡΠ΄Π΅Ρ, Π° ΡΠ°ΠΌΠΎ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΠΌΠΎΠΆΠ½ΠΎ ΠΎΡΠΊΡΡΡΡ Π½Π° http://localhost:3000/
Π§ΡΠΎΠ±Ρ ΠΎΡΡΠ°Π½ΠΎΠ²ΠΈΡΡ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅, Π² ΡΠ΅ΡΠΌΠΈΠ½Π°Π»Π΅ Π½Π°ΠΆΠΌΠΈΡΠ΅: Ctrl + C
ΠΠ°ΡΡΡΠ°ΠΈΠ²Π°Π΅ΠΌ ΠΊΠΎΡΠΈΠ³ΡΡΠ°ΡΠΈΡ Π½Π°ΡΠ΅Π³ΠΎ ΠΎΠ±ΠΎΡΡΠ΄ΠΎΠ²Π°Π½ΠΈΡ, ΡΠΎΠ·Π΄Π°ΡΠΌ file .env:
cd ..
touch .env //Π² ΡΠ»ΡΡΠ°Π΅ ΡΠ΅ΡΠΌΠΈΠ½Π°Π»Π° Ubuntu (WSL)
echo $null >> .env //Π² ΡΠ»ΡΡΠ°Π΅ ΡΠ΅ΡΠΌΠΈΠ½Π°Π»Π° powershell
ΠΠ°Ρ ΠΎΠ΄ΠΈΠΌ Π² ΡΠ°ΠΉΠ» .env ΠΈ ΠΏΡΠΎΠΏΠΈΡΡΠ²Π°Π΅ΠΌ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ:
API_PORT=3001
API_HOST=http://localhost:
TYPEORM_CONNECTION=postgres
TYPEORM_USERNAME=admin
TYPEORM_PASSWORD=123456
TYPEORM_DATABASE=AngularNestJS_Full-stack
TYPEORM_PORT=5432
TYPEORM_HOST=localhost
ΠΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ Π² Π½Π°ΡΠ΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΡΠ΅ΡΠ²Π΅ΡΠ½ΠΎΠΉ ΡΠ°ΡΡΠΈ (backend):
cd backend/
yarn add @nestjs/config
ΠΠ°Ρ ΠΎΠ΄ΠΈΠΌ Π² ΡΠ°ΠΉΠ» main.ts ΡΠΎΠ·Π΄Π°Π΅ΠΌ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅: config ΠΈ port. ΠΡΠΈΠΌΠ΅ΡΠ½ΡΠΉ ΠΊΠΎΠ΄:
import { ConfigService } from '@nestjs/config';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = await app.get(ConfigService);
const port = config.get<number>('API_PORT');
await app.listen(port || 3000, () => {
console.log(`App started on port: ${port}`);
});
}
bootstrap();
ΠΠ΅ΡΠ΅Ρ ΠΎΠ΄ΠΈΠΌ Π² ΡΠ°ΠΉΠ» app.module.ts ΠΈ ΡΠ΅ΡΠ΅Π· imports ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠ°Π΅ΠΌ ConfigModule:
ConfigModule.forRoot({
isGlobal: true,
envFilePath: '../.env',
}),
ΠΠ°ΠΏΡΡΠΊΠ°Π΅ΠΌ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΡΡΠΎΠ±Ρ ΡΠ±Π΅Π΄ΠΈΡΡΡ, ΡΡΠΎ Π²ΡΠ΅ ΡΠ°Π±ΠΎΡΠ°Π΅Ρ:
cd backend/
nest start --watch
Π ΡΠ»ΡΡΠ°Π΅ ΡΡΠΏΠ΅Ρ
Π° Π² ΡΠ΅ΡΠΌΠΈΠ½Π°Π»Π΅ ΠΏΠΎΡΠ²ΠΈΡΡΡ ΡΠ΅ΠΊΡΡ:
App started on port: 3001
ΠΠ°ΡΡΡΠ°ΠΈΠ²Π°Π΅ΠΌ Π±Π°Π·Ρ Π΄Π°Π½Π½ΡΡ PostgreSQL, ΠΊΠΎΡΠΎΡΠ°Ρ Π±ΡΠ΄Π΅Ρ Π·Π°ΠΏΡΡΠΊΠ°ΡΡΡΡ ΡΠ΅ΡΠ΅Π· Docker:
-
Π² ΠΊΠΎΡΠ½Π΅ ΠΏΡΠΎΠ΅ΠΊΡΠ° ΡΠΎΠ·Π΄Π°Π΅ΠΌ ΡΠ°ΠΉΠ» docker-compose.yml:
cd .. touch docker-compose.yml //Π² ΡΠ»ΡΡΠ°Π΅ ΡΠ΅ΡΠΌΠΈΠ½Π°Π»Π° Ubuntu (WSL) echo $null >> docker-compose.yml //Π² ΡΠ»ΡΡΠ°Π΅ ΡΠ΅ΡΠΌΠΈΠ½Π°Π»Π° powershell
ΠΠ°Ρ ΠΎΠ΄ΠΈΠΌ Π² ΡΠ°ΠΉΠ» docker-compose.yml ΠΈ Π½Π°ΡΡΡΠ°ΠΈΠ²Π°Π΅ΠΌ Π΅Π³ΠΎ. ΠΡΠΈΠΌΠ΅ΡΠ½ΡΠΉ ΠΊΠΎΠ΄:
version: '3.8'
services:
db:
container_name: postgres-AngularNestJS_Full-stack
image: postgres
restart: always
environment:
- POSTGRES_USER=${TYPEORM_USERNAME}
- POSTGRES_PASSWORD=${TYPEORM_PASSWORD}
- POSTGRES_DB=${TYPEORM_DATABASE}
volumes:
- ./pgdata:/var/lib/postresql/data
ports:
- ${TYPEORM_PORT}:${TYPEORM_PORT}
backend:
container_name: backend-AngularNestJS_Full-stack
build:
context: ./backend
depends_on:
- db
restart: unless-stopped
ports:
- '${API_PORT}:3001'
environment:
- API_PORT=${API_PORT}
- API_HOST=${API_HOST}
- TYPEORM_CONNECTION=${TYPEORM_CONNECTION}
- TYPEORM_USERNAME=${TYPEORM_USERNAME}
- TYPEORM_PASSWORD=${TYPEORM_PASSWORD}
- TYPEORM_DATABASE=${TYPEORM_DATABASE}
- TYPEORM_PORT=${TYPEORM_PORT}
- TYPEORM_HOST=db
frontend:
container_name: frontend-AngularNestJS_Full-stack
build:
context: ./frontend
depends_on:
- db
- backend
restart: unless-stopped
ports:
- '80:80'
cd backend/
yarn add @nestjs/typeorm typeorm postgres
ΠΠ΅ΡΠ΅Ρ ΠΎΠ΄ΠΈΠΌ Π² ΡΠ°ΠΉΠ» app.module.ts ΠΈ ΡΠ΅ΡΠ΅Π· imports ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠ°Π΅ΠΌ TypeOrmModule:
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (config: ConfigService) => ({
type: config.get<'postgres'>('TYPEORM_CONNECTION'),
host: config.get<string>('TYPEORM_HOST'),
username: config.get<string>('TYPEORM_USERNAME'),
password: config.get<string>('TYPEORM_PASSWORD'),
database: config.get<string>('TYPEORM_DATABASE'),
port: config.get<number>('TYPEORM_PORT'),
entities: [__dirname + 'dist/**/*.entity{.ts,.js}'],
synchronize: true,
autoLoadEntities: true,
logging: true,
dropSchema: true,
}),
}),
cd ..
docker-compose up -d
Π‘ΠΌΠΎΡΡΠΈΠΌ Π·Π°ΠΏΡΡΠ΅Π½Π½ΡΠ΅ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΡ, ΠΊΠΎΡΠΎΡΡΠ΅ Π΄ΠΎΠ»ΠΆΠ½Ρ Π±ΡΡΡ Π² ΡΠ»ΡΡΠ°Π΅ ΡΠ΄Π°ΡΠ½ΠΎΠ³ΠΎ Π·Π°ΠΏΡΡΠΊΠ° ΡΠ°ΠΉΠ» docker-compose.yml:
cd ..
docker ps
Π ΡΠ»ΡΡΠ°Π΅ ΡΡΠΏΠ΅Ρ Π° Π² ΡΠ΅ΡΠΌΠΈΠ½Π°Π»Π΅ ΡΠ²ΠΈΠ΄ΠΈΠΌ ΠΏΠΎ ΠΊΡΠ°ΠΉΠ½Π΅ΠΉ ΠΌΠ΅ΡΠ΅ ΠΎΠ΄ΠΈΠ½ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅Ρ Π·Π°ΠΏΡΡΠ΅Π½Π½ΡΠΉ Π½Π° ΠΏΠΎΡΡΡ 5432
ΠΠ°ΠΏΡΡΠΊΠ°Π΅ΠΌ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΡΡΠΎΠ±Ρ ΡΠ±Π΅Π΄ΠΈΡΡΡ, ΡΡΠΎ Π±Π°Π·Π° Π΄Π°Π½Π½ΡΡ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠΈΠ»Π°ΡΡ:
cd backend/
nest start --watch
Π ΡΠ»ΡΡΠ°Π΅ ΡΡΠΏΠ΅Ρ Π° Π² ΡΠ΅ΡΠΌΠΈΠ½Π°Π»Π΅ ΡΠ²ΠΈΠ΄ΠΈΠΌ query: SELECT * FROM current_schema() ΠΈ Ρ.Π΄.
Π£ΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ GraphQL (Π΅ΡΠ»ΠΈ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π·Π°ΠΏΡΡΠ΅Π½ΠΎ, Π² Π½Π°ΡΠ°Π»Π΅ ΡΠΎΡΠΌΠΎΠ·ΠΈΠΌ Π΅Π³ΠΎ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ Π² ΡΠ΅ΡΠΌΠΈΠ½Π°Π»Π΅: Ctrl + C):
cd backend/
yarn add @nestjs/graphql @nestjs/apollo graphql apollo-server-express
ΠΠ΅ΡΠ΅Ρ ΠΎΠ΄ΠΈΠΌ Π² ΡΠ°ΠΉΠ» app.module.ts ΠΈ ΡΠ΅ΡΠ΅Π· imports ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠ°Π΅ΠΌ GraphQLModule:
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: 'schema.gql',
}),
ΠΠ°Π»Π΅Π΅ Π³Π΅Π½Π΅ΡΠΈΡΡΠ΅ΠΌ ΠΌΠΎΠ΄ΡΠ»Ρ users (nest Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ ΡΠ³Π΅Π½Π΅ΡΠΈΡΡΠ΅Ρ Π² ΠΏΠ°ΠΏΠΊΠ΅ srs ΠΏΠ°ΠΏΠΊΡ users ΠΈ ΡΠΎΠ·Π΄Π°ΡΡ Π² Π½Π΅ΠΉ ΡΠ°ΠΉΠ» users.module.ts):
cd backend/
nest g mo users
Π‘ΠΎΠ·Π΄Π°Π΄Π°ΡΠΌ ΠΏΠ΅ΡΠ²ΡΡ ΡΡΡΠ½ΠΎΡΡΡ: Π² ΠΏΠ°ΠΏΠΊΠ΅ users ΡΠΎΠ·Π΄Π°Π΅ΠΌ ΠΏΠ°ΠΏΠΊΡ entities Π² ΠΊΠΎΡΠΎΡΠΎΠΉ ΡΠΎΠ·Π΄Π°Π΅ΠΌ ΡΠ°ΠΉΠ» user.entity.ts ΠΈ Π½Π°Π²Π΅ΡΠΈΠ²Π°Π΅ΠΌ Π΄Π΅ΠΊΠΎΡΠ°ΡΠΎΡΡ @ TypeORM & GraphQL. ΠΠΎΠ΄ ΠΈΠΌΠ΅Π΅Ρ ΡΠ»Π΅Π΄ΡΡΡΠΈΠΉ Π²ΠΈΠ΄:
import {
Column,
CreateDateColumn,
Entity,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
import { Field, Float, ID, ObjectType } from '@nestjs/graphql';
@ObjectType()
@Entity('users')
export class UserEntity {
@Field(() => ID)
@PrimaryGeneratedColumn()
id: number | string;
@Field()
@CreateDateColumn()
createdAt: Date;
@Field()
@UpdateDateColumn()
updatedAt: Date;
@Field()
@Column()
email: string;
@Field({ nullable: true })
@Column({ nullable: true })
name: string;
}
ΠΠ΅ΡΠ΅Ρ ΠΎΠ΄ΠΈΠΌ Π² ΡΠ°ΠΉΠ» users.module.ts ΠΈ Π² ΠΈΠΌΠΏΠΎΡΡΠ΅ ΡΠΊΠ°Π·ΡΠ²Π°Π΅ΠΌ Π½Π°Π±ΠΎΡ ΠΎΠΏΡΠΈΠΉ:
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserService } from './services/user/user.service';
import { UserResolver } from './resolvers/user/user.resolver';
import { UserEntity } from 'src/users/entities/user.entity';
@Module({
imports: [TypeOrmModule.forFeature([UserEntity])],
providers: [UserService, UserResolver],
})
export class UsersModule {}
Π ΠΏΠ°ΠΏΠΊΠ΅ users ΡΠΎΠ·Π΄Π°Π΅ΠΌ ΡΠ΅ΡΠ²ΠΈΡ Π΄Π»Ρ ΠΌΠ°Π½ΠΈΠΏΡΠ»ΠΈΡΠΎΠ²Π°Π½ΠΈΡ Π½Π°ΡΠΈΠΌΠΈ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΠΌΠΈ:
cd backend/
nest g s users/services/user
Π ΠΏΠ°ΠΏΠΊΠ΅ users ΡΠΎΠ·Π΄Π°Π΅ΠΌ ΠΏΠ°ΠΏΠΊΡ resolver Π² ΠΊΠΎΡΠΎΡΠΎΠΉ Π³Π΅Π½Π΅ΡΠΈΡΡΠ΅ΠΌ ΡΠ°ΠΉΠ» user.resolver.ts:
cd backend/
nest r s users/resolvers/user
user.resolver.ts: import { Test, TestingModule } from '@nestjs/testing'; import { UserResolver } from './user.resolver';
describe('UserResolver', () => {
let resolver: UserResolver;
beforeEach(async () => {
const module: TestingModule = await Test. createTestingModule({
providers: [UserResolver],
}).compile();
resolver = module.get<UserResolver>(UserResolver);
});
it('should be defined', () => {
expect(resolver).toBeDefined();
});
});
Π ΠΏΠ°ΠΏΠΊΠ΅ users ΡΠΎΠ·Π΄Π°Π΅ΠΌ ΠΏΠ°ΠΏΠΊΡ inputs Π² ΠΊΠΎΡΠΎΡΠΎΠΉ ΡΠΎΠ·Π΄Π°Π΅ΠΌ Π΄Π²Π° ΡΠ°ΠΉΠ»Π°:
-
create-user.input.ts
import { Field, InputType } from '@nestjs/graphql';
@InputType() export class CreateUserInput { @Field() email: string;
@Field({ nullable: true }) name: string;
}
-
update-user.input.ts
import { Field, ID, InputType } from '@nestjs/graphql';
@InputType() export class UpdateUserInput { @Field(() => ID) id: number;
@Field({ nullable: true }) email: string; @Field({ nullable: true }) name: string;
}
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { UserEntity } from 'src/users/entities/user.entity';
import { CreateUserInput } from 'src/users/inputs/create-user.input';
import { UpdateUserInput } from 'src/users/inputs/update-user.input';
import { Repository } from 'typeorm';
@Injectable()
export class UserService {
constructor(
@InjectRepository(UserEntity)
private readonly userRepository: Repository<UserEntity>,
) {}
async createUser(createUserInput: CreateUserInput): Promise<UserEntity> {
return await this.userRepository.save({ ...createUserInput });
}
async getOneUser(id: number): Promise<UserEntity> {
return await this.userRepository.findOne({
where: {
id: id,
},
});
}
async getAllUsers(): Promise<UserEntity[]> {
return await this.userRepository.find();
}
async removeUser(id: number): Promise<number> {
await this.userRepository.delete({ id });
return id;
}
async updateUser(updateUserInput: UpdateUserInput): Promise<UserEntity> {
await this.userRepository.update(
{ id: updateUserInput.id },
{ ...updateUserInput },
);
return await this.getOneUser(updateUserInput.id);
}
}
cd backend/
docker ps //ΠΏΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ ΡΡΠΎ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅Ρ Π·Π°ΠΏΡΡΠ΅Π½ ΠΈΠ½Π°ΡΠ΅ docker-compose up -d
yarn start
Π ΡΠ»ΡΡΠ°Π΅ ΡΡΠΏΠ΅Ρ Π° Π² ΡΠ΅ΡΠΌΠΈΠ½Π°Π»Π΅ ΡΠ²ΠΈΠ΄ΠΈΠΌ query: CREATE TABLE "users" & App started on port: 3001
ΠΠ°ΠΏΡΡΠΊ Π½Π°ΡΠ΅Π³ΠΎ ΠΏΡΠΎΠ΅ΠΊΡΠ° Ρ ΠΏΠΎΠΌΠΎΡΡΡ Docker. Π‘ΠΎΠ·Π΄Π°ΡΠΌ Π² ΠΊΠΎΡΠ½Π΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ Dockerfile ΡΠΎ ΡΠ»Π΅Π΄ΡΡΡΠΈΠΌ ΠΊΠΎΠ΄ΠΎΠΌ:
FROM node:18-alpine AS builder
WORKDIR /app
COPY /*.json ./
COPY . .
RUN npm run build
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app ./
EXPOSE 3001
CMD ["npm", "run", "start:prod"]
version: '3.8'
services:
db:
container_name: postgres-AngularNestJS_Full-stack
image: postgres
restart: always
environment:
- POSTGRES_USER=${TYPEORM_USERNAME}
- POSTGRES_PASSWORD=${TYPEORM_PASSWORD}
- POSTGRES_DB=${TYPEORM_DATABASE}
volumes:
- ./pgdata:/var/lib/postresql/data
ports:
- ${TYPEORM_PORT}:${TYPEORM_PORT}
backend:
container_name: backend-AngularNestJS_Full-stack
build:
context: ./backend
depends_on:
- db
restart: unless-stopped
ports:
- '${API_PORT}:3001'
environment:
- API_PORT=${API_PORT}
- API_HOST=${API_HOST}
- TYPEORM_CONNECTION=${TYPEORM_CONNECTION}
- TYPEORM_USERNAME=${TYPEORM_USERNAME}
- TYPEORM_PASSWORD=${TYPEORM_PASSWORD}
- TYPEORM_DATABASE=${TYPEORM_DATABASE}
- TYPEORM_PORT=${TYPEORM_PORT}
- TYPEORM_HOST=db
frontend:
container_name: frontend-AngularNestJS_Full-stack
build:
context: ./frontend
depends_on:
- db
- backend
restart: unless-stopped
ports:
- '80:80'
ΠΠ°ΠΏΡΡΠΊΠ°Π΅ΠΌ Π½Π°ΡΠ΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΡΠ΅ΡΠ΅Π· ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅Ρ Docer (Π΅ΡΠ»ΠΈ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π·Π°ΠΏΡΡΠ΅Π½ΠΎ, Π² Π½Π°ΡΠ°Π»Π΅ ΡΠΎΡΠΌΠΎΠ·ΠΈΠΌ Π΅Π³ΠΎ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ Π² ΡΠ΅ΡΠΌΠΈΠ½Π°Π»Π΅: Ctrl + C):
cd ..
docker-compose down
docker ps //ΠΏΠΎΡΠΌΠΎΡΡΠ΅ΡΡ Π½Π°ΡΠΈ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΡ
docker-compose up -d --build
npm install -g @angular/cli
ΠΠ΅ΡΠ΅Ρ ΠΎΠ΄ΠΈΠΌ Π² ΡΠ°Π½Π΅Π΅ ΡΠΎΠ·Π΄Π°Π½Π½ΡΡ ΠΏΠ°ΠΏΠΊΡ Ρ Π½Π°ΡΠΈΠΌ ΠΏΡΠΎΠ΅ΠΊΡΠΎΠΌ, Π² ΠΌΠΎΡΠΌ ΡΠ»ΡΡΠ°Π΅ AngularNestJS_Full-stack Π΄Π»Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΠΊΠ»ΠΈΠ΅Π½ΡΡΠΊΠΎΠΉ ΡΠ°ΡΡΠΈ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ (Π² Π΄Π°Π½Π½ΠΎΠΌ ΡΠ»ΡΡΠ°Π΅ ΡΡΠΎ ΠΏΠ°ΠΏΠΊΠ° frontend):
cd ..
ng new frontend --package-manager=yarn --routing --skip-git --skip-tests --style=scss
ΠΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ ΡΡΠΈΠ»ΠΈ bootstrap, ΡΡΠΎ Π±Ρ ΡΠΎΠΊΡΠ°ΡΠΈΡΡ Π²ΡΠ΅ΠΌΡ Π½Π° Π²Π΅ΡΡΡΠΊΡ:
cd ..
yarn add bootstrap
ΠΏΠΎΡΠ»Π΅ ΡΡΡΠ°Π½ΠΎΠ²ΠΊΠΈ ΡΡΠΈΠ»Π΅ΠΉ Π΄ΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ ΠΈΡ Π² ΡΠ°ΠΉΠ» styles.scss:
@import "~bootstrap/dist/css/bootstrap.min.css";
ΡΡΠΎΠ±Ρ Π²ΡΠ΅ Π±ΡΠ»ΠΎ Π² ΠΎΠ΄Π½ΠΎΠΌ ΡΡΠΈΠ»Π΅ ΡΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ ΠΈΠΊΠΎΠ½ΠΊΠΈ bootstrap:
yarn add bootstrap-icons
ΠΏΠΎΡΠ»Π΅ ΡΡΡΠ°Π½ΠΎΠ²ΠΊΠΈ ΠΈΠΊΠΎΠ½ΠΎΠΊ Π΄ΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ ΠΈΡ Π² ΡΠ°ΠΉΠ» styles.scss:
@import "~bootstrap-icons/font/bootstrap-icons.css";
Π² ΡΠ°ΠΉΠ»Π΅ angular.json Π΄ΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ ΡΡΡΠΎΠΊΡ: "changeDetection": "OnPush"
"schematics": {
"@schematics/angular:component": {
"style": "scss",
"skipTests": true,
"changeDetection": "OnPush"
},
cd frontend/
ng serve --open
Π ΡΠ»ΡΡΠ°Π΅ ΡΡΠΏΠ΅Ρ Π° ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΠΎΡΠΊΡΠΎΠ΅ΡΡΡ Π½Π° http://localhost:4200/
ΠΠ°Ρ ΠΎΠ΄ΠΈΠΌ Π² ΡΠ°ΠΉΠ» app.component.html ΡΠ΄Π°Π»ΡΠ΅ΠΌ Π²ΡΠ΅ ΡΡΠΎ Π²Π½ΡΡΡΠΈ ΠΈ Π²Π·Π°ΠΌΠ΅Π½ ΠΏΠΈΡΠ΅ΠΌ ΡΠ²ΠΎΠΉ ΠΊΠΎΠ΄:
<div class="container pt-3">
<h1>ΠΠ»Π°Π²Π½Π°Ρ ΡΡΡΠ°Π½ΠΈΡΠ°</h1>
<router-outlet></router-outlet>
</div>
###########################################################################################
Π£ΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ GraphQL (Π΅ΡΠ»ΠΈ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π·Π°ΠΏΡΡΠ΅Π½ΠΎ, Π² Π½Π°ΡΠ°Π»Π΅ ΡΠΎΡΠΌΠΎΠ·ΠΈΠΌ Π΅Π³ΠΎ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ Π² ΡΠ΅ΡΠΌΠΈΠ½Π°Π»Π΅: Ctrl + C):
cd frontend/
ng add apollo-angular
? Url to your GraphQL API http://localhost:3001/graphql ? Version of GraphQL 16 Enter
ΠΠ΅ΡΠ΅Ρ ΠΎΠ΄ΠΈΠΌ Π² ΠΏΠ°ΠΏΠΊΡ environments/ ΠΈ ΠΏΡΠΎΠΏΠΈΡΡΠ²Π°Π΅ΠΌ Π² 2Ρ ΡΠ°ΠΉΠ»Π°Ρ uri Π²Π·ΡΡΡΠΉ Π² ΡΠ°ΠΉΠ»Π΅ graphql.module.ts Π° Π½Π° Π΅Π³ΠΎ ΠΌΠ΅ΡΡΠΎ Π²ΠΏΠΈΡΡΠ²Π°Π΅ΠΌ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ ΠΈΠ· environment.ts:
const uri = environment.graphql_uri;
environment.ts:
export const environment = {
production: false,
graphql_uri: 'http://localhost:3001/graphql',
};
environment.prod.ts:
export const environment = {
production: true,
graphql_uri: 'http://localhost:3001/graphql',
};