Lawrence  Lesch

Lawrence Lesch

1675498260

Typescript-starter: Nest Framework TypeScript Starter

Typescript-starter

A progressive Node.js framework for building efficient and scalable server-side applications.

Description

Nest framework TypeScript starter repository.

Installation

$ npm install

Running the app

# development
$ npm run start

# watch mode
$ npm run start:dev

# production mode
$ npm run start:prod

Test

# unit tests
$ npm run test

# e2e tests
$ npm run test:e2e

# test coverage
$ npm run test:cov

Support

Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please read more here.

Stay in touch

Download Details:

Author: Nestjs
Source Code: https://github.com/nestjs/typescript-starter 
License: MIT licensed

#typescript #javascript #nodejs #nest 

Typescript-starter: Nest Framework TypeScript Starter
Laura  Fox

Laura Fox

1661656140

Nestjs Postgres: Postgres Module for Nest Framework (node.js)

Description

PostgreSQL module for Nest framework (node.js) 😻

Installation

First install the module via yarn or npm and do not forget to install the driver package as well:

    $ npm i --save nest-postgres pg

or

    $ yarn add nest-postgres pg

Usage

PostgresModule

PostgresModule is the primary entry point for this package and can be used synchronously

@Module({
  imports: [
    PostgresModule.forRoot({
        connectionString: 'postgresql://[user]:[password]@[host]/[nameDb]',
        // or 
        // host: 'localhost',
        // database: [:databaseName],
        // password: [:passwordDb],
        // user: [:userDb],
        // port: 5432,
    }),
  ],
})

or asynchronously

@Module({
  imports: [
    PostgresModule.forRootAsync({
      useFactory: () => ({
        connectionString: 'postgresql://[user]:[password]@[host]/[nameDb]',
        // or 
        // host: 'localhost',
        // database: [:databaseName],
        // password: [:passwordDb],
        // user: [:userDb],
        // port: 5432,
      }),
    }),
  ],
})

Example of use

UsersService:

import { Client } from 'pg';
import { InjectClient } from 'nest-postgres';

@Injectable()
export class UsersService {
  constructor(@InjectClient() private readonly pg: Client) {}

  public async findAll(): Promise<any> {
    const users = await this.pg.query('SELECT * FROM users');
    return users.rows;
  }
}

UsersController:

import { Controller, Get } from '@nestjs/common';
import { UsersService } from './users.service';

@Controller()
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  async getAllUsers() {
    return await this.usersService.findAll();
  }
}

Multi Connections Database

