הגדרת TypeORM לאפליקציית האינטרנט שלך: ישויות, מיגרציות, רפוזיטוריז

כתיבת SQL ידנית וסנכרון בין הסכמה לקוד מובילים פעמים רבות לשגיאות ולחוסר יציבות בסביבת הייצור. אנו מגדירים עבורכם את TypeORM—משרת פשוט ועד יישום NestJS מורכב—ומבטיחים פעילות תקינה של entities, migrations ו-repositories. הצוות שלנו מספק הגדרה מלאה עם תמיכה מתמשכת, כך שהאפליקציה שלכם רצה באופן אמין וגדלה יחד עם העסק.

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

אלה רק חלק מהסוגים הטכניים של אתרים שאנו עובדים איתם, ולכל אחד מהם יכולים להיות מאפיינים ופונקציונליות ספציפיים משלו, וכן ניתן להתאים אותם לצרכים ולמטרות הספציפיים של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
הגדרת TypeORM לאפליקציית האינטרנט שלך: ישויות, מיגרציות, רפוזיטוריז
בינוני
~1 יום

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1359
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113
  • פיתוח אתר עבור SBH Partners
    פיתוח אתר עבור SBH Partners
    1201
  • פיתוח אתר עבור Red Pear
    פיתוח אתר עבור Red Pear
    619

הגדרת TypeORM עבור יישום האינטרנט שלך: ישויות, מיגרציות, מאגרי נתונים

בעת פיתוח עם Node.js ו-TypeScript, רבים נתקלים באותה בעיה: כתיבת שאילתות SQL ידנית וסנכרון הסכמה עם הקוד. טעות אחת בשאילתת SQL — והפרודקשן קורס. TypeORM פותר בעיה זו, אך התצורה שלו דורשת התחשבות בעשרות ניואנסים: מהגדרות חיבור ועד יצירת מיגרציות. אנו נעזור לכם להגדיר TypeORM עבור הפרויקט שלכם — משרת פשוט ועד יישום NestJS מורכב. למהנדסים שלנו יש ניסיון של למעלה מ-10 שנים בעבודה עם מסדי נתונים רלציוניים והם מבטיחים יציבות של הסכמה שלכם.

מדוע הגדרת TypeORM סטנדרטית מובילה לעיתים קרובות לבעיות?

שאילתת N+1 — בעיה קלאסית בעת שימוש ב-ORM. ללא אופטימיזציה, טעינת 100 מאמרים עם מחברים תייצר 101 שאילתות. TypeORM מאפשר להגדיר בקלות טעינה מוקדמת באמצעות relations או להשתמש ב-Query Builder לשליטה מדויקת. כאב נוסף הוא synchronize: true בפרודקשן: זה מוביל לאובדן נתונים כאשר הסכמה משתנה. תצורת מיגרציות נכונה עם יצירת קובצי diff היא הדרך הבטוחה היחידה לעדכון מסד הנתונים. הבעיה השלישית היא ביצועים: שאילתות לא מותאמות יוצרות עומס נוסף. TypeORM מספק כלים לניטור וכיוונון עדין: subscribers למטמון, connection pooling, ורישום שאילתות איטיות. תחת עומס טיפוסי של 10,000 בקשות לדקה, תצורה נכונה מפחיתה את מספר השאילתות ב-60%.

כיצד אנו מגדירים את TypeORM: מקרה מבחן

מהניסיון שלנו: בפרויקט עם PostgreSQL ועומס של 10,000 בקשות לדקה, נתקלנו בירידה בביצועים עקב N+1 בעת טעינת תגים קשורים. פתרון: השתמשנו ב-Query Builder עם leftJoinAndSelect ופאג'ינציה באמצעות skip/take. בנוסף, הגדרנו connection pool עם max: 20 ו-idleTimeoutMillis: 30000, מה שהפחית את זמן התגובה ב-40%. שימוש ב-Query Builder אפשר לבצע שאילתה מורכבת פי 3 מהר יותר בהשוואה לטעינה דרך relations. connection pooling נכון חסך ללקוח כ-$1.4k–1.9k בשנה על תשתית. הזמינו הגדרת TypeORM — קבלו ייעוץ ממהנדס תוך שעה.

// db/data-source.ts
import 'reflect-metadata'
import { DataSource } from 'typeorm'
import { User } from './entities/User'
import { Post } from './entities/Post'

export const AppDataSource = new DataSource({
  type: 'postgres',
  url: process.env.DATABASE_URL,
  entities: [User, Post],
  migrations: ['dist/db/migrations/*.js'],
  migrationsTableName: 'migrations',
  synchronize: false, // НИКОГДА true в production
  logging: process.env.NODE_ENV === 'development' ? ['query', 'error'] : ['error'],
  ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,
  extra: {
    max: 20,
    idleTimeoutMillis: 30000,
  }
})

// Инициализация
await AppDataSource.initialize()

מה כלול בהגדרת TypeORM?

