We will create a new account table where the ID of the connected external accounts will be stored. So we will generate a migration for this.
In addition to the account table, indexes are created to perform more optimized searches in the database.
import { MigrationInterface, QueryRunner } from 'typeorm';
export class createTableAccounts1659967614370 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "public"."account" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "external_id" character varying NULL, "baas_id" character varying NULL, "integration_id" uuid NOT NULL, "user_id" uuid NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), "deleted_at" TIMESTAMP , CONSTRAINT "PK_9162znh8ld8tylna9z6odivqt8u" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`ALTER TABLE "public"."account" ADD CONSTRAINT "FK_5i6mtodoa8fcqzujy1qg53nvxlh" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE CASCADE`,
);
await queryRunner.query(
`ALTER TABLE "public"."account" ADD CONSTRAINT "FK_ugmbyl1vy7py0xhyx6qdx46w8a3" FOREIGN KEY ("integration_id") REFERENCES "public"."integration"("id") ON DELETE CASCADE`,
);
await queryRunner.query(
`CREATE INDEX "IDX_twrqghqf5afgc0u5ejz3pb1oco" ON "public"."account" ("user_id")`,
);
await queryRunner.query(
`CREATE INDEX "IDX_oyv3ypxovks0kioee3tk4nyvzq" ON "public"."account" ("integration_id")`,
);
await queryRunner.query(
`CREATE INDEX "IDX_ueqv91y92rs0stsvoyegrhue14" ON "public"."account" ("external_id")`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "public"."account" DROP CONSTRAINT "FK_5i6mtodoa8fcqzujy1qg53nvxlh"`,
);
await queryRunner.query(
`ALTER TABLE "public"."account" DROP CONSTRAINT "FK_ugmbyl1vy7py0xhyx6qdx46w8a3"`,
);
await queryRunner.query(
`DROP INDEX IF EXISTS "public"."IDX_twrqghqf5afgc0u5ejz3pb1oco"`,
);
await queryRunner.query(
`DROP INDEX IF EXISTS "public"."IDX_oyv3ypxovks0kioee3tk4nyvzq"`,
);
await queryRunner.query(
`DROP INDEX IF EXISTS "public"."IDX_ueqv91y92rs0stsvoyegrhue14"`,
);
await queryRunner.query('DROP TABLE "public"."account"');
}
}
Inside the feature folder, we will create a folder named account and with the following files:
account.entity.ts
In this file we will add the following configuration where we declare the properties and relationships that the accounts table will have.
import { Entity, Column, ManyToOne } from 'typeorm';
import { BaseModel } from 'src/common/database/base-model';
import { Integration } from 'src/features/integration/integration.entity';
import { User } from 'src/features/user/user.entity';
import { encryptionTransformer } from 'src/common/transformers/typeorm-encrypted.transformer';
@Entity()
export class Account extends BaseModel {
@Column({
nullable: true,
transformer: encryptionTransformer,
})
public externalId!: string;
@Column({
name: 'service_external_id',
nullable: true,
transformer: encryptionTransformer,
})
public serviceExternalId!: string;
@Column({ name: 'user_id' })
public userId!: string;
@Column({ name: 'integration_id' })
public integrationId!: string;
@ManyToOne(() => Integration, (integration) => integration.accounts, {
nullable: true,
})
public integration?: Integration;
@ManyToOne(() => User, (user) => user.accounts, { nullable: true })
public user?: User;
}
account.service.ts
This file will have all the methods needed to enter, get, and delete records from the accounts table.
addAccount: this method is used to add a new record to the account table.
getAccount: this method is used to get an account by its external ID and the integration it belongs to.
getAccountsByIntegrationId: this method is used to get all the accounts based on the ID of an integration.
getAccountByExternalId: this method is used to get an account using your external ID.
removeAccountById: this method is used to delete a record from the accounts table based on the database ID.
removeAllAccountsByIngratationId: this method is used to remove all records from the account table based on the ID of an integration.
removeAccountByExternalId: this method is used to delete an account based on its external ID.
exitsAccountByUser: this method will be used to check if the user already has an account based on an integration and external ID of an account. Returns a boolean value.
verifyAccountByUser: this method is used to verify a user's account. If the account does not exist, an exception occurs with a custom message that is passed as a parameter.
This file will make the connection between the service and the account entity. We will also import the integration module that will be used in the account service.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { IntegrationsModule } from '../integration/integrations.module';
import { Account } from './account.entity';
import { AccountService } from './account.service';
@Module({
imports: [TypeOrmModule.forFeature([Account]), IntegrationsModule],
providers: [AccountService],
controllers: [],
exports: [AccountService],
})
export class AccountsModule {}