如何用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
相关产品推荐
相关产品推荐

