Skip to main content

typeorm

Guidelines for developing with TypeORM, a full-featured ORM for TypeScript and JavaScript supporting multiple databases

소스 정보

저장소
rolling-scopes/rsschool-app
최근 소스 활동
2026년 3월 12일 22:23
감지된 SKILL.md 언어
영어
스타
10,397
포크
223

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
typeorm
description
Guidelines for developing with TypeORM, a full-featured ORM for TypeScript and JavaScript supporting multiple databases
# TypeORM Development Guidelines You are an expert in TypeORM, TypeScript, and database design with a focus on the Data Mapper pattern and enterprise application architecture. ## Core Principles - TypeORM supports both Active Record and Data Mapper patterns - Uses TypeScript decorators for entity and column definitions - Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, and more - Works in Node.js, Browser, Ionic, Cordova, React Native, NativeScript, Expo, and Electron - First-class support for database migrations ## TypeScript Configuration Required settings in tsconfig.json: ```json { "compilerOptions": { "experimentalDecorators": true, "emitDecoratorMetadata": true, "strict": true, "target": "ES2020", "module": "commonjs", "moduleResolution": "node" } } ``` ## Entity Definition ### Basic Entity ```typescript import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm'; @Entity('users') export class User { @PrimaryGeneratedColumn() id: number; @Column({ type: 'varchar', length: 255, unique: true }) email: string; @Column({ type: 'varchar', length: 255, nullable: true }) name: string | null; @Column({ type: 'boolean', default: true }) isActive: boolean; @CreateDateColumn() createdAt: Date; @UpdateDateColumn() updatedAt: Date; } ``` ### Primary Key Options ```typescript // Auto-increment @PrimaryGeneratedColumn() id: number; // UUID @PrimaryGeneratedColumn("uuid") id: string; // Custom primary key @PrimaryColumn() id: string; // Composite primary key @Entity() export class OrderItem { @PrimaryColumn() orderId: number; @PrimaryColumn() productId: number; } ``` ### Column Decorators ```typescript @Entity() export class Product { @PrimaryGeneratedColumn() id: number; // String columns @Column({ type: 'varchar', length: 255 }) name: string; @Column({ type: 'text', nullable: true }) description: string | null; // Numeric columns @Column({ type: 'decimal', precision: 10, scale: 2 }) price: number; @Column({ type: 'int', default: 0 }) stock: number; // Boolean @Column({ type: 'boolean', default: true }) isAvailable: boolean; // JSON @Column({ type: 'jsonb', nullable: true }) metadata: Record<string, any> | null; // Enum @Column({ type: 'enum', enum: ['active', 'inactive', 'pending'], default: 'pending', }) status: 'active' | 'inactive' | 'pending'; // Timestamps @CreateDateColumn() createdAt: Date; @UpdateDateColumn() updatedAt: Date; @DeleteDateColumn() deletedAt: Date | null; // For soft deletes // Version column for optimistic locking @VersionColumn() version: number; } ``` ## Relationships ### One-to-One ```typescript @Entity() export class User { @PrimaryGeneratedColumn() id: number; @OneToOne(() => Profile, profile => profile.user, { cascade: true }) @JoinColumn() profile: Profile; } @Entity() export class Profile { @PrimaryGeneratedColumn() id: number; @Column() bio: string; @OneToOne(() => User, user => user.profile) user: User; } ``` ### One-to-Many / Many-to-One ```typescript @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column() name: string; @OneToMany(() => Post, post => post.author) posts: Post[]; } @Entity() export class Post { @PrimaryGeneratedColumn() id: number; @Column() title: string; @ManyToOne(() => User, user => user.posts, { onDelete: 'CASCADE' }) @JoinColumn({ name: 'author_id' }) author: User; @Column() authorId: number; // Explicit foreign key column } ``` ### Many-to-Many ```typescript @Entity() export class Post { @PrimaryGeneratedColumn() id: number; @Column() title: string; @ManyToMany(() => Tag, tag => tag.posts) @JoinTable({ name: 'post_tags', joinColumn: { name: 'post_id' }, inverseJoinColumn: { name: 'tag_id' }, }) tags: Tag[]; } @Entity() export class Tag { @PrimaryGeneratedColumn() id: number; @Column({ unique: true }) name: string; @ManyToMany(() => Post, post => post.tags) posts: Post[]; } ``` ## Repository Pattern ### Basic Repository Usage ```typescript import { AppDataSource } from './data-source'; import { User } from './entities/User'; const userRepository = AppDataSource.getRepository(User); // Find all const users = await userRepository.find(); // Find with conditions const activeUsers = await userRepository.find({ where: { isActive: true }, }); // Find one const user = await userRepository.findOne({ where: { id: 1 }, }); // Find or fail const user = await userRepository.findOneOrFail({ where: { id: 1 }, }); // Save const newUser = userRepository.create({ email: 'user@example.com', name: 'John Doe', }); await userRepository.save(newUser); // Update await userRepository.update({ id: 1 }, { name: 'Jane Doe' }); // Delete await userRepository.delete({ id: 1 }); // Soft delete (requires @DeleteDateColumn) await userRepository.softDelete({ id: 1 }); ``` ### Custom Repository ```typescript import { Repository, DataSource } from 'typeorm'; import { User } from './entities/User'; export class UserRepository extends Repository<User> { constructor(private dataSource: DataSource) { super(User, dataSource.createEntityManager()); } async findByEmail(email: string): Promise<User | null> { return this.findOne({ where: { email } }); } async findActiveUsers(): Promise<User[]> { return this.find({ where: { isActive: true }, order: { createdAt: 'DESC' }, }); } async findWithPosts(userId: number): Promise<User | null> { return this.findOne({ where: { id: userId }, relations: ['posts'], }); } } ``` ### Query Builder ```typescript const users = await userRepository .createQueryBuilder('user') .leftJoinAndSelect('user.posts', 'post') .where('user.isActive = :isActive', { isActive: true }) .andWhere('post.publishedAt IS NOT NULL') .orderBy('user.createdAt', 'DESC') .skip(0) .take(10) .getMany(); // With raw results const result = await userRepository .createQueryBuilder('user') .select('COUNT(*)', 'count') .where('user.isActive = :isActive', { isActive: true }) .getRawOne(); // Insert with query builder await userRepository .createQueryBuilder() .insert() .into(User) .values([ { email: 'user1@example.com', name: 'User 1' }, { email: 'user2@example.com', name: 'User 2' }, ]) .execute(); ``` ## Data Source Configuration ```typescript // data-source.ts import { DataSource } from 'typeorm'; import { User } from './entities/User'; import { Post } from './entities/Post'; export const AppDataSource = new DataSource({ type: 'postgres', host: process.env.DB_HOST || 'localhost', port: parseInt(process.env.DB_PORT || '5432'), username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, // Entity configuration entities: [User, Post], // Or use glob pattern: entities: ["src/entities/**/*.ts"] // Migrations migrations: ['src/migrations/**/*.ts'], // Synchronize - NEVER use in production synchronize: false, // Logging logging: process.env.NODE_ENV === 'development', // Connection pool poolSize: 10, // SSL (for production) ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false, }); // Initialize connection AppDataSource.initialize() .then(() => console.log('Data Source initialized')) .catch(error => console.error('Error initializing Data Source:', error)); ``` ## Migrations ### Creating Migrations ```bash # Generate migration from entity changes npx typeorm migration:generate src/migrations/CreateUsers -d src/data-source.ts # Create empty migration npx typeorm migration:create src/migrations/SeedUsers # Run migrations npx typeorm migration:run -d src/data-source.ts # Revert last migration npx typeorm migration:revert -d src/data-source.ts ``` ### Migration File Structure ```typescript import { MigrationInterface, QueryRunner, Table, TableIndex } from 'typeorm'; export class CreateUsers1234567890 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise<void> { await queryRunner.createTable( new Table({ name: 'users', columns: [ { name: 'id', type: 'int', isPrimary: true, isGenerated: true, generationStrategy: 'increment', }, { name: 'email', type: 'varchar', length: '255', isUnique: true, }, { name: 'name', type: 'varchar', length: '255', isNullable: true, }, { name: 'is_active', type: 'boolean', default: true, }, { name: 'created_at', type: 'timestamp', default: 'CURRENT_TIMESTAMP', }, { name: 'updated_at', type: 'timestamp', default: 'CURRENT_TIMESTAMP', onUpdate: 'CURRENT_TIMESTAMP', }, ], }), true, ); await queryRunner.createIndex( 'users', new TableIndex({ name: 'IDX_USERS_EMAIL', columnNames: ['email'], }), ); } public async down(queryRunner: QueryRunner): Promise<void> { await queryRunner.dropIndex('users', 'IDX_USERS_EMAIL'); await queryRunner.dropTable('users'); } } ``` ## Transactions ```typescript // Using QueryRunner const queryRunner = AppDataSource.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); try { const user = queryRunner.manager.create(User, { email: 'user@example.com', name: 'User', }); await queryRunner.manager.save(user); const post = queryRunner.manager.create(Post, { title: 'First Post', author: user, });
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기