Entities

A Class Mapped to a Table

src/products/entities/product.entity.ts

import { Column, CreateDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
import { Category } from '../../categories/entities/category.entity';

const decimal = { to: (v: number) => v, from: (v: string | null) => (v === null ? null : Number(v)) };

@Entity('products')
export class Product {
  @PrimaryGeneratedColumn()
  id: number;

  @Index()
  @Column({ length: 120 })
  name: string;

  @Column({ type: 'text', nullable: true })
  description: string | null;

  @Column({ type: 'numeric', precision: 10, scale: 2, transformer: decimal })
  price: number;                         // Postgres returns numeric as string; the transformer fixes that

  @Column({ type: 'int', default: 0 })
  stock: number;

  @ManyToOne(() => Category, (category) => category.products, { nullable: false, onDelete: 'RESTRICT' })
  category: Category;

  @Column()
  categoryId: number;                    // the foreign key as a plain column too

  @CreateDateColumn({ type: 'timestamptz' })
  createdAt: Date;

  @UpdateDateColumn({ type: 'timestamptz' })
  updatedAt: Date;
}

The Table it Creates

CREATE TABLE "products" (
  "id" SERIAL PRIMARY KEY,
  "name" character varying(120) NOT NULL,
  "description" text,
  "price" numeric(10,2) NOT NULL,
  "stock" integer NOT NULL DEFAULT 0,
  "categoryId" integer NOT NULL REFERENCES "categories"("id") ON DELETE RESTRICT,
  "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
  "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()
);
CREATE INDEX "IDX_products_name" ON "products" ("name");

Note: Store money as numeric (or as an integer number of paise), never as float: 0.1 + 0.2 is not exactly 0.3 in floating point.