Primary_keys
Chapter 8: Primary Keys & Generated Values
Section titled “Chapter 8: Primary Keys & Generated Values”Understanding Primary Keys in TypeORM
Section titled “Understanding Primary Keys in TypeORM”8.1 Primary Key Overview
Section titled “8.1 Primary Key Overview”A primary key uniquely identifies each row in a table. TypeORM provides several ways to define primary keys.
Primary Key Types ================================================================================
Primary Key | +-------------------+-------------------+ | | | | v v v v +-------+ +----------+ +--------+ +-----------+ | Simple| | Auto-inc | | UUID | | Composite | | Key | | (Serial) | | | | Key | +-------+ +----------+ +--------+ +-----------+ | | | | v v v v Manual Automatic Globally Multiple Assignment Generation Unique Columns ID
================================================================================8.2 @PrimaryColumn()
Section titled “8.2 @PrimaryColumn()”Use @PrimaryColumn() when you want to manually assign primary key values.
import { Entity, PrimaryColumn, Column } from 'typeorm';
@Entity('countries')export class Country { // String primary key @PrimaryColumn() code: string; // SQL: code VARCHAR(255) PRIMARY KEY
@Column() name: string;}
// Usage - you must provide the primary keyconst country = new Country();country.code = 'US'; // Must be set manuallycountry.name = 'United States';await repository.save(country);When to Use @PrimaryColumn()
Section titled “When to Use @PrimaryColumn()” Use Cases for Manual Primary Keys +------------------------------------------------------------------+ | | | Scenario | Example | | ---------------------|---------------------------------------- | | Country codes | 'US', 'UK', 'IN' | | Currency codes | 'USD', 'EUR', 'INR' | | Status codes | 'ACTIVE', 'PENDING', 'INACTIVE' | | External IDs | IDs from external systems | | Natural keys | Email, username (with validation) | | | +------------------------------------------------------------------+8.3 @PrimaryGeneratedColumn()
Section titled “8.3 @PrimaryGeneratedColumn()”Use @PrimaryGeneratedColumn() for auto-generated primary keys.
Auto-Increment Integer
Section titled “Auto-Increment Integer”import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('users')export class User { // Auto-increment integer (1, 2, 3, ...) @PrimaryGeneratedColumn() id: number; // PostgreSQL: id SERIAL PRIMARY KEY // MySQL: id INT AUTO_INCREMENT PRIMARY KEY
@Column() name: string;}
// Usage - ID is auto-generatedconst user = new User();user.name = 'John Doe';await repository.save(user);console.log(user.id); // 1 (auto-assigned)import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('users')export class User { // UUID primary key @PrimaryGeneratedColumn('uuid') id: string; // PostgreSQL: id UUID PRIMARY KEY DEFAULT uuid_generate_v4() // MySQL: id CHAR(36) PRIMARY KEY
@Column() name: string;}
// Usage - UUID is auto-generatedconst user = new User();user.name = 'John Doe';await repository.save(user);console.log(user.id); // 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'Identity (PostgreSQL 10+)
Section titled “Identity (PostgreSQL 10+)”import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('users')export class User { // Identity column (PostgreSQL 10+) @PrimaryGeneratedColumn('identity') id: number; // SQL: id GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
@Column() name: string;}Row ID (CockroachDB)
Section titled “Row ID (CockroachDB)”import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('users')export class User { // Row ID (CockroachDB) @PrimaryGeneratedColumn('rowid') id: number;
@Column() name: string;}8.4 UUID Generation Strategies
Section titled “8.4 UUID Generation Strategies” UUID Generation Strategies +------------------------------------------------------------------+ | | | Strategy | Description | | -------------------|------------------------------------------| | uuid v4 | Random UUID (most common) | | uuid v1 | Time-based UUID | | uuid v5 | Namespace-based UUID | | Database-generated | Generated by database function | | | +------------------------------------------------------------------+TypeORM UUID Generation
Section titled “TypeORM UUID Generation”import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('users')export class User { // TypeORM generates UUID before insert @PrimaryGeneratedColumn('uuid') id: string; // Uses uuid v4 by default}Database-Level UUID Generation
Section titled “Database-Level UUID Generation”import { Entity, PrimaryColumn, Column } from 'typeorm';
@Entity('users')export class User { // Let database generate UUID @PrimaryColumn({ type: 'uuid', default: () => 'uuid_generate_v4()', }) id: string;
@Column() name: string;}Custom UUID Generation
Section titled “Custom UUID Generation”import { Entity, PrimaryColumn, Column, BeforeInsert } from 'typeorm';import { v4 as uuidv4 } from 'uuid';
@Entity('users')export class User { @PrimaryColumn({ type: 'uuid' }) id: string;
@Column() name: string;
// Generate UUID before insert @BeforeInsert() generateId() { if (!this.id) { this.id = uuidv4(); } }}8.5 Composite Primary Keys
Section titled “8.5 Composite Primary Keys”Composite primary keys use multiple columns to uniquely identify a row.
Composite Primary Key +------------------------------------------------------------------+ | | | +---------------------------+ | | | enrollments | | | +---------------------------+ | | | student_id (PK) | <-- Part of composite key | | | course_id (PK) | <-- Part of composite key | | | enrollment_date | | | | grade | | | +---------------------------+ | | | | Primary Key: (student_id, course_id) | | Each combination is unique | | | +------------------------------------------------------------------+Defining Composite Primary Keys
Section titled “Defining Composite Primary Keys”import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm';import { Student } from './student.entity';import { Course } from './course.entity';
@Entity('enrollments')export class Enrollment { // First part of composite key @PrimaryColumn() studentId: number;
// Second part of composite key @PrimaryColumn() courseId: number;
@Column() enrollmentDate: Date;
@Column() grade: string;
// Relations @ManyToOne(() => Student, student => student.enrollments) student: Student;
@ManyToOne(() => Course, course => course.enrollments) course: Course;}Composite Key with Relations
Section titled “Composite Key with Relations”import { Entity, PrimaryColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
@Entity('order_items')export class OrderItem { @PrimaryColumn() orderId: number;
@PrimaryColumn() productId: number;
@Column() quantity: number;
@Column({ type: 'decimal', precision: 10, scale: 2 }) unitPrice: number;
// Relation to Order @ManyToOne(() => Order, order => order.items) @JoinColumn({ name: 'orderId' }) order: Order;
// Relation to Product @ManyToOne(() => Product) @JoinColumn({ name: 'productId' }) product: Product;}8.6 Generated Columns
Section titled “8.6 Generated Columns”TypeORM supports database-generated values beyond primary keys.
@Generated()
Section titled “@Generated()”import { Entity, PrimaryGeneratedColumn, Column, Generated } from 'typeorm';
@Entity('orders')export class Order { @PrimaryGeneratedColumn() id: number;
// Auto-generated UUID @Generated('uuid') @Column({ unique: true }) orderNumber: string; // SQL: orderNumber VARCHAR UNIQUE DEFAULT uuid_generate_v4()
@Column() customerName: string;}Identity Columns
Section titled “Identity Columns”import { Entity, PrimaryGeneratedColumn, Column, Generated } from 'typeorm';
@Entity('invoices')export class Invoice { @PrimaryGeneratedColumn() id: number;
// Auto-increment identity @Generated('increment') @Column({ unique: true }) invoiceNumber: number; // SQL: invoiceNumber INT UNIQUE GENERATED BY DEFAULT AS IDENTITY
@Column() customerName: string;}8.7 Sequence-Based Generation
Section titled “8.7 Sequence-Based Generation”For databases that support sequences (PostgreSQL, Oracle).
import { Entity, PrimaryColumn, Column, Generated } from 'typeorm';
@Entity('invoices')export class Invoice { // Use sequence for primary key @PrimaryColumn() @Generated({ type: 'increment', name: 'invoice_seq', }) id: number; // SQL: // CREATE SEQUENCE invoice_seq; // id INT PRIMARY KEY DEFAULT nextval('invoice_seq')
@Column() customerName: string;}8.8 Choosing the Right Primary Key
Section titled “8.8 Choosing the Right Primary Key” Primary Key Selection Guide +------------------------------------------------------------------+ | | | Use Case | Recommended Type | | ----------------------------|---------------------------------| | General purpose | Auto-increment integer | | Distributed systems | UUID | | External reference | UUID | | Natural key exists | @PrimaryColumn (with caution) | | Junction tables | Composite key | | High insert volume | UUID (avoid contention) | | Public-facing IDs | UUID (hide internal structure) | | | +------------------------------------------------------------------+Pros and Cons
Section titled “Pros and Cons” Auto-Increment vs UUID +------------------------------------------------------------------+ | | | Auto-Increment Integer | | +------------------------+------------------------------------+ | | | Pros | Cons | | | +------------------------+------------------------------------+ | | | Compact (4-8 bytes) | Predictable | | | | Database efficient | Not globally unique | | | | Human readable | Can cause contention | | | | Good for sorting | Exposes table size | | | +------------------------+------------------------------------+ | | | | UUID | | +------------------------+------------------------------------+ | | | Pros | Cons | | | +------------------------+------------------------------------+ | | | Globally unique | Large (36 chars or 16 bytes) | | | | Non-predictable | Not human readable | | | | No contention | Slower inserts (random I/O) | | | | Good for distributed | Fragmented indexes | | | +------------------------+------------------------------------+ | | | +------------------------------------------------------------------+8.9 Primary Key Best Practices
Section titled “8.9 Primary Key Best Practices”1. Always Have a Primary Key
Section titled “1. Always Have a Primary Key”// Good@Entity('users')export class User { @PrimaryGeneratedColumn() id: number;}
// Bad - no primary key@Entity('users')export class User { @Column() name: string;}2. Use Surrogate Keys Over Natural Keys
Section titled “2. Use Surrogate Keys Over Natural Keys”// Good - surrogate key@Entity('users')export class User { @PrimaryGeneratedColumn() id: number;
@Column({ unique: true }) email: string; // Natural key as unique constraint}
// Risky - natural key as primary key@Entity('users')export class User { @PrimaryColumn() email: string; // What if email changes?}3. Avoid Business Logic in Primary Keys
Section titled “3. Avoid Business Logic in Primary Keys”// Good - simple primary key@Entity('orders')export class Order { @PrimaryGeneratedColumn() id: number;
@Column() orderNumber: string; // Business identifier}
// Bad - business logic in primary key@Entity('orders')export class Order { @PrimaryColumn() orderNumber: string; // Format: ORD-2024-0001}4. Use UUID for External APIs
Section titled “4. Use UUID for External APIs”@Entity('users')export class User { // Internal use @PrimaryGeneratedColumn() id: number;
// External use (APIs, URLs) @Generated('uuid') @Column({ unique: true }) publicId: string;}8.10 Primary Key with Relations
Section titled “8.10 Primary Key with Relations”import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from 'typeorm';
// User entity@Entity('users')export class User { @PrimaryGeneratedColumn() id: number;
@Column() name: string;
@OneToOne(() => Profile, profile => profile.user) profile: Profile;}
// Profile entity - shares primary key with User@Entity('profiles')export class Profile { // Primary key is also foreign key to User @PrimaryColumn() userId: number;
@Column() bio: string;
@OneToOne(() => User, user => user.profile) @JoinColumn({ name: 'userId' }) user: User;}8.11 Summary
Section titled “8.11 Summary” Primary Key Decorators Summary +------------------------------------------------------------------+ | | | Decorator | Description | | ------------------------|--------------------------------------| | @PrimaryColumn() | Manual primary key | | @PrimaryGeneratedColumn()| Auto-generated primary key | | @PrimaryGeneratedColumn('increment')| Auto-increment integer | | @PrimaryGeneratedColumn('uuid')| UUID primary key | | @PrimaryGeneratedColumn('identity')| Identity column (PG 10+) | | @Generated('uuid') | Auto-generated non-primary UUID | | @Generated('increment')| Auto-generated non-primary number | | | +------------------------------------------------------------------+Next Chapter
Section titled “Next Chapter”Chapter 9: Entity Relationships Overview
Last Updated: February 2026