You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用NestJS和TypeORM将Supabase表外键关联到Auth.users表?

在NestJS + TypeORM中关联Supabase自带的Auth.users表

问题核心

你之前用@supabase/supabase-js的User接口做关联行不通——TypeORM的实体关联需要实体类而非TS接口;另外Supabase的auth.users是系统内置表,不需要我们用TypeORM创建,只需在自定义实体中建立外键关联映射即可。

解决方案步骤

1. 创建Auth.users的虚拟映射实体

定义一个对应auth.users的实体类,仅用于关联映射,不需要TypeORM自动生成表:

// src/auth/auth-user.entity.ts
import { Entity, Column, PrimaryColumn } from 'typeorm';

@Entity({ name: 'users', schema: 'auth' }) // 指定schema为auth,表名为users(Supabase内置表名)
export class AuthUser {
  @PrimaryColumn('uuid')
  id: string;

  // 可选:添加你需要用到的auth.users表字段,比如邮箱、创建时间等
  @Column({ type: 'text', nullable: true })
  email: string;

  @Column({ type: 'timestamp', nullable: true })
  created_at: Date;
}

2. 修正UserService实体的关联配置

修改你的UserService实体,正确配置与AuthUser的关联,同时修复service_id的错误配置(你之前误用了ForeignKeyMetadata,应该关联Service实体并指定外键列):

// src/user/user-service.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, OneToOne, JoinColumn } from 'typeorm';
import { AuthUser } from '../auth/auth-user.entity';
import { Service } from './service.entity';

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

  // 关联Supabase的auth.users表
  @OneToOne(() => AuthUser, { onDelete: 'CASCADE' }) // 对应SQL中的on delete cascade
  @JoinColumn({ name: 'user_id' }) // 指定外键列名为user_id,关联auth.users的id字段
  user: AuthUser;

  // 修正Service关联:关联Service实体并指定外键列
  @OneToOne(() => Service, { onDelete: 'CASCADE' })
  @JoinColumn({ name: 'service_id' })
  service: Service;

  @Column('text')
  token: string;

  @Column({ default: () => 'CURRENT_TIMESTAMP', type: 'timestamp' })
  created: Date;

  @Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP', onUpdate: 'CURRENT_TIMESTAMP' })
  updated: Date;
}

3. 配置TypeORM识别虚拟实体

在NestJS的TypeORM模块配置中,将AuthUser实体加入entities数组,确保TypeORM能识别这个映射:

// src/app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AuthUser } from './auth/auth-user.entity';
import { UserService } from './user/user-service.entity';
import { Service } from './user/service.entity';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      // 填入你的Supabase PostgreSQL连接信息
      type: 'postgres',
      host: process.env.DB_HOST,
      port: parseInt(process.env.DB_PORT),
      username: process.env.DB_USERNAME,
      password: process.env.DB_PASSWORD,
      database: process.env.DB_NAME,
      entities: [AuthUser, UserService, Service], // 加入虚拟实体AuthUser
      synchronize: process.env.NODE_ENV === 'development', // 生产环境建议关闭自动同步
    }),
  ],
})
export class AppModule {}

4. 启用行级安全(RLS)(可选)

如果需要像Supabase官方示例那样启用RLS,可以通过TypeORM的QueryRunner执行SQL,或直接在Supabase控制台操作:

// 示例:在模块初始化时配置RLS
import { Injectable, OnModuleInit } from '@nestjs/common';
import { DataSource } from 'typeorm';

@Injectable()
export class RlsSetupService implements OnModuleInit {
  constructor(private dataSource: DataSource) {}

  async onModuleInit() {
    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    try {
      // 给user_service表启用RLS
      await queryRunner.query('ALTER TABLE public.user_service ENABLE ROW LEVEL SECURITY;');
      // 可选:添加RLS策略,仅允许用户访问自己的数据
      await queryRunner.query(`
        CREATE POLICY "Users can access their own service links" ON public.user_service
        FOR SELECT USING (auth.uid() = user_id);
      `);
    } finally {
      await queryRunner.release();
    }
  }
}

关键说明

  • 虚拟实体AuthUser仅用于映射Supabase内置表,不需要通过synchronize创建该表
  • @JoinColumn用于指定外键列名,对应SQL中的references auth.users(id)逻辑
  • 生产环境建议关闭synchronize,改用TypeORM迁移脚本管理表结构变更

内容的提问来源于stack exchange,提问作者queng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 08:23:39