תהליך העבודה מורכב מחמישה שלבים:

  1. ניתוח — אנו בוחנים את סכמת מסד הנתונים הנוכחית, העומס ופרופיל השאילתות.
  2. עיצוב — אנו בוחרים תבנית (Active Record או Data Mapper), מעצבים ישויות ויחסים.
  3. יישום — אנו מגדירים את החיבור, כותבים ישויות עם decorators, יוצרים מיגרציות ומאגרי נתונים.
  4. בדיקות — אנו בודקים ביצועים, היעדר N+1 ונכונות המיגרציות.
  5. פריסה וניטור — אנו פורסים לפרודקשן, מגדירים רישום והתראות.

מה כלול בתוצאה:

  • תצורת DataSource עם pool ו-SSL (תומך בכל מסד נתונים: PostgreSQL, MySQL, SQLite).
  • מאגרי נתונים וישויות עם אינדקסים ויחסים.
  • מיגרציות (יצירה, הרצה, rollback).
  • Query Builder לשאילתות טיפוסיות.
  • אינטגרציית NestJS (מודול, שירותים, בקרים).
  • Subscribers למטמון והתראות.
  • תיעוד API וארכיטקטורה.
  • אחריות ל-30 יום לאחר המסירה — אנו מתקנים באגים בחינם.

השוואת תבניות ובחירת ישויות

מאפיין Active Record Data Mapper
הפרדת לוגיקה נתונים והתנהגות יחד נתונים בישות, התנהגות במאגרי נתונים
מורכבות נמוכה, מתאים ל-CRUD פשוט גבוהה, דורש יותר קוד
בדיקות קשה יותר עקב קריאות ישירות למסד הנתונים קל יותר בזכות מאגרי נתונים
גמישות מוגבלת גבוהה, קל לשנות שאילתות
פופולריות בפרויקטים קטנים בפתרונות ארגוניים גדולים

המהנדסים שלנו תמיד ממליצים על Data Mapper עבור פרויקטים עם לוגיקה עסקית, מכיוון שהוא נותן יותר שליטה ומפשט בדיקות. אך אם אתם צריכים אב-טיפוס מהיר — Active Record הוא גם אפשרות עובדת. בחירת ישויות נכונה היא קריטית לביצועים. השתמשו ב-// db/data-source.ts import 'reflect-metadata' import { DataSource } from 'typeorm' import { User } from './entities/User' import { Post } from './entities/Post' export const AppDataSource = new DataSource({ type: 'postgres', url: process.env.DATABASE_URL, entities: [User, Post], migrations: ['dist/db/migrations/*.js'], migrationsTableName: 'migrations', synchronize: false, // НИКОГДА true в production logging: process.env.NODE_ENV === 'development' ? ['query', 'error'] : ['error'], ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false, extra: { max: 20, idleTimeoutMillis: 30000, } }) // Инициализация await AppDataSource.initialize() עבור מערכות מבוזרות, @PrimaryGeneratedColumn('uuid') עבור שדות המסוננים לעיתים קרובות. מאגר נתונים הוא שכבה בין הלוגיקה העסקית למסד הנתונים. עטפו את כל השאילתות לישות במאגר נתונים: זה הופך את הקוד לשימוש חוזר וניתן לבדיקה.

Decorator מטרה
@Index() UUID או מספר שלם עם הגדלה אוטומטית
@PrimaryGeneratedColumn שדה פשוט עם סוג ואפשרויות
@Column אינדקס להאצת שאילתות
@Index יחס של רבים-לאחד
@ManyToOne הצד ההפוך של היחס

דוגמה למאגר נתונים עם פאג'ינציה:

const postRepository = AppDataSource.getRepository(Post)
async function findPosts(opts: { page: number; limit: number; search?: string }) {
  const { page, limit, search } = opts
  const qb = postRepository.createQueryBuilder('post')
    .leftJoinAndSelect('post.author', 'author')
    .leftJoinAndSelect('post.tags', 'tag')
    .where('post.published = :published', { published: true })
    .orderBy('post.createdAt', 'DESC')
    .skip((page - 1) * limit)
    .take(limit)
  if (search) {
    qb.andWhere(
      'post.title ILIKE :search OR post.content ILIKE :search',
      { search: `%${search}%` }
    )
  }
  const [items, total] = await qb.getManyAndCount()
  return { items, total, pages: Math.ceil(total / limit) }
}
// Сложные агрегаты
const stats = await AppDataSource.query(`
  SELECT date_trunc('week', created_at) AS week,
         count(*) AS posts,
         count(*) FILTER (WHERE published = true) AS published
  FROM posts
  WHERE created_at >= now() - interval '90 days'
  GROUP BY 1
  ORDER BY 1
`)

מדוע מיגרציות חשובות לפרודקשן?

לפי התיעוד הרשמי של TypeORM, מיגרציות הן הדרך הבטוחה היחידה לניהול הסכמה בפרודקשן. מיגרציות מאפשרות גרסת שינויים בסכמת מסד הנתונים והחלתם ברצף. TypeORM יכול ליצור מיגרציות אוטומטית בהתבסס על שינויים בישויות. זה חוסך עד 50% מזמן הפיתוח של מיגרציות בהשוואה לכתיבת SQL ידנית. דוגמה ליצירה והחלה:

