Convert JSON to TypeORM Schema!

Input JSON to generate TypeORM entities (@Entity, @PrimaryGeneratedColumn, @Column)! Great for NestJS projects.
JSON
Result
 

Convert JSON to TypeORM Schema!

Converts JSON data into a TypeORM entity class: an @Entity() class with a primary key, and every other key as an @Column() property with a TypeScript type, nullability and a class-validator decorator.

Use it to create the first entity for TypeOrmModule.forFeature([...]) in a NestJS + TypeORM app, or when adding a TypeORM DataSource to an Express project and you need entities that match data you already have.

How to use

  1. Paste the record to store as an object, or several records as an array. Fields missing from some records become nullable columns.
  2. Enter the entity class name in Model name (default MyModel); support ticket becomes SupportTicket.
  3. Save it as support-ticket.entity.ts and add it to your DataSource entities array or to TypeOrmModule.forFeature([SupportTicket]) in a NestJS module.

Example

Input (JSON)
[
  { "subject": "Login fails on Safari", "priority": 2, "assignee": "jdoe", "closed": false, "opened_at": "2024-06-03T08:12:00Z" },
  { "subject": "Export CSV is empty", "priority": 1, "closed": true, "opened_at": "2024-06-04T14:40:00Z" }
]
Output (TypeORM)
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
import { IsNotEmpty, IsOptional } from 'class-validator';

@Entity()
export class SupportTicket {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  @IsNotEmpty()
  subject: string;

  @Column()
  @IsNotEmpty()
  priority: number;

  @Column({ nullable: true })
  @IsOptional()
  assignee: string | null;

  @Column()
  @IsNotEmpty()
  closed: boolean;

  @Column({ name: 'opened_at' })
  @IsNotEmpty()
  openedAt: Date;
}

The second ticket has no assignee, so that column is @Column({ nullable: true }) with the type string | null. opened_at became the openedAt property typed Date, with name: 'opened_at' keeping the original column name.

Supported features

  • Primary key from your data: no id key or an integer id → @PrimaryGeneratedColumn() id: number; an id above the 32-bit range → @PrimaryGeneratedColumn({ type: 'bigint' }) id: string; a string id such as a UUID → @PrimaryColumn() id: string.
  • Column types: integers use the default @Column(), decimals get type: 'double precision' so fractions are not truncated, bigint and date-only date columns are typed string (TypeORM returns them as text), date-times are Date.
  • Objects and arrays become simple-json columns typed Record<string, unknown> or a typed array like string[].
  • Quoted digits like "007" remain string columns.
  • Properties are camelCase; @Column({ name: 'opened_at' }) is added whenever the JSON key differs.
  • Fields that were null or missing get nullable: true, | null and @IsOptional(); the rest get @IsNotEmpty().

Use cases

Filling in an entity right after `nest g resource`

Replace the empty ticket.entity.ts created by nest g resource tickets with this output; the entity then matches the mock data the frontend was already using, and Repository<SupportTicket> is ready to use.

A starting point for generated migrations

After creating the entity, typeorm migration:generate compares it with the database and writes the CREATE TABLE migration, column names and nullability included.

Notes and limitations

  • simple-json stores JSON as text, so you cannot filter on values inside it. On PostgreSQL switch to type: 'jsonb', on MySQL to type: 'json', if you need to query them.
  • double precision is a floating-point type; for prices use @Column('decimal', { precision: 10, scale: 2 }), which TypeORM returns as a string.
  • Relations are not inferred. Nested objects that are really other entities should become @ManyToOne or @OneToMany relations.

FAQ

What table name does the entity map to?

@Entity() has no name, so TypeORM's default naming turns the class name into snake_case: support_ticket. For an existing table, pass the name, e.g. @Entity('tickets').

What if emitDecoratorMetadata is off?

An option-less @Column() reads its type from reflection metadata, so TypeORM fails with a data type error. Enable it in tsconfig or state each type, e.g. @Column('varchar').

What database type does a Date property get?

datetime on MySQL and timestamp without time zone on PostgreSQL. If time zones matter on PostgreSQL, switch to @Column('timestamptz').