Skip to content

Latest commit

 

History

History
 
 

webhooks

@nestjs-shopify/webhooks

Enable Shopify webhook support for your NestJS application using ShopifyWebhooksModule. Import this module at the root level after ShopifyCoreModule:

// app.module.ts
import { CustomersCreateWebhookHandler } from './customers-create.webhook-handler.ts';

@Module({
  imports: [
    ShopifyCoreModule.forRoot({
      apiKey: 'foo',
      apiSecretKey: 'bar',
      apiVersion: ApiVersion.Unstable,
      hostName: 'localhost:8081',
      isEmbeddedApp: true,
      scopes: ['test_scope'],
    }),
    ShopifyWebhooksModule.forRoot({
      path: '/shopify-webhooks',
    }),
  ],
  providers: [CustomersCreateWebhookHandler],
})
export class AppModule {}

The ShopifyWebhooksModule automatically configures webhook handlers based on the usage of the @WebhookHandler class decorator. These webhook handlers require you to extend the ShopifyWebhookHandler class and define a handle method:

// customers-create.webhook-handler.ts
import {
  ShopifyWebhookHandler,
  WebhookHandler,
} from '@nestjs-shopify/webhooks';

@WebhookHandler('PRODUCTS_CREATE')
export class CustomersCreateWebhookHandler extends ShopifyWebhookHandler {
  async handle(shop: string, product: unknown): Promise<void> {
    console.log(shop, product);
  }
}

Make sure you add all ShopifyWebhookHandler classes to the providers definition of the AppModule as shown in all examples.

NOTE: this package relies on Nest >8.4.5 rawBody option.

// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule, { rawBody: true });
  await app.listen(3000);
}
bootstrap();

This will config gives us access to the raw body of a request to perform HMAC validation on incoming Shopify webhook requests.

You can also import the ShopifyWebhooksModule with useFactory, useClass or useExisting when importing the module using .forRootAsync(). This allows you to inject a webhook handler within the context of dependency injection in your application:

// app.module.ts
import { CustomersCreateWebhookHandler } from './customers-create.webhook-handler.ts';

@Module({
  imports: [
    ShopifyCoreModule.forRoot({
      apiKey: 'foo',
      apiSecretKey: 'bar',
      apiVersion: ApiVersion.Unstable,
      hostName: 'localhost:8081',
      isEmbeddedApp: true,
      scopes: ['test_scope'],
    }),
    ShopifyWebhooksModule.forRootAsync({
      useFactory: () => ({
        path: '/shopify-webhooks',
      }),
    }),
  ],
  providers: [CustomersCreateWebhookHandler],
})
export class AppModule {}

Registering webhooks in Shopify API

This module exports the ShopifyWebhooksService. Call this service with an offline access token to register your webhooks:

// my-auth.handler.ts
import { ShopifyAuthAfterHandler } from '@nestjs-shopify/auth';
import { ShopifyWebhooksService } from '@nestjs-shopify/webhooks';

@Injectable()
export class MyAuthHandler implements ShopifyAuthAfterHandler {
  constructor(private readonly webhooksService: ShopifyWebhooksService) {}
  async afterAuth(req: Request, res: Response, session: Session) {
    if (session.isOnline) {
      // redirect to homepage of your app etc...
      return;
    }

    // Otherwise, we have an offline access token
    await this.webhooksService.registerWebhooks(session);

    // Your other logic for offline auth...
  }
}

Registering webhooks on application start

This module registers webhook handlers as providers. Due to the nature of providers being initialised asynchronious, the ShopifyWebhooksService.registerWebhooks might have no access to the registered handlers onModuleInit. If you want to ensure registration of webhooks on startup of the application, you should rely on the OnApplicationBootstrap interface instead even tho this could slow down your application start time.