# Генерация миграции из diff схемы
npx typeorm migration:generate -n AddUserProfile -d dist/db/data-source.js

# Создать пустую миграцию вручную
npx typeorm migration:create -n AddIndexes

# Применить
npx typeorm migration:run -d dist/db/data-source.js

# Откатить последнюю
npx typeorm migration:revert -d dist/db/data-source.js
// db/migrations/1234567890-AddUserProfile.ts
import { MigrationInterface, QueryRunner } from 'typeorm'

export class AddUserProfile1234567890 implements MigrationInterface {
    public async up(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`
            CREATE TABLE profiles (
                id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
                user_id UUID NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE,
                bio TEXT,
                avatar_url VARCHAR(500),
                updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
            )
        `)
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`DROP TABLE profiles`)
    }
}

אינטגרציית NestJS ו-Subscribers לאירועים

ב-NestJS, הגדרת TypeORM מסתכמת בייבוא TypeOrmModule עם תצורה. בנוסף, ניתן להזריק מאגרי נתונים באמצעות ה-decorator @OneToMany. Subscribers מאפשרים להגיב לשינויים בישויות: לאחר insert, עדכון אינדקס חיפוש; לאחר פרסום, שליחת התראה. זהו כלי חזק אם לא משתמשים בו יותר מדי. דוגמה להגדרת NestJS:

// app.module.ts
import { TypeOrmModule } from '@nestjs/typeorm'

@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        type: 'postgres',
        url: config.get('DATABASE_URL'),
        entities: [__dirname + '/**/*.entity{.ts,.js}'],
        migrations: [__dirname + '/db/migrations/*{.ts,.js}'],
        migrationsRun: true,
        synchronize: false,
      })
    }),
    TypeOrmModule.forFeature([User, Post])
  ]
})
export class AppModule {}
טעויות נפוצות וכיצד להימנע מהן
  • Synchronize בפרודקשן — לעולם לא. השתמשו במיגרציות.
  • חוסר אינדקסים — הוסיפו const postRepository = AppDataSource.getRepository(Post) async function findPosts(opts: { page: number; limit: number; search?: string }) { const { page, limit, search } = opts const qb = postRepository.createQueryBuilder('post') .leftJoinAndSelect('post.author', 'author') .leftJoinAndSelect('post.tags', 'tag') .where('post.published = :published', { published: true }) .orderBy('post.createdAt', 'DESC') .skip((page - 1) * limit) .take(limit) if (search) { qb.andWhere( 'post.title ILIKE :search OR post.content ILIKE :search', { search: `%${search}%` } ) } const [items, total] = await qb.getManyAndCount() return { items, total, pages: Math.ceil(total / limit) } } // Сложные агрегаты const stats = await AppDataSource.query(` SELECT date_trunc('week', created_at) AS week, count(*) AS posts, count(*) FILTER (WHERE published = true) AS published FROM posts WHERE created_at >= now() - interval '90 days' GROUP BY 1 ORDER BY 1 `) על שדות שאתם מסננים לעיתים קרובות.
  • התעלמות מ-connection pool — הגדירו # Генерация миграции из diff схемы npx typeorm migration:generate -n AddUserProfile -d dist/db/data-source.js # Создать пустую миграцию вручную npx typeorm migration:create -n AddIndexes # Применить npx typeorm migration:run -d dist/db/data-source.js # Откатить последнюю npx typeorm migration:revert -d dist/db/data-source.js ו-// db/migrations/1234567890-AddUserProfile.ts import { MigrationInterface, QueryRunner } from 'typeorm' export class AddUserProfile1234567890 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise<void> { await queryRunner.query(` CREATE TABLE profiles ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE, bio TEXT, avatar_url VARCHAR(500), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ) `) } public async down(queryRunner: QueryRunner): Promise<void> { await queryRunner.query(`DROP TABLE profiles`) } } .
  • שאילתות N+1 — השתמשו ב-@InjectRepository או Query Builder עם // app.module.ts import { TypeOrmModule } from '@nestjs/typeorm' @Module({ imports: [ TypeOrmModule.forRootAsync({ inject: [ConfigService], useFactory: (config: ConfigService) => ({ type: 'postgres', url: config.get('DATABASE_URL'), entities: [__dirname + '/**/*.entity{.ts,.js}'], migrations: [__dirname + '/db/migrations/*{.ts,.js}'], migrationsRun: true, synchronize: false, }) }), TypeOrmModule.forFeature([User, Post]) ] }) export class AppModule {} .
  • אחסון סיסמאות בישות — השתמשו ב-@Index() ו-hash ב-extra.max.

זמנים משוערים ועלות

הגדרת TypeORM בסיסית עם ישויות, מיגרציות ומאגרי נתונים: 1–2 ימים. אינטגרציה לפרויקט NestJS עם מודולים ובדיקות: 2–3 ימים. העברת פרויקט קיים מ-ORM אחר ל-TypeORM: 3–5 ימים. עלות תצורת TypeORM בסיסית תלויה במורכבות הפרויקט. צרו קשר להערכה חינמית — זה לוקח לא יותר משעה.