Database & Models
Spin up your database in Docker, then describe the two things this app stores: users and products. Use the Postgres tab (TypeORM) or the MongoDB tab (Mongoose) — whichever you picked.
01 — CHOOSE
Postgres vs MongoDB
Relational tables (Postgres) or flexible documents (MongoDB). Either works fine for this app — pick one and follow its tab everywhere.
Unsure?
Pick PostgreSQL. Inventory data is naturally tabular, and TypeORM gives you type safety end-to-end.
02 — RUN IT
Set Up the Database
No messy local install — Postgres runs in a local Docker container, and MongoDB runs in the cloud via MongoDB Atlas.
run Postgres 16
$ docker run --name inv-pg -e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=inventory -p 5432:5432 -d postgres:16
# .env →
DATABASE_URL="postgresql://postgres:secret@localhost:5432/inventory"Daily commands
For Postgres, use
docker start inv-pg / docker stop inv-pg to start & stop. MongoDB Atlas is hosted in the cloud, so it is always online and doesn't require daily commands. We'll replace Postgres startup with a tidy docker-compose.yml on the deploy page.03 — THE SHAPE
The Data Model
Two tables/collections power everything. Keep them small — you can always add fields later.
Why isVerified and minStock?
isVerified blocks login until the email link is clicked. minStock is the threshold that triggers a low-stock alert when quantity drops to or below it.04 — DEFINE
Connect & Define the Models
Pick your database tab. Each shows the connection plus both Express and NestJS wiring.
tsconfig
TypeORM uses decorators. In
server/tsconfig.json set "experimentalDecorators": true and "emitDecoratorMetadata": true, and add import "reflect-metadata"; at the very top of your entry file.Entities
server/src/entities.ts
import {
Entity, PrimaryGeneratedColumn, Column,
CreateDateColumn, UpdateDateColumn,
} from "typeorm";
@Entity()
export class User {
@PrimaryGeneratedColumn("uuid") id!: string;
@Column({ unique: true }) email!: string;
@Column() passwordHash!: string;
@Column({ default: false }) isVerified!: boolean;
@CreateDateColumn() createdAt!: Date;
}
@Entity()
export class Product {
@PrimaryGeneratedColumn("uuid") id!: string;
@Column() name!: string;
@Column({ unique: true }) sku!: string;
@Column({ type: "int", default: 0 }) quantity!: number;
@Column({ type: "int", default: 0 }) minStock!: number;
@CreateDateColumn() createdAt!: Date;
@UpdateDateColumn() updatedAt!: Date;
}For Express — a DataSource
server/src/data-source.ts
import "reflect-metadata";
import { DataSource } from "typeorm";
import { User, Product } from "./entities";
export const AppDataSource = new DataSource({
type: "postgres",
url: process.env.DATABASE_URL,
entities: [User, Product],
synchronize: true, // auto-creates tables in dev — turn OFF in prod
});
// in index.ts, before app.listen():
await AppDataSource.initialize();For NestJS — TypeOrmModule
server/src/app.module.ts
import { TypeOrmModule } from "@nestjs/typeorm";
import { ConfigModule } from "@nestjs/config";
import { User, Product } from "./entities";
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
TypeOrmModule.forRoot({
type: "postgres",
url: process.env.DATABASE_URL,
entities: [User, Product],
synchronize: true,
}),
TypeOrmModule.forFeature([User, Product]),
],
})
export class AppModule {}About synchronize / auto-schema
In development, letting the ORM (the library that maps code to database tables) create tables for you is convenient. For production, generate proper migrationsinstead so you never risk data loss. See the TypeORM & Mongoose docs linked above.
05 — CHECKPOINT
Confirm the Connection
Restart your backend. On boot you should see the connection log and (for Postgres) two tables created.
- ✓Database is running (Docker container for Postgres, cloud Atlas cluster for MongoDB)
- ✓Backend logs "connected" with no errors on start
- ✓
UserandProductmodels are defined and imported