npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

nestjs-appwrite

v1.1.1

Published

Easier Appwrite integration for your NestJS application.

Downloads

332

Readme

nestjs-appwrite

Easier Appwrite integration for your NestJS application.

Overview

This is a core library developed by Coffee IT which offers easier integration with Appwrite when using the NestJS framework.

The library offers a few functionalities:

  1. Appwrite database setup through decorators
  2. A SecretStore service which can be configured to use AWS Parameter Store or env variables
  3. AccessTokenGuard. This is a simple guard that verified the JWT token based on the JWT secret and adds an Appwrite Client in the body of the request
  4. CustomLogger. This logger does a setup of the log levels and exposes a function to log objects
  5. Generic utility functions

Appwrite setup through decorators

The library will create your database collections and configures the attribute and indexes based on your schema decorators.

Step 1: Configure the Appwrite Module

Setup the AppwriteModule inside your main AppModule and pass the required options. Based on the useEnvVarsForSecrets boolean the library will either use env variables for secrets or otherwise AWS parameter store. The library expects the following secrets:

JWT_SECRET=Secret that can be used to verify JWT tokens, only required when using AccessTokenGuard
APPWRITE_API_KEY=The Appwrite Api key

The names of the secrets need to be uppercase when using env variables and lowercase when using parameter store.

@Module({
  imports: [
    AppwriteModule.forRoot({
      endpoint: Config.APPWRITE_ENDPOINT,
      projectId: Config.APPWRITE_PROJECT_ID,
      databaseId: Config.APPWRITE_DATABASE_ID,
      awsRegion: AWS_REGION,
      useEnvVarsForSecrets: Config.USE_ENV_VARS,
    }),
  ],
})
export class AppModule {}

Step 2: Create your schema

You can create Appwrite database schemas based on decorators. For example:

import { ADMIN_TEAM_ID, EXPERIENCES_COLLECTION_ID, EXPERIENCES_COLLECTION_NAME } from '../../constants';
import { ExperienceType } from '../domain/experience-type.enum';
import { Permission, Role } from 'node-appwrite';
import { EnumProp, Index, Schema, StringProp } from 'appwrite-core';
import { Document } from 'appwrite-core';
import { ApiProperty, IntersectionType } from '@nestjs/swagger';

@Schema({
  collectionId: EXPERIENCES_COLLECTION_ID,
  collectionName: EXPERIENCES_COLLECTION_NAME,
  permissions: [
    Permission.read(Role.any()),
    Permission.write(Role.team(ADMIN_TEAM_ID))
  ],
})
export class Experience {
  @ApiProperty()
  @StringProp()
  title: string;

  @ApiProperty()
  @StringProp({ size: 30 })
  @Index({ type: 'unique', orders: 'DESC' })
  coverImageId: string;

  @ApiProperty()
  @StringProp({ size: 1000 })
  content: string;

  @ApiProperty({ enum: ExperienceType })
  @EnumProp({ enum: ExperienceType })
  type: ExperienceType;
}

export class ExperienceModel extends IntersectionType(Experience, Document) {}

Step 3: Import your schema

Within the module that needs the schema you will have to import the schema. For example:

@Module({
  imports: [
    AppwriteModule.forFeatureAsync([
      { class: Experience },
    ]),
  ],
  providers: [],
  controllers: [ExperienceController]
})
export class ExperienceModule {}

Step 4: Use your schema in controllers/services

You're now ready to use the schema within your controllers and services. The schema will be wrapped inside a AppwriteRepository class which offers many useful database operations. Example usage within a controller:

@Controller()
export class ExperienceController {

  constructor(
    @Inject(Experience.name) private readonly experienceRepo: AppwriteRepository<Experience>,
  ) {}

  @Post('experiences')
  @UseGuards(AccessTokenGuard)
  @ApiOkResponse({ type: ExperienceModel })
  public async createExperience(@AppClient() client: ClientWithToken, @Body() dto: CreateExperienceDto): Promise<ExperienceModel> {
    return this.experienceRepo.asAppClient(client).createDocument(dto);
  }
}

The ClientWithToken object is available here because it gets added to the request by the AccessTokenGuard. You then have to use asAppClient() to make use of the permissions of the client.

That's it. When you start your service the database will be configured and you can create a document with the properties of the schema.

Globally available services

This library exposes a few services that are globally available.

Guards

You can simply import the AccessTokenGuard and use it in your controller

@UseGuards(AccessTokenGuard)

Appwrite server client provider

You can inject an instance of the appwrite server client like so:

@Controller()
export class AppController  implements OnApplicationBootstrap {

  constructor(
    @Inject(CLIENT_PROVIDER_NAME) private readonly rootClient: Client,
  ) {
  }
  
  public async onApplicationBootstrap(): Promise<void> {
    await this.setupTeams();
  }

  private async setupTeams(): Promise<void> {
    const teams = new Teams(this.rootClient);
    try {
      await teams.get(ADMIN_TEAM_ID);
    } catch (err: any) {
      if (err.code === HttpStatus.NOT_FOUND) {
        await teams.create(ADMIN_TEAM_ID, ADMIN_TEAM_NAME);
        this.logger.debug(`Created team with id ${ADMIN_TEAM_ID} and name ${ADMIN_TEAM_NAME}`);
        return;
      }
      this.logger.error(err);
    }
  }
}

As you can see you can use the client to do any setup work yourself.

Store service provider

Let's say you want to make use of a secret that might be configured inside parameter store on a server or as a env variable locally.
You can then make use of SecretStoreService. Simply import the SecretStoreService inside your class like so:


@Injectable()
export class FirebaseService implements OnApplicationBootstrap {

  private readonly logger = new Logger();

  constructor(
    private readonly storeService: SecretStoreService
  ) { }

  public async onApplicationBootstrap(): Promise<void> {
    await this.init();
  }

  private async init() {
    let firebaseServiceAccount = await this.storeService.getSecretString(FIREBASE_SERVICE_ACCOUNT_SECRET);
    if (!firebaseServiceAccount) {
      this.logger.error(`Secret ${FIREBASE_SERVICE_ACCOUNT_SECRET} is undefined, unable to use Firebase`);
      return;
    }
    admin.initializeApp({
      credential: admin.credential.cert(firebaseServiceAccount)
    });
  }
}

In this example we get the Firebase service account secret from the SecretStoreService to setup firebase.