@Module({
  imports: [
    PostgresModule.forRoot(
      {
        connectionString: 'postgresql://postgres:pass123@localhost:5432/nest1',
        // or 
        // host: 'localhost',
        // database: [:databaseName],
        // password: [:passwordDb],
        // user: [:userDb],
        // port: 5432,
      },
      'db1Connection',
    ),
    PostgresModule.forRoot(
      {
        connectionString: 'postgresql://postgres:pass123@localhost:5434/nest2',
      },
      'db2Connection',
    ),
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}

Usage example with Multi Connection

PostService:

import { Client } from 'pg';
import { InjectConnection } from 'nest-postgres';
import { CreatePostDto } from './dto/create-post.dto';

@Injectable()
export class PostService {
  constructor(
    @InjectConnection('db2Connection')
    private dbConnection: Client,
  ) {}

  public async findAll(): Promise<any> {
    const users = await this.dbConnection.query('SELECT * FROM posts');
    return users.rows;
  }

  public async create(createPostDto: CreatePostDto): Promise<any> {
    try {
      const user = await this.dbConnection.query(
        'INSERT INTO posts (title, description)  VALUES ($1, $2) RETURNING *',
        [createPostDto.title, createPostDto.description],
      );
      return user.rows;
    } catch (err) {
      throw new HttpException(err, HttpStatus.BAD_REQUEST);
    }
  }
}

UsersService:

import { Client } from 'pg';
import { InjectConnection } from 'nest-postgres';
import { CreateUserDto } from './dto/create-user.dto';

@Injectable()
export class UsersService {
  constructor(
    @InjectConnection('db1Connection')
    private dbConnection: Client,
  ) {}

  public async findAll(): Promise<any> {
    const users = await this.dbConnection.query('SELECT * FROM users');
    return users.rows;
  }

  public async create(createUserDto: CreateUserDto): Promise<any> {
    try {
      const user = await this.dbConnection.query(
        'INSERT INTO users (firstName, lastName)  VALUES ($1, $2) RETURNING *',
        [createUserDto.firstName, createUserDto.lastName],
      );
      return user.rows;
    } catch (err) {
      throw new HttpException(err, HttpStatus.BAD_REQUEST);
    }
  }
}

For more information on node-postgres for Nodejs see here

Contribute

Feel free to help this library, I'm quite busy with also another Nestjs packages, but the community will appreciate the effort of improving this library. Make sure you follow the guidelines

Stay in touch

Download details:

Author: Tony133
Source code: https://github.com/Tony133/nestjs-postgres 
License: MIT license

#nest #nestjs #node #typescript #postgressql

Nestjs Postgres: Postgres Module for Nest Framework (node.js)
Laura  Fox

Laura Fox

1661641380

Nestx Log4js: Provide Log4js Module As NestJS Module

nestx-log4js

Provide log4js module as NestJS module

This is root of @nestx-log4js monorepo.

To Get Started, please read @nestx-log4js/core Documentation

Core Module Documentation

log4js as NestJS Module.

Features

  • Provide log4js wrapper as NestJS Global Module
  • Provide Log4jsLogger instance for replacement logger usage

Installation

yarn add @nestx-log4js/core

Usage

Just want to use log4js?

Since logger is a special service in NestJS, we suppose import Log4jsModule and manual call app.useLogger(app.get(Log4jsLogger))

app.module.ts

import { Module } from '@nestjs/common';
import { Log4jsModule } from '@nestx-log4js/core';


@Module({
  imports: [
    Log4jsModule.forRoot()
  ]
})
export class AppModule {}

bootstrap.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module'; 
import { Log4jsLogger } from '@nestx-log4js/core';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  app.useLogger(app.get(Log4jsLogger));
  await app.listen(3000);
}
bootstrap();

For more details, you can refer unit tests in source code

Initial Log4jsModule with AsyncOptions (Production Usage)

You might want to use different appender (e.g. file/dateFile appender) in real production usage and initial in your ConfigService/LoggerOnlyConfigService

app.module.ts

import { Module } from '@nestjs/common';
import { Log4jsModule } from '@nestx-log4js/core';

// import your ConfigService/LoggerConfigService

@Module({
  imports: [
    Log4js.forRootAsync({
      inject: [ConfigService],
      useFactory: (config: ConfigService) => config.getLog4jsOptions() // config.getLog4jsOptions should return valid Log4jsOptions
    })
  ]
})
export class AppModule {}

Bundled Layout & Appenders

When using Log4jsModule.forRoot() and no spec any appenders and layouts,

It will use default below layouts:

export const LOG4JS_DEFAULT_LAYOUT = {
  type: 'pattern',
  // log4js default pattern %d{yyyy-MM-dd HH:mm:ss:SSS} [%thread] %-5level %logger{36} - %msg%n
  // we use process id instead thread id
  pattern: '%[%d{yyyy-MM-dd hh:mm:ss:SSS} %p --- [%15.15x{name}]%] %40.40f{3}  : %m',
  tokens: {
    name: (logEvent) => {
      return (logEvent.context && logEvent.context['name']) || '-';
    }
  }
};

export const LOG4JS_NO_COLOUR_DEFAULT_LAYOUT = {
  type: 'pattern',
  // log4js default pattern %d{yyyy-MM-dd HH:mm:ss:SSS} [%thread] %-5level %logger{36} - %msg%n
  // we use process id instead thread id
  pattern: '%d{yyyy-MM-dd hh:mm:ss:SSS} %p --- [%15.15x{name}] %40.40f{3}  : %m',
  tokens: {
    name: (logEvent) => {
      return (logEvent.context && logEvent.context['name']) || '-';
    }
  }
};



export const LOG4JS_DEFAULT_CONFIG: Configuration = {
  appenders: {
    stdout: {
      type: 'stdout',
      layout: LOG4JS_DEFAULT_LAYOUT
    },
    file: {
      type: 'file',
      filename: './logs/application.log',
      maxLogSize: 20 * 1024 * 1024, // maxLogSize use bytes ad unit
      backups: 10,     // default use 5 so 1KB file size total rotating
      layout: LOG4JS_NO_COLOUR_DEFAULT_LAYOUT
    }
  },
  categories: {
    default: {
      enableCallStack: true,
      appenders: ['stdout', 'file'],
      level: 'debug'
    }
  }
};

It will use default below layouts (from spring boot default log pattern without process id)

You can refer to SpringBoot logging features


2020-11-14 15:47:24:486 INFO --- [         NestJS]               core/src/log4js.classes.ts  : log using nestjs as category
2020-11-14 15:47:24:486 INFO --- [              -]               core/src/log4js.classes.ts  : log using none as category
2020-11-14 15:47:24:490 INFO --- [         NestJS]               core/src/log4js.classes.ts  : log using nestjs as category
2020-11-14 15:47:24:490 WARN --- [              -]      src/__tests__/log4js.module.test.ts  : log using none as category

Tips: You are using grok pattern via filebeat/other sidecar log agent? You can use below grok pattern: %{TIMESTAMP_ISO8601:server_time}\s*%{LOGLEVEL:level}\s*---\s[\s%{NOTSPACE:context}]\s*%{NOTSPACE:file_path}\s*:\s*%{GREEDYDATA:content}

It will split to friendly format (you can update alias yourself via grok document)

  • server_time
  • level
  • context
  • file_path
  • content

Download details:

Author: nest-x
Source code: https://github.com/nest-x/nestx-log4js 
License: MIT license

#nest #nestjs #node #typescript

Nestx Log4js: Provide Log4js Module As NestJS Module
Laura  Fox

Laura Fox

1661626860

Twilio notification channel for NestJS Application

This package makes it easy to send Twilio notifications with NestJS.

This package is a sub-module of NestJS notification.

Installation

::: code

$ npm i @nestjs-notification-channels/twilio
$ yarn add @nestjs-notification-channels/twilio

:::

Configuration

Add your Twilio Account SID, Auth Token, and Sender (optional) to your .env:

TWILIO_ACCOUNT_SID=
TWILIO_AUTH_TOKEN=
TWILIO_SENDER=

Usage

Module declaration

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import {
  NestjsNotificationModule,
  NestjsNotificationModuleOptions,
} from '@sinuos/nestjs-notification';

@Module({
  imports: [
    NestjsNotificationModule.register(<NestjsNotificationModuleOptions>{}),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

You can also declare the account sid, auth token and sender by declaring them like this.

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import {
  NestjsNotificationModule,
  NestjsNotificationModuleOptions,
} from '@sinuos/nestjs-notification';
import { TwilioChannelModule } from '@nestjs-notification-channels/twilio';

@Module({
  imports: [
    NestjsNotificationModule.register(<NestjsNotificationModuleOptions>{}),
    TwilioChannelModule.register({
      twilioAccountSid: 'xxx',
      twilioAuthToken: 'xxx',
      twilioSender: 'xxx', // SENDER OR +212xxxxxx,
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Now you can use the channel in your sendToChannels() method inside the notification:

import { NestJsNotification } from '@sinuos/nestjs-notification';
import {
  TwilioChannel,
  TwilioChannelMessage,
  ITwilioChannel,
} from '@nestjs-notification-channels/twilio';

export class OrderedNotification implements NestJsNotification {
  public sendToChannels() {
    return [TwilioChannel];
  }

  toTwilio() {
    return new TwilioChannelMessage()
      .setToFrom('+212xxxxxxxx')
      .setMessage('Your order XXXXX is placed');
  }
}

Available Message methods

TwilioMessage

  • accountSid('') - Optional if account sid is defined in your env.
  • authToken('') - Optional if auth token is defined in your env.
  • sender('') - Optional if sender is defined in your env.
  • message('') - SMS content.
  • toPhoneNumber('') - Your recipient.

Changelog

Please see CHANGELOG for more information what has changed recently.

Testing

::: code

$ npm run test
$ yarn test

:::

Security

If you discover any security related issues, please email gregoriohc@gmail.com instead of using the issue tracker.

Contributing

Please see CONTRIBUTING for details.

Download details:

Author: nestjs-notification-channels
Source code: https://github.com/nestjs-notification-channels/twilio 
License: MIT license

#nest #nestjs #node #typescript

Twilio notification channel for NestJS Application
Laura  Fox

Laura Fox

1661622900

Doubleshot: A Split Node Backend and Electron Main Process

Doubleshot

A split node backend and electron main process

Description

Doubleshot is a collection of tools for integrating the nodejs backend framework with electron to build a desktop application. At some point in the future, you can simply and quickly split it up and convert it to a web application.

A software engineer's time is precious, and I hope that one set of code can be applied to two different applications with minor modifications. So we can have more time to enjoy a cup of coffee. πŸ˜€

Features

  • πŸƒβ€ A Runner to start and build both the frontend (renderer) and backend (main).
  • πŸ”¨ A Builder to run/build electron main process or node backend.
  • πŸ›» An electron ipc transport for nestjs that provides simple ipc communication.
  • πŸͺŸ An electron module for nestjs to launch electron windows.
  • 😎 Very easy integration of electron and node.
  • ⚑ Quickly split and convert between C/S and B/S.
  • ⏩ Quick start and build, powered by tsup and electron-builder

Warning: this project is in early stage, do not use in production environment

Download details:

Author: Doubleshotjs
Source code: https://github.com/Doubleshotjs/doubleshot 
License: MIT license

#nest #nestjs #node

Doubleshot: A Split Node Backend and Electron Main Process
Laura  Fox

Laura Fox

1661615340

Nestjs Zeebe: Zeebe Transport and Client for Nestjs Framework

NestJS Zeebe Connector (Transport and Client) - Up-to-date

A zeebe transport and client for NestJS 7.x

Using the zeebe-node module and exposing it as a NestJS transport and module.

Use version 2.x.x and above for Zeebe 1.x.x and above

Install

npm install nestjs-zeebe

Basic usage

    // app.module.ts
    import { Module } from '@nestjs/common';
    import { AppController } from './app.controller';
    import { ZeebeModule, ZeebeServer } from 'nestjs-zeebe';

    @Module({
    imports: [ ZeebeModule.forRoot({ gatewayAddress: 'localhost:26500' })],
    controllers: [AppController],
    providers: [ZeebeServer],
    })
    export class AppModule {}
    // main.ts
    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';
    import { ZeebeServer } from 'nestjs-zeebe';

    async function bootstrap() {
    const app = await NestFactory.create(AppModule);
    const microservice = app.connectMicroservice({
        strategy: app.get(ZeebeServer),
    });

    await app.startAllMicroservicesAsync();

    await app.listen(3000);
    }
    bootstrap();
    // app.controller.ts
    import { Controller, Get, Inject } from '@nestjs/common';
    import { AppService } from './app.service';
    import { ZBClient } from 'zeebe-node';
    import { ZEEBE_CONNECTION_PROVIDER, ZeebeWorker, ZeebeJob } from 'nestjs-zeebe';
    import {
    Ctx,
    Payload,
    } from '@nestjs/microservices';
    import { ZBClient, ZBWorker, ICustomHeaders, IInputVariables, IOutputVariables, CompleteFn } from 'zeebe-node';
    import { CreateProcessInstanceResponse } from 'zeebe-node/dist/lib/interfaces-grpc-1.0';

    @Controller()
    export class AppController {
        constructor(private readonly appService: AppService, @Inject(ZEEBE_CONNECTION_PROVIDER) private readonly zbClient: ZBClient) {}

        // Use the client to create a new workflow instance
        @Get()
        getHello() : Promise<CreateProcessInstanceResponse> {
            return this.zbClient.createProcessInstance('order-process', { test: 1, or: 'romano'});
        }

        // Subscribe to events of type 'payment-service
        @ZeebeWorker('payment-service')
        paymentService(@Payload() job: ZeebeJob, @Ctx() context: { complete: CompleteFn<IOutputVariables>, worker: ZBWorker<IInputVariables, ICustomHeaders, IOutputVariables> }) {
            console.log('Payment-service, Task variables', job.variables);
            let updatedVariables = Object.assign({}, job.variables, {
            paymentService: 'Did my job',
            });

            // Task worker business logic goes here

            job.complete(updatedVariables);
        }

        // Subscribe to events of type 'inventory-service and create a worker with the options as passed below (zeebe-node ZBWorkerOptions)
        @ZeebeWorker('inventory-service', { maxJobsToActivate: 10, timeout: 300 })
        inventoryService(@Payload() job: ZeebeJob, @Ctx() context: { complete: CompleteFn<IOutputVariables>, worker: ZBWorker<IInputVariables, ICustomHeaders, IOutputVariables> }) {
            console.log('inventory-service, Task variables', job.variables);
            let updatedVariables = Object.assign({}, job.variables, {
            inventoryVar: 'Inventory donnnneee',
            });

            // Task worker business logic goes here
            //job.complete(updatedVariables);
            job.complete(updatedVariables);
        }
    }

For v1.x.x

    // app.controller.ts
    import { Controller, Get, Inject } from '@nestjs/common';
    import { AppService } from './app.service';
    import { ZBClient } from 'zeebe-node';
    import { CreateWorkflowInstanceResponse, CompleteFn, Job } from 'zeebe-node/interfaces';
    import { ZEEBE_CONNECTION_PROVIDER, ZeebeWorker } from 'nestjs-zeebe';
    import {
        Ctx,
        Payload,
    } from '@nestjs/microservices';

    @Controller()
    export class AppController {
        constructor(private readonly appService: AppService, @Inject(ZEEBE_CONNECTION_PROVIDER) private readonly zbClient: ZBClient) {}

        // Use the client to create a new workflow instance
        @Get()
        getHello() : Promise<CreateWorkflowInstanceResponse> {
            return this.zbClient.createWorkflowInstance('order-process', { test: 1, or: 'romano'});
        }

        // Subscribe to events of type 'payment-service
        @ZeebeWorker('payment-service')
        paymentService(@Payload() job, @Ctx() fn: CompleteFn<any> {
            console.log('Payment-service, Task variables', job.variables);
            let updatedVariables = Object.assign({}, job.variables, {
            paymentService: 'Did my job',
            });

            // Task worker business logic goes here

            complete.success(updatedVariables);
        }

        // Subscribe to events of type 'inventory-service and create a worker with the options as passed below (zeebe-node ZBWorkerOptions)
        @ZeebeWorker('inventory-service', { maxJobsToActivate: 10, timeout: 300 })
        inventoryService(@Payload() job, @Ctx() fn: CompleteFn<any>) {
            console.log('inventory-service, Task variables', job.variables);
            let updatedVariables = Object.assign({}, job.variables, {
            inventoryVar: 'Inventory donnnneee',
            });

            // Task worker business logic goes here

            complete.success(updatedVariables);
        }
    }


Download details:

Author: camunda-community-hub
Source code: https://github.com/camunda-community-hub/nestjs-zeebe 
License: MIT license

#nest #nestjs #node #typescript

Nestjs Zeebe: Zeebe Transport and Client for Nestjs Framework
Laura  Fox

Laura Fox

1661607902

Adminjs Nestjs: NestJS Module to Import Admin The Nest Way

NestJS plugin for AdminJS

This is an official AdminJS plugin which integrates it to nestjs framework.

Version >3.0.0 might not be compatible with Nestjs 7 or lower. If adapter is not working, try downgrading adapter to version 2..

AdminJS

AdminJS is an automatic admin interface which can be plugged into your application. You, as a developer, provide database models (like posts, comments, stores, products or whatever else your application uses), and AdminJS generates UI which allows you (or other trusted users) to manage content.

Check out the example application with mongo and postgres models here: https://admin-bro-example-app-staging.herokuapp.com/admin/login

Or visit AdminJS github page.

Usage

To see example usage see the example-app or visit the Nestjs section under AdminJS project page

About SoftwareBrothers.co

We’re an open, friendly team that helps clients from all over the world to transform their businesses and create astonishing products.

  • We are available for hire.
  • If you want to work for us - checkout the career page.

Download details:

Author: SoftwareBrothers
Source code: https://github.com/SoftwareBrothers/adminjs-nestjs 
License: MIT license

#nest #nestjs #node

Adminjs Nestjs: NestJS Module to Import Admin The Nest Way
Laura  Fox

Laura Fox

1661600468

Nestjs-stripe: Injectable Stripe Client for Your Nestjs Projects

nestjs-stripe

Injectable Stripe client for your nestjs projects

About

nestjs-stripe implements a module, StripeModule, which when imported into your nestjs project provides a Stripe client to any class that injects it. This lets Stripe be worked into your dependency injection workflow without having to do any extra work outside of the initial setup.

Installation

npm install --save nestjs-stripe

Getting Started

The simplest way to use nestjs-stripe is to use StripeModule.forRoot

import { Module } from '@nestjs-common';
import { StripeModule } from 'nestjs-stripe';

@Module({
  imports: [
    StripeModule.forRoot({
      apiKey: 'my_secret_key',
      apiVersion: '2020-08-27',
    }),
  ],
})
export class AppModule {}

You can then inject the Stripe client into any of your injectables by using a custom decorator

import { Injectable } from '@nestjs/common';
import { InjectStripe } from 'nestjs-stripe';
import Stripe from 'stripe';

@Injectable()
export class AppService {
  public constructor(@InjectStripe() private readonly stripeClient: Stripe) {}
}

Asynchronous setup is also supported

import { Module } from '@nestjs-common';
import { StripeModule } from 'nestjs-stripe';

@Module({
  imports: [
    StripeModule.forRootAsync({
      inject: [ConfigService],
      useFactory: (configService: ConfigService) => ({
        apiKey: configService.get('stripe_key'),
        apiVersion: '2020-08-27',
      }),
    }),
  ],
})
export class AppModule {}

Read up on the stripe-node caveats here. Due to the way stripe-node works you can only use the latest version of the Stripe API that was published at the time the module version was published. If you wish to use an older version of the Stripe API, follow the steps in the above link. Because of this, the apiVersion field is now required along with the apiKey field.

Example

In order to run the example run the following commands in your terminal. The expected output of the example is to show that the Stripe client was successfully injected into the AppService.

cd example
yarn install
yarn start

Contributing

I would greatly appreciate any contributions to make this project better. Please make sure to follow the below guidelines before getting your hands dirty.

  1. Fork the repository
  2. Create your branch (git checkout -b my-branch)
  3. Commit any changes to your branch
  4. Push your changes to your remote branch
  5. Open a pull request

Download details:

Author: dhaspden
Source code: https://github.com/dhaspden/nestjs-stripe 
License: MIT license

#nest #nestjs #node #stripe

Nestjs-stripe: Injectable Stripe Client for Your Nestjs Projects
Laura  Fox

Laura Fox

1661593080

Nestjs Braintree: A Module for Webhooks and Transactions

Nestjs Braintree

A module for Braintree reoccurring payments and transactions built for the Nestjs framework.

Using the Braintree node SDK.

NOTE! Currently building

Install

$ yarn add nestjs-braintree

Use

Basic use

import { Module } from '@nestjs/common';
import { BraintreeModule } from 'nestjs-braintree';
import * as braintree from 'braintree';

@Module({
  imports: [
    BraintreeModule.forRoot({
      environment: braintree.Environment.Sandbox,
      merchantId: '',
      publicKey: '',
      privateKey: '',
    }),
  ],
})
export default class AppModule {}

In a subModule

import { Module } from '@nestjs/common';
import { BraintreeModule } from 'nestjs-braintree';

@Module({
  imports: [
    BraintreeModule.forFeature(),
  ],
})
export default class SubModule {}

Use with nestjs-config

import { Module } from '@nestjs/common';
import { BraintreeModule } from 'nestjs-braintree';
import { ConfigModule, ConfigService } from 'nestjs-config';

@Module({
  imports: [
    ConfigModule.load('root/to/config/*/**.{ts,js}'),
    BraintreeModule.forRootAsync({
      useFactory: async (config: ConfigService) => config.get('braintree'),
      inject: [ConfigService],
    }),
  ],
})
export default class AppModule {}

//config/braintree.ts
import * as braintree from 'braintree';

export default {
  environment:
    process.env.NODE_ENV == 'development'
      ? braintree.Environment.Sandbox
      : braintree.Environment.Live,
  merchantId: process.env.BRAINTREE_MERCHANT_ID,
  publicKey: process.env.BRAINTREE_PUBLIC_KEY,
  privateKey: process.env.BRAINTREE_PRIVATE_KEY,
};

Transactions

Braintree is capable of making one off transactions

import { Module } from '@nestjs/common';
import { BraintreeModule, InjectBraintreeProvider } from 'nestjs-braintree';
import { ConfigModule, ConfigService } from 'nestjs-config';

class TransactionProvider {
  constructor(
    @InjectBraintreeProvider()
    private readonly braintreeProvider: BraintreeProvider,
  ) {}

  takePayment(amount: string, nonce: string) {
    this.braintreeProvider.sale({
      payment_method_nonce: nonce,
      amount,
    });
  }
}

@Module({
  imports: [
    ConfigModule.load('root/to/config/*/**.{ts,js}'),
    BraintreeModule.forRoot({
      useFactory: async (config: ConfigService) => config.get('braintree'),
      inject: [ConfigService],
    }),
  ],
  providers: [TransactionProvider],
})
export default class AppModule {}

Available methods relating to transactions are

Sale

braintreeProvider.sale(transaction: BraintreeTransactionInterface): Promise<BraintreeTransactionResultInterface>

Refund

braintreeProvider.refund(transactionId: string, amount?: string, orderId?: string): Promise<BraintreeTransactionResultInterface>

Find

braintreeProvider.find(transactionId: string): Promise<BraintreeTransactionResultInterface>

The braintree SDK does offer additional methods. I will implement them soon hopefully

Webhooks

When using subscriptions with braintree, braintree will issue webhooks to your endpoint which you can use the decorators to handle those actions.

import { Module } from '@nestjs/common';
import {
  BraintreeModule,
  BraintreeWebhookModule,
  BraintreeSubscriptionCanceled,
  BraintreeSubscriptionExpired,
  BraintreeWebhookHandler,
} from 'nestjs-braintree';
import { ConfigModule, ConfigService } from 'nestjs-config';

@BraintreeWebhookHandler()
class SubscriptionProvider {
  @BraintreeSubscriptionCanceled()
  canceled() {
    console.log('subscription canceled');
  }

  @BraintreeSubscriptionExpired()
  expired() {
    console.log('subscription expired');
  }
}

@Module({
  imports: [
    ConfigModule.load('root/to/config/*/**.{ts,js}'),
    BraintreeModule.forRootAsync({
      useFactory: async (config: ConfigService) => config.get('braintree'),
      inject: [ConfigService],
    }),
    BraintreeWebhookModule,
  ],
  providers: [SubscriptionProvider],
})
export default class AppModule {}

Use Example

The idea of the Braintree Webhook Module is to make implementation of actions a lot easier. For example we can build a provider like this one to cancel canceled subscriptions.

@BraintreeWebhookHandler()
export class SubscriptionProvider {
  constructor(@InjectRepository(Subscription) private readonly subscriptionRepository: Repository<Subscription>) {}

  async findByBraintreeId(braintreeId: string): Promise<Subscription|null> {
    return await this.subscriptionRepository.find({
      where: {
        braintreeId,
      },
    });
  }

  async update(subscription: Subscription): Promise<boolean> {
    return await this.subscriptionRepository.update(subscription);
  }

  @BraintreeSubscriptionCanceled()
  async canceled(webhook: BraintreeWebhook) {
    const subscription = await this.findByBraintreeId(webhook.subscription.id);
    if (!subscription) {
      return;
    }
    subscription.active = false;
    await this.update(subscription);
  }
}

Available webhooks

ShortnameBraintree webhook name/const/keyNestJS decorator
Subscription Canceledsubscription_canceled@BraintreeSubscriptionCanceled()
Subscription Expiredsubscription_expired@BraintreeSubscriptionExpired()
Subscription Charged Successfullysubscription_charged_successfully@BraintreeSubscriptionChargedSuccessfully()
Subscription Charged Unsuccessfullysubscription_charged_unsuccessfully@BraintreeSubscriptionChargedUnsuccessfully()
Subscription Went Activesubscription_went_active@BraintreeSubscriptionWentActive()
Subscription Went Past Duesubscription_went_past_due@BraintreeSubscriptionWentPastDue()
Subscription Trial Endedsubscription_trial_ended@BraintreeSubscriptionTrialEnded()

You can find out more about the webhooks here.

Custom routing for webhooks

You may want to divert from the default routing of {your_domain}/braintree/webhook for whatever reason. You can do so using the forRoot method on the BraintreeWebhookModule like so

@Module({
  imports: [
    ConfigModule.load('root/to/config/*/**.{ts,js}'),
    BraintreeModule.forRootAsync({
      useFactory: async (config: ConfigService) => config.get('braintree'),
      inject: [ConfigService],
    }),
    BraintreeWebhookModule.forRoot({
      root: 'replace-braintree',
      handle: 'replace-webhook',
    }),
  ],
  providers: [SubscriptionProvider],
})
export default class AppModule {}

The above will result in your route for your braintree webhooks being {your_domain}/replace-braintree/replace-webhook

Download details:

Author: nestjsx
Source code: https://github.com/nestjsx/nestjs-braintree 
License: MIT license

#nest #nestjs #node

Nestjs Braintree: A Module for Webhooks and Transactions
Laura  Fox

Laura Fox

1661585642

Nestjs Typeorm Paginate: A Simple Function & Interfaces for Pagination

Nestjs Typeorm paginate

Pagination helper method for TypeORM repositories or queryBuilders with strict typings

Install

$ yarn add nestjs-typeorm-paginate

or

$ npm i nestjs-typeorm-paginate

If you're using typeorm^0.2.6 please use nestjs-typeorm-paginate^3.2.0 For typeorm^0.3.0 please use nestjs-typeorm-paginate^4.0.0

Usage

Service

Repository

import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { CatEntity } from './entities';
import {
  paginate,
  Pagination,
  IPaginationOptions,
} from 'nestjs-typeorm-paginate';

@Injectable()
export class CatService {
  constructor(
    @InjectRepository(CatEntity)
    private readonly repository: Repository<CatEntity>,
  ) {}

  async paginate(options: IPaginationOptions): Promise<Pagination<CatEntity>> {
    return paginate<CatEntity>(this.repository, options);
  }
}

QueryBuilder

import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { CatEntity } from './entities';
import {
  paginate,
  Pagination,
  IPaginationOptions,
} from 'nestjs-typeorm-paginate';

@Injectable()
export class CatService {
  constructor(
    @InjectRepository(CatEntity)
    private readonly repository: Repository<CatEntity>,
  ) {}

  async paginate(options: IPaginationOptions): Promise<Pagination<CatEntity>> {
    const queryBuilder = this.repository.createQueryBuilder('c');
    queryBuilder.orderBy('c.name', 'DESC'); // Or whatever you need to do

    return paginate<CatEntity>(queryBuilder, options);
  }
}

Controller

import { Controller, DefaultValuePipe, Get, ParseIntPipe, Query } from '@nestjs/common';
import { CatService } from './cat.service';
import { CatEntity } from './cat.entity';
import { Pagination } from 'nestjs-typeorm-paginate';

@Controller('cats')
export class CatsController {
  constructor(private readonly catService: CatService) {}
  @Get('')
  async index(
    @Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number = 1,
    @Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number = 10,
  ): Promise<Pagination<CatEntity>> {
    limit = limit > 100 ? 100 : limit;
    return this.catService.paginate({
      page,
      limit,
      route: 'http://cats.com/cats',
    });
  }
}

If you use ParseIntPipe on the query params (as in the example), don't forget to also add DefaultValuePipe. See issue 517 for more info.

the route property of the paginate options can also be the short version of an absolute path , In this case, it would be /cats instead of http://cats.com/cats

Example Response

{
  "items": [
    {
      "lives": 9,
      "type": "tabby",
      "name": "Bobby"
    },
    {
      "lives": 2,
      "type": "Ginger",
      "name": "Garfield"
    },
    {
      "lives": 6,
      "type": "Black",
      "name": "Witch's mate"
    },
    {
      "lives": 7,
      "type": "Purssian Grey",
      "name": "Alisdaya"
    },
    {
      "lives": 1,
      "type": "Alistair",
      "name": "ali"
    },
    ...
  ],
  "meta": {
    "itemCount": 10,
    "totalItems": 20,
    "itemsPerPage": 10,
    "totalPages": 5,
    "currentPage": 2
  },
  "links" : {
    "first": "http://cats.com/cats?limit=10",
    "previous": "http://cats.com/cats?page=1&limit=10",
    "next": "http://cats.com/cats?page=3&limit=10",
    "last": "http://cats.com/cats?page=5&limit=10"
  }
}

items: An array of SomeEntity

meta.itemCount: The length of items array (i.e., the amount of items on this page) meta.totalItems: The total amount of SomeEntity matching the filter conditions meta.itemsPerPage: The requested items per page (i.e., the limit parameter)

meta.totalPages: The total amount of pages (based on the limit) meta.currentPage: The current page this paginator "points" to

links.first: A URL for the first page to call | "" (blank) if no route is defined links.previous: A URL for the previous page to call | "" (blank) if no previous to call links.next: A URL for the next page to call | "" (blank) if no page to call links.last: A URL for the last page to call | "" (blank) if no route is defined

Do note that links.first may not have the 'page' query param defined

Find Parameters

@Injectable()
export class CatService {
  constructor(
    @InjectRepository(CatEntity)
    private readonly repository: Repository<CatEntity>,
  ) {}

  async paginate(options: IPaginationOptions): Promise<Pagination<CatEntity>> {
    return paginate<CatEntity>(this.repository, options, {
      lives: 9,
    });
  }
}

Eager loading

Eager loading should work with typeorm's eager property out the box. Like so

import { Entity, OneToMany } from 'typeorm';

@Entity()
export class CatEntity {
  @OneToMany(t => TigerKingEntity, tigerKing.cats, {
    eager: true,
  })
  tigerKings: TigerKingEntity[];
}

// service
class CatService {
  constructor(private readonly repository: Repository<CatEntity>) {}

  async paginate(page: number, limit: number): Promise<Pagination<CatEntity>> {
    return paginate(this.repository, { page, limit });
  }
}

QueryBuilder

However, when using the query builder you'll have to hydrate the entities yourself. Here is a crude example that I've used in the past. It's not great but this is partially what typeORM will do.

const results = paginate(queryBuilder, { page, limit });

return new Pagination(
  await Promise.all(
    results.items.map(async (item: SomeEntity) => {
      const hydrate = await this.someRepository.findByEntity(item);
      item.hydrated = hydrate;

      return item;
    }),
  ),
  results.meta,
  results.links,
);

Raw queries

const queryBuilder = this.repository
  .createQueryBuilder<{ type: string; totalLives: string }>('c')
  .select('c.type', 'type')
  .addSelect('SUM(c.lives)', 'totalLives')
  .groupBy('c.type')
  .orderBy('c.type', 'DESC'); // Or whatever you need to do

return paginateRaw(queryBuilder, options);

Raw and Entities

A similar approach is used for TypeORM's getRawAndEntities

Let's assume there's a joined table that matches each cat with its cat toys. And we want to bring how many toys each cat has.


const queryBuilder = this.repository
  .createQueryBuilder<{ type: string; totalLives: string }>('cat')
    .leftJoinAndSelect('cat.toys', 'toys')
    .addSelect('COUNT(toys)::INTEGER', 'toyCount')
    .groupBy('cat.name');

This will allow us to get the paginated cats information with the additional raw query to build our actual response value. The return pagination object will be the same, but you're now able to handle or map the results and the raw objects as needed.

const [pagination, rawResults] = await paginateRawAndEntities(query, options);
pagination.items.map((item, index) => {
  // we can do what we need with the items and raw results here
  // change your items using rawResults.find(raw => raw.id === item.id)
});
return pagination;

Note about joined tables and raw values

Since the values of the raw results will include all the joined table items as queried, you must make sure to handle the items as needed for your use case. Refer to TypeORM's getRawAndEntities implementation as needed.

The rawResults array will look something like this:

[
    { // Bobby appears 3 times due to the joined query
      "cat_lives": 9,
      "cat_type": "tabby",
      "cat_name": "Bobby",
      "toyCount": 3
    },
    {
      "cat_lives": 9,
      "cat_type": "tabby",
      "cat_name": "Bobby",
      "toyCount": 3
    },
    {
      "cat_lives": 9,
      "cat_type": "tabby",
      "cat_name": "Bobby",
      "toyCount": 3
    },
    {
      "cat_lives": 2,
      "cat_type": "Ginger",
      "cat_name": "Garfield",
      "toyCount": 1
    },
    ...
]

Custom meta data transformer

If you wanted to alter the meta data that is returned from the pagination object. Then use the metaTransformer in the options like so


class CustomPaginationMeta {
  constructor(
    public readonly count: number,
    public readonly total: number,
  ) {}
}

return paginate<MyEntity, CustomPaginationMeta>(this.repository, { 
  page,
  limit,
  metaTransformer: (meta: IPaginationMeta): CustomPaginationMeta => new CustomPaginationMeta(
    meta.itemCount,
    meta.totalItems,
  ),
 });

This will result in the above returning CustomPaginationMeta in the meta property instead of the default IPaginationMeta.

Custom links query params labels

If you want to alter the limit and/or page labels in meta links, then use routingLabels in the options like so


return paginate<MyEntity>(this.repository, { 
  page,
  limit,
  routingLabels: {
    limitLabel: 'page-size', // default: limit
    pageLabel: 'current-page', //default: page
  }
 });

This will result links like http://example.com/something?current-page=1&page-size=3.

Download details:

Author: nestjsx
Source code: https://github.com/nestjsx/nestjs-typeorm-paginate 
License: MIT license

#nest #nestjs #node #typeorm

Nestjs Typeorm Paginate: A Simple Function & Interfaces for Pagination
Laura  Fox

Laura Fox

1661578080

Nestjs Hasura: NestJS integrations for Working with Hasura

@golevelup/nestjs-hasura

Leverage NestJS to make incorporating business logic and event processing easier with Hasura

Features

πŸŽ‰ Exposes an API endpoint from your NestJS application at to be used for event processing from Hasura. Defaults to /hasura/events/ but can be easily configured

πŸ”’ Automatically validates that the event payload was actually sent from Hasura using configurable secrets

πŸ•΅οΈ Discovers methods from your application and automatically turns them into Hasura event handlers. Supports insert, update and delete events from your tables as well as scheduled events based on a CRON schedule

🧭 Routes incoming webhook payloads to the correct event handler based on configuration so you can maintain a single webhook endpoint for Hasura

πŸ”Œ Optionally supports automatic management of your Hasura metadata files which means that your application code can be the source of truth for configuration of events. This reduces a ton of boilerplate and developer overhead

Usage

Install

npm install ---save @golevelup/nestjs-hasura

or

yarn add @golevelup/nestjs-hasura

Import

Import and add HasuraModule to the imports section of the consuming module (most likely AppModule). In order to ensure that your Hasura events webhook endpoint is secure, the module requires configuration for an HTTP header name and value that will be used to verify that the event actually came from Hasura.

Configuration

The Hasura Module supports both the forRoot and forRootAsync patterns for configuration, so you can easily retrieve the necessary config values from a ConfigService or other provider.

Usage

Integrating with your NestJS app

The HasuraModule makes it easy to reuse the same events API endpoint for all events that you create in Hasura. The internal routing mechanism on the NestJS side ensures that the all events coming in through the endpoint will be sent to the correct handler. The endpoint provided defaults to /hasura/events. This can be overriden with the module by specifying an alternative controllerPrefix so for example you could set this to webhooks and the resulting endpoint would be available at /webhooks/events.

Automatically Synchronize Hasura Metadata

One of the more powerful features of this Module is the ability to automatically generate the necessary Hasura metadata for your event handlers instead of having to worry about configuring each handler individually. Under the hood, this uses the @hasura/metadata to generate and merge changes to your tables.yaml and cron_triggers.yaml files.

If you decide to opt into this functionality, you should include the optional managedMetaDataConfig object when importing the HasuraModule into your application.

import { HasuraModule } from '@golevelup/nestjs-hasura';

@Module({
  imports: [
    HasuraModule.forRoot(HasuraModule, {
      webhookConfig: {
        secretFactory: secret,
        secretHeader: secretHeader,
      },
      managedMetaDataConfig: {
        dirPath: join(process.cwd(), 'hasura/metadata'),
        secretHeaderEnvName: 'HASURA_NESTJS_WEBHOOK_SECRET_HEADER_VALUE',
        nestEndpointEnvName: 'NESTJS_EVENT_WEBHOOK_ENDPOINT',
        defaultEventRetryConfig: {
          intervalInSeconds: 15,
          numRetries: 3,
          timeoutInSeconds: 100,
          toleranceSeconds: 21600,
        },
      },
    }),
  ],
})
export class AppModule {
  // ...
}

It is recommended that you conditionally add this configuration based on the Node Environment as this should only be used in development environments to track the necessary changes to your metadata yaml files so that they can be tracked in source control.

After generating changes to these files you should make sure they are applied against your Hasura instance using the CLI command:

hasura metadata apply

Opting Out

If you decide to opt out of automatic metadata synchronization it is up to you to ensure that the secret header name and values match. When creating the event in the Hasura console, you should set these values such that they match the configuration provided to the HasuraModule configuration in your NestJS application. This ensures that only Hasura can trigger events in your system.

Registering Table Event Handlers

Decorate methods in your NestJS providers in order to have them be automatically attached as event handlers for incoming Hasura events. The event payload will be analyzed and routed to your provider methods based on the configuration provided in the decorator.

import {
  TrackedHasuraEventHandler,
  HasuraUpdateEvent,
  HasuraInsertEvent,
} from '@golevelup/nestjs-hasura';

@Injectable()
class UsersService {
  @TrackedHasuraEventHandler({
    triggerName: 'user-created',
    tableName: 'user',
    definition: { type: 'insert' },
  })
  handleUserCreated(evt: HasuraInsertEvent<User>) {}

  @TrackedHasuraEventHandler({
    triggerName: 'user-updated',
    tableName: 'user',
    definition: { type: 'update', columns: ['avatarUrl'] },
  })
  handleUserUpdated(evt: HasuraUpdateEvent<User>) {}
}

Registering Scheduled Event Handlers

import { TrackedHasuraScheduledEventHandler } from '@golevelup/nestjs-hasura';

@Injectable()
class RecurringJobService {
  @TrackedHasuraScheduledEventHandler({
    cronSchedule: CommonCronSchedules.EveryMinute,
    name: 'every-minute',
    payload: {},
    comment: 'this is my comment',
  })
  public async cronTask(evt: any) {
    this.logger.log(evt);
  }
}

Retry Configuration

Retry configuration for both Table Event handlers as well as Scheduled Event handlers can be configured on the individual decorator or you can provide a default retry configuration at the module level that will be used for any event handler that does not explicitly provide its own retry settings.

Configuring Hasura Environment Variables

You should provide ENV variables to your Hasura instance that map the webhook endpoint and secret header values for communication to your NestJS application.

In the examples above, HASURA_NESTJS_WEBHOOK_SECRET_HEADER_VALUE and NESTJS_EVENT_WEBHOOK_ENDPOINT were used. The webhook endpoint should point to the automatically scaffolded events endpoint eg: https://my-nest-app.com/api/hasura/events

Usage with Interceptors, Guards and Filters

This library is built using an underlying NestJS concept called External Contexts which allows for methods to be included in the NestJS lifecycle. This means that Guards, Interceptors and Filters (collectively known as "enhancers") can be used in conjunction with Hasura event handlers. However, this can have unwanted/unintended consequences if you are using Global enhancers in your application as these will also apply to all Hasura event handlers. If you were previously expecting all contexts to be regular HTTP contexts, you may need to add conditional logic to prevent your enhancers from applying to Hasura event handlers.

You can identify Hasura event contexts by their context type, 'hasura_event':

@Injectable()
class ExampleInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler<any>) {
    const contextType = context.getType<'http' | 'hasura_event'>();

    // Do nothing if this is a Hasura event
    if (contextType === 'hasura_event') {
      return next.handle();
    }

    // Execute custom interceptor logic for HTTP request/response
    return next.handle();
  }
}

Related Hasura Documentation

Concepts

https://hasura.io/docs/1.0/graphql/manual/event-triggers/index.html#event-triggers

Tutorials

https://hasura.io/docs/1.0/graphql/manual/getting-started/first-event-trigger.html https://hasura.io/event-triggers

Contribute

Contributions welcome! Read the contribution guidelines first.

Link: https://github.com/golevelup/nestjs/tree/master/packages/hasura 

Link: https://github.com/golevelup/nestjs/tree/master/packages/hasura 

#nest #nestjs #node #graphql #hasura

Nestjs Hasura: NestJS integrations for Working with Hasura
Laura  Fox

Laura Fox

1661570352

Inject & Work with GraphQLClient Instances From Server Side NestJS

Description

This library provides the GraphQLRequestModule which makes it easy to initialize an instance of GraphQLClient for use in Depdendency Injection. This can be combined with automatic codegen for type safe access to third party GQL APIs from your Nest services

Usage

Install

npm install ---save @golevelup/nestjs-graphql-request

or

yarn add @golevelup/nestjs-graphql-request

Import

Import and add GraphQLRequestModule to the imports section of the consuming module.

import { GraphQLRequestModule } from '@golevelup/nestjs-graphql-request';

@Module({
  imports: [
    GraphQLRequestModule.forRoot(GraphQLRequestModule, {
      // Exposes configuration options based on the graphql-request package
      endpoint: config.get('endpoint'),
      options: {
        headers: {
          'content-type': 'application/json',
          'x-hasura-admin-secret': config.get('secret'),
        },
      },
    }),
  ],
})
export class AppModule {
  // ...
}

Inject the GraphQLClient

To make GraphQL requests from your controllers or services use the provided decorator @InjectGraphQLClient.

import { InjectGraphQLClient } from '@golevelup/nestjs-graphql-request';
import { GraphQLClient } from 'graphql-request';

@Injectable()
export class ExampleService {
  constructor(@InjectGraphQLClient() private readonly client: GraphQLClient) {}
}

Typesafe GraphQL Access

The GraphQL client works best when combined with GraphQL code generation tools for communicating to a GraphQL endpoint. Follow the GraphQL Code Generator GraphQL Request Plugin Instructions.

Running the code generator against a GraphQL Endpoint will produce typescript interfaces for all operations and types exposed by the API as well as a getSdk function which you can use in conjunction with GraphQL Request to get intellisense and type saftey for all requests you make. The getSdk function only requires a GraphQLClient instance (which this library provides). However because each generated SDK is unique per endpoint, it's up to you to wire up the provider for this in your application. This can be done using a Provider Factory with minimal code.

import {
  GraphQLRequestModule,
  GraphQLClientInject,
} from '@golevelup/nestjs-graphql-request';
import { getSdk } from './your-codegen-location';

@Module({
  imports: [
    GraphQLRequestModule.forRoot(GraphQLRequestModule, {
      // Exposes configuration options based on the graphql-request package
      endpoint: config.get('endpoint'),
      options: {
        headers: {
          'content-type': 'application/json',
          'x-hasura-admin-secret': config.get('secret'),
        },
      },
    }),
  ],
  providers: [
    {
      // you can provide whatever key you want. use it in conjunction with @Inject("TypeSafeGqlSdk") to get the SDK instance in your controllers/services
      provide: 'TypeSafeGqlSdk',
      inject: [GraphQLClientInject],
      useFactory: (client: GraphQLClient) => getSdk(client),
    },
  ],
})
export class AppModule {
  // ...
}

Contribute

Contributions welcome! Read the contribution guidelines first.

Link: https://github.com/golevelup/nestjs/tree/master/packages/graphql-request 

#nest #nestjs #node #javascript #graphql

Inject & Work with GraphQLClient Instances From Server Side NestJS
Laura  Fox

Laura Fox

1661562514

Firebase Authentication Strategy for NestJS Passport Integration

Installation

Install peer dependencies

npm install passport passport-jwt
npm install --save-dev @types/passport-jwt

Install @nestjs/passport for authentication

npm install @nestjs/passport

Install strategy

npm install @tfarras/nestjs-firebase-auth

Important

To work with Firebase Auth you need to configure and initialize your firebase app. For this purpose you can use my module for firebase-admin.

Create strategy

import { Injectable } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { ExtractJwt } from 'passport-jwt';
import { FirebaseAuthStrategy } from '@tfarras/nestjs-firebase-auth';

@Injectable()
export class FirebaseStrategy extends PassportStrategy(FirebaseAuthStrategy, 'firebase') {
  public constructor() {
    super({
      extractor: ExtractJwt.fromAuthHeaderAsBearerToken(),
    });
  }
}

Note: You should provide an extractor. More information about passport-jwt extractors you can find here: http://www.passportjs.org/packages/passport-jwt/#included-extractors

Create AuthModule and provide created strategy

import { Module } from "@nestjs/common";
import { PassportModule } from "@nestjs/passport";
import { FirebaseStrategy } from "./firebase.strategy";

@Module({
  imports: [PassportModule],
  providers: [FirebaseStrategy],
  exports: [FirebaseStrategy],
  controllers: [],
})
export class AuthModule { }

Import AuthModule into AppModule

import { FirebaseAdminCoreModule } from '@tfarras/nestjs-firebase-admin';
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from 'nestjs-config';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AuthModule } from './auth/auth.module';
import * as path from 'path';

@Module({
  imports: [
    ConfigModule.load(path.resolve(__dirname, 'config', '**', '!(*.d).{ts,js}')),
    FirebaseAdminCoreModule.forRootAsync({
      useFactory: (config: ConfigService) => config.get('firebase'),
      inject: [ConfigService],
    }),
    AuthModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule { }

Protect your routes

import { Controller, Get, Inject, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { FirebaseAdminSDK, FIREBASE_ADMIN_INJECT } from '@tfarras/nestjs-firebase-admin';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(
    private readonly appService: AppService,
    @Inject(FIREBASE_ADMIN_INJECT) private readonly fireSDK: FirebaseAdminSDK,
  ) { }

  @Get()
  @UseGuards(AuthGuard('firebase'))
  getHello() {
    return this.fireSDK.auth().listUsers();
  }
}

Custom second validation

In cases when you want to validate also if user exists in your database, or anything else after successfull Firebase validation you can define custom validate method in your strategy.

Example

import { Injectable } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { FirebaseAuthStrategy, FirebaseUser } from '@tfarras/nestjs-firebase-auth';
import { ExtractJwt } from 'passport-jwt';

@Injectable()
export class FirebaseStrategy extends PassportStrategy(FirebaseAuthStrategy, 'firebase') {
  public constructor() {
    super({
      extractor: ExtractJwt.fromAuthHeaderAsBearerToken(),
    });
  }

  async validate(payload: FirebaseUser): Promise<FirebaseUser> {
    // Do here whatever you want and return your user
    return payload;
  }
}

Download details:

Author: tfarras
Source code: https://github.com/tfarras/nestjs-firebase-auth 

#nest #nestjs #node #javascript

Firebase Authentication Strategy for NestJS Passport Integration
Laura  Fox

Laura Fox

1658452680

How to Add Prisma integration To A NestJS Application

Add Prisma support to your NestJS application.

Installation

Install the library by running its schematics:

nest add nestjs-prisma

or us your package manager to install the library directly:

# npm
npm install nestjs-prisma

# yarn
yarn add nestjs-prisma

Example output of the schematics:

βœ” Package installation in progress... β˜•
Starting library setup...
? Which datasource provider do you want to use for `prisma init`? postgresql
? Do you like to Dockerize your application? (Supports postgresql and mysql) Yes
    βœ…οΈ Added prisma@latest
    βœ…οΈ Added @prisma/client@latest
    βœ…οΈ Added Prisma scripts [6]
    βœ…οΈ Added Prisma Seed script
    βœ…οΈ Added Docker file
    βœ…οΈ Added Docker Compose and .env
    βœ…οΈ Add "prisma" directory to "excludes" in tsconfig.build.json
CREATE .dockerignore (42 bytes)
CREATE Dockerfile (455 bytes)
CREATE .env (642 bytes)
CREATE docker-compose.yml (497 bytes)
UPDATE package.json (2754 bytes)
UPDATE tsconfig.build.json (130 bytes)
βœ” Packages installed successfully.
βœ” Packages installed successfully.
    βœ…οΈ Initialized Prisma - Datasource postgresql

Use PrismaService and PrismaModule provided by nestjs-prisma

Add PrismaModule to the imports section in your AppModule or other modules to gain access to PrismaService.

import { Module } from '@nestjs/common';
import { PrismaModule } from 'nestjs-prisma';

@Module({
  imports: [PrismaModule.forRoot()],
})
export class AppModule {}

Shutdown Hook

Handle Prisma shutdown signal to shutdown your Nest application.

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { PrismaService } from 'nestjs-prisma';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // enable shutdown hook
  const prismaService: PrismaService = app.get(PrismaService);
  prismaService.enableShutdownHooks(app);

  await app.listen(3000);
}
bootstrap();

Prisma Middleware

Apply Prisma Middlewares with PrismaModule

import { Module } from '@nestjs/common';
import { PrismaModule } from 'nestjs-prisma';

@Module({
  imports: [
    PrismaModule.forRoot({
      prismaServiceOptions: {
        middlewares: [
          async (params, next) => {
            // Before query: change params
            const result = await next(params);
            // After query: result
            return result;
          },
        ], // see example loggingMiddleware below
      },
    }),
  ],
})
export class AppModule {}

Here is an example for using a Logging middleware.

Create your Prisma Middleware and export it as a function

// src/logging-middleware.ts
import { Prisma } from '@prisma/client';

export function loggingMiddleware(): Prisma.Middleware {
  return async (params, next) => {
    const before = Date.now();

    const result = await next(params);

    const after = Date.now();

    console.log(
      `Query ${params.model}.${params.action} took ${after - before}ms`,
    );

    return result;
  };
}

Now import your Middleware and add the function into the middlewares array.

import { Module } from '@nestjs/common';
import { PrismaModule } from 'nestjs-prisma';
import { loggingMiddleware } from './logging-middleware';

@Module({
  imports: [
    PrismaModule.forRoot({
      prismaServiceOptions: {
        middlewares: [loggingMiddleware()],
      },
    }),
  ],
})
export class AppModule {}

PrismaModule configuration

PrismaModule allows to be used globally and to pass options to the PrismaClient.

import { Module } from '@nestjs/common';
import { PrismaModule } from 'nestjs-prisma';

@Module({
  imports: [
    PrismaModule.forRoot({
      isGlobal: true,
      prismaServiceOptions: {
        prismaOptions: { log: ['info'] },
        explicitConnect: true,
      },
    }),
  ],
})
export class AppModule {}

Additionally, PrismaModule provides a forRootAsync to pass options asynchronously. One option is to use a factory function:

import { Module } from '@nestjs/common';
import { PrismaModule } from 'nestjs-prisma';

@Module({
  imports: [
    PrismaModule.forRootAsync({
      isGlobal: true,
      useFactory: () => ({
        prismaOptions: {
          log: ['info', 'query'],
        },
        explicitConnect: false,
      }),
    }),
  ],
})
export class AppModule {}

You can inject dependencies such as ConfigModule to load options from .env files.

import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { PrismaModule } from 'nestjs-prisma';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
    }),
    PrismaModule.forRootAsync({
      isGlobal: true,
      useFactory: async (configService: ConfigService) => {
        return {
          prismaOptions: {
            log: [configService.get('log')],
            datasources: {
              db: {
                url: configService.get('DATABASE_URL'),
              },
            },
          },
          explicitConnect: configService.get('explicit'),
        };
      },
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}

Alternatively, you can use a class instead of a factory:

import { Module } from '@nestjs/common';
import { PrismaModule } from 'nestjs-prisma';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
    }),
    PrismaModule.forRootAsync({
      isGlobal: true,
      useClass: PrismaConfigService,
    }),
  ],
})
export class AppModule {}

Create the PrismaConfigService and extend it with the PrismaOptionsFactory

import { Injectable } from '@nestjs/common';
import { PrismaOptionsFactory, PrismaServiceOptions } from 'nestjs-prisma';

@Injectable()
export class PrismaConfigService implements PrismaOptionsFactory {
  constructor() {
    // TODO inject any other service here like the `ConfigService`
  }

  createPrismaOptions(): PrismaServiceOptions | Promise<PrismaServiceOptions> {
    return {
      prismaOptions: {
        log: ['info', 'query'],
      },
      explicitConnect: true,
    };
  }
}

Generate custom PrismaService and PrismaModule

nest add nestjs-prisma --addPrismaService

Add the flag --addPrismaService if you like to generate your own PrismaService and PrismaModule for further customizations. Add PrismaModule to the imports section in your AppModule or other modules to gain access to PrismaService.

import { Module } from '@nestjs/common';
import { PrismaModule } from './prisma/prisma.module';

@Module({
  imports: [PrismaModule],
})
export class AppModule {}

Note: It is safe to remove nestjs-prisma as dependency otherwise you have two import suggestions for PrismaService and PrismaModule.

PrismaClientExceptionFilter

nestjs-prisma provides a PrismaClientExceptionFilter to catch unhandled PrismaClientKnownRequestError and returns different HttpStatus codes instead of 500 Internal server error.

To use the filter you have the following two options.

  1. Instantiate the filter in your main.ts and pass the HttpAdapterHost
//src/main.ts
import { ValidationPipe } from '@nestjs/common';
import { HttpAdapterHost, NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { PrismaClientExceptionFilter } from 'nestjs-prisma';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const { httpAdapter } = app.get(HttpAdapterHost);
  app.useGlobalFilters(new PrismaClientExceptionFilter(httpAdapter));

  await app.listen(3000);
}
bootstrap();

Optionally, provide your own error code mapping via the constructor:

//src/main.ts
import { ValidationPipe } from '@nestjs/common';
import { HttpAdapterHost, NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { PrismaClientExceptionFilter } from 'nestjs-prisma';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const { httpAdapter } = app.get(HttpAdapterHost);
  app.useGlobalFilters(
    new PrismaClientExceptionFilter(httpAdapter, {
      // Prisma Error Code: Http Status Response
      P2000: HttpStatus.BAD_REQUEST,
      P2002: HttpStatus.CONFLICT, 
      P2025: HttpStatus.NOT_FOUND,
    }),
  );

  await app.listen(3000);
}
bootstrap();

See the list of Prisma CLient Errors in the Prisma docs.

This will override the default error code mapping:

Error CodeHttp Status
P2000 - "The provided value for the column is too long for the column's type. Column: {column_name}"Bad Request - 400
P2002 - "Unique constraint failed on the {constraint}"Conflict - 409
P2025 - "An operation failed because it depends on one or more records that were required but not found. {cause}"Not Found - 404
  1. Use APP_FILTER token in any module
//src/app.module.ts
import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
import { PrismaClientExceptionFilter } from 'nestjs-prisma';

@Module({
  providers: [
    {
      provide: APP_FILTER,
      useClass: PrismaClientExceptionFilter,
    },
  ],
})
export class AppModule {}

Additional options

All available flags:

FlagDescriptionTypeDefault
datasourceProviderSpecifies the datasource provider for prisma init and docker.booleanPrompted
addDockerCreate a Dockerfile and docker-compose.yaml.booleanPrompted
addPrismaServiceCreate a Prisma service extending the Prisma Client and module.booleanfalse
dockerNodeImageVersionNode version for the builder and runner image.string14
nameThe name for the Prisma service extending the Prisma Client and module.stringPrisma
prismaVersionThe Prisma version to be installed.stringlatest
skipInstallSkip installing dependency packages.booleanfalse
skipPrismaInitSkip initializing Prisma.booleanfalse

You can pass additional flags to customize the schematic. For example, if you want to install a different version for Prisma use --prismaVersion flag:

nest add nestjs-prisma --prismaVersion 3.2.1

If you want to skip installing dependencies use --skipInstall flag:

nest add nestjs-prisma --skipInstall

Add Dockerfile and docker-compose.yaml, you can even use a different node version (14-alpine or 16).

Currently uses PostgreSQL as a default database in docker-compose.yaml.

nest add nestjs-prisma --addDocker --dockerNodeImageVersion 14-alpine

Developing

Install @angular-devkit/schematics-cli to be able to use schematics command

npm i -g @angular-devkit/schematics-cli

Now build the schematics and run the schematic.

npm run build:schematics
# or
npm run dev:schematics

# dry-run
schematics .:nest-add

# execute schematics
schematics .:nest-add --debug false
# or
schematics .:nest-add --dry-run false

Helpful

Helpful article about Custom Angular Schematics which also applies to Nest.

Download Details:
Author: notiz-dev
Source Code: https://github.com/notiz-dev/nestjs-prisma
License: MIT license

#nest #nestjs #node #javascript #prisma

How to Add Prisma integration To A NestJS Application
Laura  Fox

Laura Fox

1658444580

The Methods Of Testing NestJS including Unit Tests, Integration Tests

testing-nestjs

Project Structure

Each folder is a fully functional Nest application on its own. All you need to do after cloning the repository is move into the folder, install the dependencies, and test! Or you could just, you know, look at the test on GitHub too, no big deal.

Motivation

I've noticed a lot of people seem to get stuck on testing, and how to do it using the utilities given to us by Nest. Hopefully with a community driven repository of tests it will help others get a better understanding and feel for how to get tests running.

Please Note

This is not necessarily the Nest canonical way to test an application, nor is it the only way in any scenario. I will do my best to show different ways to run tests, but I will more than likely miss certain ways of running the tests, whether it is mocking in a specific way or otherwise. Again, this is a community maintained project and not a part of the official documentation. If you find other ways to test applications, great, I'd love to see them, but please do not think this is the only way.

Running the Project

  1. git clone https://github.com/jmcdo29/testing-nestjs.git
  2. cd testing-nestjs/<folderName>
  3. npm install OR yarn add
  4. npm run test OR yarn test

Contributing

Did I miss your favorite topic? Did I miss out on something you absolutely need tested? Well then open an issue, or better yet, a pull request! I'll be happy to look over any topics and try to help figure out how to test them or to merge any PRs that add to the value of the repository. If you do end up making a pull request, please add a screenshot of the test coverage so others can see how well the tests run. The complex-sample branch has a good example.

Change log

The change log can be found on the Releases page.

Authors and license

Jay McDoniel and contributors. Thanks to everyone who's helping with this effort!

MIT License, see the included License.md file.

Download Details:
Author: jmcdo29
Source Code: https://github.com/jmcdo29/testing-nestjs
License: MIT license

#nest #nestjs #node #javascript #testing

The Methods Of Testing NestJS including Unit Tests, Integration Tests