drizzle-orm icon indicating copy to clipboard operation
drizzle-orm copied to clipboard

[FEATURE]: Extending base schemas

Open hisamafahri opened this issue 2 years ago • 2 comments

Describe what you want

Is there a way for us to extends aschema declaration from existing schema? This way, we don't need to repeatedly declaring repeated columns over and over again.

For example:

export const timeSchemas = pgTable({
    createdAt: timestamp("created_at").defaultNow().notNull(),
    updatedAt: timestamp("updated_at").defaultNow().notNull(),
    deletedAt: timestamp("deleted_at"),
})

export const songs = pgTable('songs', {
  id: serial('id').primaryKey(),
  // I can reuse the existing objects/schema/something similar
  ...timeSchemas,
});

hisamafahri avatar Jun 07 '23 09:06 hisamafahri

You can inverse your approach

export const baseTable = <
  TTableName extends string,
  TColumnsMap extends Record<string, AnyPgColumnBuilder>
>(
  name: TTableName,
  columns: TColumnsMap
) => {
  return pgTable(name, {
    ...columns,
    createdAt: timestamp("created_at").defaultNow().notNull(),
    updatedAt: timestamp("updated_at").defaultNow().notNull(),
    deletedAt: timestamp("deleted_at"),
  });
};

export const usersTable = baseTable("users", {
  id: integer("id").primaryKey(),
});

AndriiSherman avatar Jun 07 '23 20:06 AndriiSherman

I've split every table's schema in a different file (just like in the docs). And I put the baseTable inside the index.ts file (I've tried to change the filename or the directory too).

But, I've got the following errors:

drizzle-kit generate:pg --schema=./src/db/schema/ --out=./src/db/migrations/
drizzle-kit: v0.18.0
drizzle-orm: v0.26.5

ReferenceError: Cannot access 'baseTable' before initialization
    at Object.baseTable (/Users/hisamafahri/myprojects/packages/api/src/db/schema/index.ts:1:1)
    at Object.get [as baseTable] (/Users/hisamafahri/myprojects/packages/api/src/db/schema/index.ts:14:45)
    at Object.<anonymous> (/Users/hisamafahri/myprojects/packages/api/src/db/schema/accountsInVault.ts:7:32)
    at Module._compile (node:internal/modules/cjs/loader:1159:14)
    at Module._compile (/Users/hisamafahri/myprojects/node_modules/drizzle-kit/index.js:16334:30)
    at Module._extensions..js (node:internal/modules/cjs/loader:1213:10)
    at Object.newLoader [as .ts] (/Users/hisamafahri/myprojects/node_modules/drizzle-kit/index.js:16338:13)
    at Module.load (node:internal/modules/cjs/loader:1037:32)
    at Module._load (node:internal/modules/cjs/loader:878:12)
    at Module.require (node:internal/modules/cjs/loader:1061:19)

hisamafahri avatar Jun 08 '23 08:06 hisamafahri

Why not just use a plain object like so:

const timestamps = {
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().notNull(),
  deletedAt: timestamp("deleted_at"),
}

export const songs = pgTable('songs', {
  id: serial('id').primaryKey(),
  // spread the object
  ...timestamps,
});

ranjan-purbey avatar Jul 24 '23 20:07 ranjan-purbey

Follwing because its very important feature

25khattab avatar Aug 23 '23 17:08 25khattab

Why not just use a plain object like so:

const timestamps = {
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().notNull(),
  deletedAt: timestamp("deleted_at"),
}

export const songs = pgTable('songs', {
  id: serial('id').primaryKey(),
  // spread the object
  ...timestamps,
});

Seems to me that this is the best workaround. Is there something else Drizzle could provide to make this simpler?

Angelelz avatar Nov 14 '23 05:11 Angelelz

I've split every table's schema in a different file (just like in the docs). And I put the baseTable inside the index.ts file (I've tried to change the filename or the directory too).

But, I've got the following errors:

drizzle-kit generate:pg --schema=./src/db/schema/ --out=./src/db/migrations/
drizzle-kit: v0.18.0
drizzle-orm: v0.26.5

ReferenceError: Cannot access 'baseTable' before initialization
    at Object.baseTable (/Users/hisamafahri/myprojects/packages/api/src/db/schema/index.ts:1:1)
    at Object.get [as baseTable] (/Users/hisamafahri/myprojects/packages/api/src/db/schema/index.ts:14:45)
    at Object.<anonymous> (/Users/hisamafahri/myprojects/packages/api/src/db/schema/accountsInVault.ts:7:32)
    at Module._compile (node:internal/modules/cjs/loader:1159:14)
    at Module._compile (/Users/hisamafahri/myprojects/node_modules/drizzle-kit/index.js:16334:30)
    at Module._extensions..js (node:internal/modules/cjs/loader:1213:10)
    at Object.newLoader [as .ts] (/Users/hisamafahri/myprojects/node_modules/drizzle-kit/index.js:16338:13)
    at Module.load (node:internal/modules/cjs/loader:1037:32)
    at Module._load (node:internal/modules/cjs/loader:878:12)
    at Module.require (node:internal/modules/cjs/loader:1061:19)

Just bring all common base schemas into another file like common.ts, and import it in other schema files. DO NOT put these common base schemas in index.ts where you will export other schemas.

eyeix avatar Feb 22 '24 12:02 eyeix

I am working in a monorepo. If I spread the object like mentioned:

const timestamps = {
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().notNull(),
}

export const songs = pgTable('songs', {
  id: serial('id').primaryKey(),
  // spread the object
  ...timestamps,
});

export type Song = typeof songs.$inferSelect;

and import the type in another package, I get:

(alias) type Song = {
    [x: string]: any;
}

If I remove the spread timestamps, the type is passed successfully. I can create a new issue if needed.

trompx avatar Mar 04 '24 11:03 trompx

@trompx try adding as const like so:

const timestamps = {
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().notNull(),
} as const

ranjan-purbey avatar Mar 04 '24 18:03 ranjan-purbey

@ranjan-purbey It doesn't work unfortunately.

I also wanted to do the following:

export const date = (name: string) => {
  return timestamp(name).defaultNow().notNull();
};

export const datesColumns = {
  createdAt: date("created_at"),
  updatedAt: date("updated_at"),
};

But doesn't work either.

In pgTable:

  1. doing:
   ...datesColumns,

The whole table types are lost:

(alias) type User = {
    [x: string]: any;
}
  1. doing:
    createdAt: date("created_at"),
    updatedAt: date("updated_at"),

I get types

    createdAt: any;
    updatedAt: any;
  1. doing:
    createdAt: timestamp("created_at").defaultNow().notNull(),
    updatedAt: timestamp("updated_at").defaultNow().notNull(),

I get the correct types

    createdAt: Date;
    updatedAt: Date;

I want to mention that I use drizzle-orm/kysely. drizzle-orm : 0.30.1

trompx avatar Mar 10 '24 00:03 trompx

Please share a minimal reproducible example

ranjan-purbey avatar May 02 '24 17:05 ranjan-purbey