Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Postgres enum in TypeORM

In TypeORM, how can I create a postgres enum type Gender as in this raw query

CREATE TYPE public.Gender AS ENUM (
    'male', 'female'
);
ALTER TABLE public.person ALTER COLUMN gender TYPE public.gender USING gender::gender;

and use it in the Entity class?

I have tried

@Entity()
export class Person {
    @Column('enum')
    gender: 'male' | 'female'
}

but obviously this isn't the right way, since I got the error message "type enum does not exist".

I don't want to use typescript enum either, since it will give me a bunch of 0s and 1s in the database.

like image 734
Tai Tran Avatar asked Jul 07 '17 15:07

Tai Tran


3 Answers

Enum is now supported on TypeOrm for postgres

By the docs

enum column type is supported by postgres and mysql. There are various possible column definitions:

Using typescript enums:

export enum UserRole {
    ADMIN = "admin",
    EDITOR = "editor",
    GHOST = "ghost"
}

@Entity()
export class User {

    @PrimaryGeneratedColumn()
    id: number;

    @Column({
        type: "enum",
        enum: UserRole,
        default: UserRole.GHOST
    })
    role: UserRole;

}

Using array with enum values:

export type UserRoleType = "admin" | "editor" | "ghost",

@Entity()
export class User {

    @PrimaryGeneratedColumn()
    id: number;

    @Column({
        type: "enum",
        enum: ["admin", "editor", "ghost"],
        default: "ghost"
    })
    role: UserRoleType;
}
like image 162
noam steiner Avatar answered Oct 23 '22 21:10

noam steiner


EDIT: This answer is still valid but a bit outdated as 0.1.0 alpha versions of TypeORM support enums for both PostgreSQL and MySQL.


PostgreSQL has a built in enum type, but unfortunately TypeORM currently only supports it for MySQL.

However, you could achieve a similar result with an int-type enum by using the @Column type as int and using the enum for your field type.

enum Gender {
  Male,
  Female,
  Other
}

@Entity()
export class Person {
    @Column('int')
    gender: Gender
}

(This approach lets you use the @IsEnum decorator from class-validator to validate the input if needed)

You could also use string enums (available on TypeScript 2.4, check Typescript `enum` from JSON string for older versions) and if that is the case just change the data type to string instead.

enum Gender {
  Male = 'male',
  Female = 'female',
  Other = 'other'
}

@Entity()
export class Person {
    @Column('text')
    gender: Gender
}
like image 40
Felipe Sabino Avatar answered Oct 23 '22 21:10

Felipe Sabino


As the accepted answer states, it is now supported in postgres but still buggy: Github issue, the fix will be released in the next RC probably. Meanwhile, I saw on the thread a nice solution which I even liked it more than the actual feature fully working:

fwiw I've been using string enum with check constraint. It's a lot more flexible than actual postgres enum, which creates whole new data types in postgres index and are really hard to manage (alter table, etc.)

export function CheckEnum(tableName: string, fieldName: string, enumValue: any) {
  // Hash enum value and put it as part of constraint name so we can
  // force typeorm to generate migration for enum changes.
  const hash = crypto
    .createHash('sha1')
    .update(Object.values(enumValue).join(''))
    .digest('hex')
  return Check(
    // https://til.hashrocket.com/posts/8f87c65a0a-postgresqls-max-identifier-length-is-63-bytes
    `cke_${tableName}_${fieldName}_${hash}`.slice(0, 63),
    `${fieldName} in (${Object.values(enumValue).map(t => `'${t}'`)})`,
  )
}

And use it like so

export enum Gender {
  Male = 'male',
  Female = 'female',
  Other = 'other'
}

@Entity()
@CheckEnum('person', 'gender', Gender)
export class Person {
like image 4
Javier Aviles Avatar answered Oct 23 '22 23:10

Javier Aviles