Использовал ли вы Foreign Key в базах данных?

Ответ

Да, активно использовал Foreign Key (внешние ключи) в реляционных базах данных для обеспечения ссылочной целостности. В Node.js проектах это реализуется либо на уровне схемы БД (например, в PostgreSQL), либо через ORM.

Пример с Sequelize (PostgreSQL):

// Определение моделей со связью
const User = sequelize.define('User', {
  id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
  name: DataTypes.STRING
});

const Post = sequelize.define('Post', {
  id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
  title: DataTypes.STRING,
  userId: {
    type: DataTypes.INTEGER,
    references: {
      model: User,
      key: 'id'
    },
    onDelete: 'CASCADE', // Автоматическое удаление зависимых записей
    onUpdate: 'CASCADE'
  }
});

// Определение ассоциации
Post.belongsTo(User, { foreignKey: 'userId' });
User.hasMany(Post, { foreignKey: 'userId' });

Практические преимущества, которые я наблюдал:

  • Целостность данных: Невозможно создать пост с несуществующим userId
  • Каскадные операции: При удалении пользователя автоматически удаляются все его посты (при onDelete: 'CASCADE')
  • Упрощение запросов: Sequelize автоматически строит JOIN-запросы при использовании include

Пример запроса с JOIN через ассоциацию:

const postsWithUsers = await Post.findAll({
  include: [{
    model: User,
    attributes: ['name', 'email']
  }]
});

В проектах с MongoDB использовал аналогичные концепции через ref в схемах Mongoose, хотя там нет настоящих внешних ключей на уровне базы.

Ответ 18+ 🔞

Э, слушай, а вот про внешние ключи — это вообще отдельная песня, ебушки-воробушки. Я их, конечно, юзал в реляционных базах, чтобы эта самая ссылочная целостность была, а то без неё — пизда рулю, честно говоря. В ноде это либо на уровне самой базы настраивается, либо через ORM типа Sequelize.

Вот смотри, как в Sequelize под PostgreSQL это выглядит:

// Модели объявляем со связью
const User = sequelize.define('User', {
  id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
  name: DataTypes.STRING
});

const Post = sequelize.define('Post', {
  id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
  title: DataTypes.STRING,
  userId: {
    type: DataTypes.INTEGER,
    references: {
      model: User,
      key: 'id'
    },
    onDelete: 'CASCADE', // Это чтобы всё каскадом почистилось
    onUpdate: 'CASCADE'
  }
});

// А вот и ассоциации
Post.belongsTo(User, { foreignKey: 'userId' });
User.hasMany(Post, { foreignKey: 'userId' });

И польза от этого, надо сказать, овердохуища:

  • Данные не разъебутся: Не получится воткнуть в пост userId от левого мужика с улицы — база не пропустит, ёпта.
  • Каскад — это сила: Удалил юзера — и все его посты автоматом накрылись медным тазом, если onDelete: 'CASCADE' стоит. Красота, а не жизнь.
  • Запросы проще: Sequelize сам эти JOIN'ы тебе нарисует, когда через include попросишь.

Вот, например, как пост с юзером одним запросом вытащить:

const postsWithUsers = await Post.findAll({
  include: [{
    model: User,
    attributes: ['name', 'email']
  }]
});

А в MongoDB, хоть там и нет настоящих внешних ключей как в SQL, я в схемах Mongoose через ref ту же идею реализовывал. В общем, штука полезная, без неё — доверия ебать ноль к данным.