Appearance
ServiceAccounts Module
Service accounts are a specialized type of user within an application, designed specifically for enabling secure and authorized service-to-service communication or API access. They are represented as User entities with a type set to SA (service account) and have their own roles and permissions, just like regular users.
The Service Account API provides a dedicated layer for managing service accounts, simplifying their creation, modification, and management. By using service accounts instead of regular user accounts, applications can maintain a clear separation between human users and automated processes, ensuring proper access control and reducing the risk of unauthorized actions.
Key Components
The module provides a set of reusable components that can be easily integrated into a NestJS project:
Service Accounts Service
The ServiceAccountsService is a specialized module within an application that handles the creation, modification, and management of service accounts.
Service Accounts Resolver
The ServiceAccountsResolver is a crucial component within a NestJS application that handles the GraphQL queries and mutations related to service accounts
Entity Inheritance
NestKit provide base classes that can be inherited and customized with your needs. For entities we use TypeORM TableInheritance.
How to install ServiceAccountsModule to my app
To create a ServiceAccountsModule in NestJS, you can follow these steps:
Install the required dependencies for
ServiceAccountsModule:Create a
service-accountsfolder in your project'ssrc/modulesdirectory.Inside the
service-accountsfolder, create aservice-accounts.service.tsfile and extendsBaseServiceAccountsServicefrom NestKit with the following code:ts// src/modules/service-accounts/service-accounts.service.ts import { BaseServiceAccountsService } from '@deeepvision/nest-kit/dist/modules/service-accounts'; import { Injectable } from '@nestjs/common'; import { UserToRole } from '../user-to-roles/user-to-role.entity'; import { User } from '../users/user.entity'; import { Organization } from '@/modules/organizations/organization.entity'; @Injectable() export class ServiceAccountsService extends BaseServiceAccountsService<User, UserToRole, Organization> {}Inside the
service-accountsfolder, create aservice-accounts.resolver.tsfile and extendsBaseServiceAccountsResolverwith the following code:ts// src/modules/serviceaccounts/service-accounts.resolver.ts import { Resolver } from '@nestjs/graphql'; import { User } from '@/modules/users/user.entity'; import { BaseServiceAccountsResolver } from '@deeepvision/nest-kit/dist/modules/service-accounts'; import { UserToRole } from '../user-to-roles/user-to-role.entity'; import { Organization } from '../organizations/organization.entity'; @Resolver() export class ServiceAccountsResolver extends BaseServiceAccountsResolver<User, UserToRole, Organization>(User) {}Inside the
service-accounts/service-tokensfolder, createservice-token.entity.tsfile and extends BaseServiceToken with the following code:ts// src/modules/serviceaccounts/service-tokens/service-token.entity.ts import { BaseServiceToken } from '@deeepvision/nest-kit/dist/modules/service-accounts/service-tokens/service-token.entity'; import { ObjectType } from '@nestjs/graphql'; import { ChildEntity } from 'typeorm'; import { User } from '@/modules/users/user.entity'; @ObjectType() @ChildEntity() export class ServiceToken extends BaseServiceToken { user!: Promise<User>; }Inside the
service-accounts/service-tokensfolder, createservice-tokens.service.tsfile and extends BaseServiceTokensService with the following code:ts// src/modules/serviceaccounts/service-tokens/service-tokens.service.ts import { User } from '@/modules/users/user.entity'; import { Injectable } from '@nestjs/common'; import { ServiceToken } from './service-token.entity'; import { BaseServiceTokensService } from '@deeepvision/nest-kit/dist/modules/service-accounts'; @Injectable() export class ServiceTokensService extends BaseServiceTokensService<User, ServiceToken> {}Inside the
service-accounts/service-tokensfolder, createservice-accounts.resolver.tsfile and extends BaseServiceTokensResolver with the following code:ts// src/modules/serviceaccounts/service-tokens/service-accounts.resolver.ts import { Resolver } from '@nestjs/graphql'; import { ServiceToken } from './service-token.entity'; import { User } from '@/modules/users/user.entity'; import { BaseServiceTokensResolver } from '@deeepvision/nest-kit/dist/modules/service-accounts'; @Resolver() export class ServiceTokensResolver extends BaseServiceTokensResolver<User, ServiceToken>(ServiceToken) {}Inside the
service-accountsfolder, create aservice-accounts.module.tsfile with the following code:ts// src/modules/service-accounts/service-accounts.module.ts import { Module } from '@nestjs/common'; import { ServiceAccountsService } from './service-accounts.service'; import { ServiceAccountsResolver } from './service-accounts.resolver'; import { UsersModule } from '../users/users.module'; import { UserToRolesModule } from '../user-to-roles/user-to-roles.module'; import { OrganizationsModule } from '../organizations/organizations.module'; import { ServiceTokensService } from './service-tokens/service-tokens.service'; import { ServiceTokensResolver } from './service-tokens/service-tokens.resolver'; import { TypeOrmModule } from '@nestjs/typeorm'; import { ServiceToken } from './service-tokens/service-token.entity'; import { SERVICE_ACCOUNTS_SERVICE_TOKEN, SERVICE_TOKENS_SERVICE_TOKEN } from '@deeepvision/nest-kit/dist/modules/service-accounts'; @Module({ imports: [ TypeOrmModule.forFeature([ ServiceToken, ]), UsersModule, UserToRolesModule, OrganizationsModule, ], providers: [ ServiceAccountsService, { provide: SERVICE_ACCOUNTS_SERVICE_TOKEN, useExisting: ServiceAccountsService, }, ServiceAccountsResolver, ServiceTokensService, { provide: SERVICE_TOKENS_SERVICE_TOKEN, useExisting: ServiceTokensService, }, ServiceTokensResolver, ], exports: [ ServiceAccountsService, { provide: SERVICE_ACCOUNTS_SERVICE_TOKEN, useExisting: ServiceAccountsService, }, ], }) export class ServiceAccountsModule {}Finally, import the
ServiceAccountsModuleinto your main AppModule or another module:ts// src/modules/app.module.ts import { Module } from '@nestjs/common'; import { ServiceAccountsModule } from './modules/service-accounts/service-accounts.module.ts'; @Module({ imports: [ServiceAccountsModule], }) export class AppModule {}In this code, we are importing the
ServiceAccountsModuleand adding it to theimportsarray of ourAppModule.
How to create service account with full access to service
Here's an example of how to call ServiceAccountService.create with an CreateServiceAccountOptions:
ts
const boServiceAccountWithFullAccess = await this.serviceAccountService.create({
alias: 'backoffice',
name: 'Backoffice App',
permissions: [
'bo:*:*:*',
],
});
// boServiceAccountWithFullAccess => User with type "SA"Each service account is created with a unique email in the format {alias}@{saEmailDomain}. For example, if the alias is backoffice, the email for the service account would be [email protected]. This ensures that each service account has a distinct email address within the system.
Create new service-account with GraphQL (full access)
Here's an example GraphQL mutation to create a new service-account (full access).
How to create service account with specific role in organization
Here's an example of how to call ServiceAccountService.create with an CreateServiceAccountOptions and create service account with role in organization:
ts
const boServiceAccountWithRole = await this.serviceAccountService.create({
alias: 'backoffice',
name: 'Backoffice App',
organizationId: 'hcorg:xjg239iomjs',
roleId: 'hcrol:2j3g2jgg2jt',
});
// boServiceAccountWithRole => User with type "SA"Create new service account with GraphQL (with role)
Here's an example GraphQL mutation to create a new service-account (with role).
How to check if service account is exists by alias
To check if a service account with a specific alias exists, you can call the isExists method of the ServiceAccountServices class. Here's an example:
ts
const alias = 'backoffice';
const isExists = await this.serviceAccountService.isExists(alias);
if (isExists) {
console.log(`A service account with the alias "${alias}" already exists.`);
}In this example, we check whether a service account with the alias backoffice exists. The isExists method returns a boolean value indicating the existence of the service account.
How to create service token for a service account
To create a long-lived service token for a service account, you can call the createServiceToken method of the ServiceTokensService class. Here's an example:
ts
const input = {
serviceAccountId: 'hcsa:xxxxxxxxxxx',
name: 'Token01',
}
const accessToken = await this.serviceTokensService.create(input, ctx);
console.log(`Createed service token for service account ID "${serviceAccountId}": ${accessToken}`);In this example, we create an serive token for a service account with a given ID. The createServiceToken method returns a string containing the access token. You can then use this token for making authorized requests on behalf of the service account.
Create service token for service account with GraphQL
Here's an example GraphQL mutation to create service token for service account
How to update service account
Here's an example of how to call ServiceAccountService.update with an UpdateServiceAccountOptions and update service account:
ts
const updatedServiceAccount = await this.serviceAccountService.update({
id: 'hcsa:xxxxxxxxxxx',
name: 'Updated Backoffice App',
});How to delete service account
Call the delete method of the ServiceAccountsService and pass in the id of the service account you want to delete:
ts
await this.serviceAccountsService.delete('hcsa:xxxxxxxxxxx');In this example, we are deleting a service account with an id of hcsa:xxxxxxxxxxx.
Delete service account with GraphQL
Here's an example GraphQL mutation to delete service account.
ServiceAccountsService uses Soft-Delete feature
Soft delete is a feature in NestKit that allows you to mark a record as "deleted" instead of physically deleting it from the database. When you use soft delete, the record is not actually removed from the database, but a status sets to DELETED to indicate that the record has been "soft-deleted". This allows you to retain the data and history associated with the record, while still hiding it from normal queries.