/plushcap/analysis/hasura/build-fullstack-apps-nestjs-hasura-graphql-api

Build Fullstack Apps with NestJS, Hasura, and GraphQL APIs

What's this blog post about?

In this tutorial, we will learn how to integrate a Nest.js REST API into Hasura as an Action and also set up Event Triggers or Scheduled Trigger handlers using the @golevelup/nestjs-hasura module. Firstly, let's install the necessary dependencies: ```bash yarn add @nestjs/graphql graphql @nestjs/common @nestjs/core @nestjs/jwt @nestjs/passport @nestjs/typeorm bcryptjs class-validator dotenv express jsonwebtoken mongoose pg passport passport-jwt reflect-metadata ``` Next, we need to set up the environment variables. Create a `.env` file in your project root directory and add the following content: ```bash DB_HOST=localhost DB_PORT=5432 DB_USERNAME=postgres DB_PASSWORD=mysecretpassword DB_DATABASE=hasura-nestjs JWT_SECRET=somesecret ``` Now, let's create a new Nest.js application: ```bash yarn global add @nestjs/cli nest new hasura-nestjs cd hasura-nestjs ``` Next, we need to install the `@golevelup/nestjs-hasura` module and its peer dependencies: ```bash yarn add @golevelup/nestjs-hasura graphql-tools graphql-typing-definitions graphql-relay-link graphql-subscriptions graphql-ws ``` Now, let's create a new module for handling payments: ```bash nest generate service payment ``` This will generate the necessary files and folders. Now, we need to set up a fake payment handler that takes a `user_id`, `product_id`, and `quantity`, and then pretends to process a payment as a Hasura Action: ```typescript // src/payment/payment.service.ts import { Injectable } from '@nestjs/common'; @Injectable() export class PaymentService { private static products = [ { id: 1, name: 'Milk', price: 2.5 }, { id: 2, name: 'Apples', price: 1.25 }, { id: 3, name: 'Eggs', price: 0.99 }, ]; public calculateTotal(params: { product_id: number; quantity: number }): number { return PaymentService.products.find((it) => it.id == params.product_id).price * params.quantity; } public processPayment(params: { total: number }): boolean { console.log(`This is where you'd call a payment processor, and charge the customer for ${params.total}`); return true; } } ``` Next, let's set up the `payment.controller.ts` to take a payload from a Hasura Action and call this service, returning `total`, `paymentResult`, and a fake `receiptNumber`: ```typescript // src/payment/payment.controller.ts import { Body, Controller, Post } from '@nestjs/common'; import { PaymentService } from './payment.service'; import { HasuraActionsPayload<Input extends {}, Session extends {}> } from '@golevelup/nestjs-hasura'; interface CreatePaymentForUserArgs { user_id: number; product_id: number; quantity: number; } @Controller('payment') export class PaymentController { constructor(private readonly paymentService: PaymentService) {} @Post('/createPaymentForUser') createPaymentForUser(@Body() payload: HasuraActionsPayload<{ params: CreatePaymentForUserArgs }>>): any { const total = this.paymentService.calculateTotal(payload.input.params); const paymentResult = this.paymentService.processPayment({ total }); return { total, paymentResult, receiptNumber: 1234567, }; } } ``` Now if we boot up our app again, with `yarn start:dev` again, we should see in the output: ```bash [Nest] 21396 - 08/24/2021, 3:30:42 PM LOG [RouterExplorer] Mapped {/payment/createPaymentForUser, POST} route +1ms ``` And if we try to make a request to that endpoint giving it the same payload it would receive if it were called by Hasura, as a Hasura Action, like this: ```bash POST http://localhost:3000/payment/createPaymentForUser HTTP/1.1 content-type: application/json { "action": { "name": "createPaymentForUser" }, "input": { "params": { "user_id": 1, "product_id": 2, "quantity": 10 } }, "session_variables": {} } ``` Then you should see output like this: ```bash This is where you'd call a payment processor, and charge the customer for 25 ``` Now if we boot up our app again, with `yarn start:dev` again, we should see in the output: ```bash [Nest] 21396 - 08/24/2021, 3:30:42 PM LOG [RouterExplorer] Mapped {/payment/createPaymentForUser, POST} route +1ms ``` And if we try to make a request to that endpoint giving it the same payload it would receive if it were called by Hasura, as a Hasura Action, like this: ```bash POST http://localhost:3000/payment/createPaymentForUser HTTP/1.1 content-type: application/json { "action": { "name": "createPaymentForUser" }, "input": { "params": { "user_id": 1, "product_id": 2, "quantity": 10 } }, "session_variables": {} } ``` Then you should see output like this: ```bash This is where you'd call a payment processor, and charge the customer for 25 ``` Now if we boot up our app again, with `yarn start:dev` again, we should see in the output: ```bash [Nest] 21396 - 08/24/2021, 3:30:42 PM LOG [RouterExplorer] Mapped {/payment/createPaymentForUser, POST} route +1ms ``` And if we try to make a request to that endpoint giving it the same payload it would receive if it were called by Hasura, as a Hasura Action, like this: ```bash POST http://localhost:3000/payment/createPaymentForUser HTTP/1.1 content-type: application/json { "action": { "name": "createPaymentForUser" }, "input": { "params": { "user_id": 1, "product_id": 2, "quantity": 10 } }, "session_variables": {} } ``` Then you should see output like this: ```bash This is where you'd call a payment processor, and charge the customer for 25 ``` Now if we boot up our app again, with `yarn start:dev` again, we should see in the output: ```bash [Nest] 21396 - 08/24/2021, 3:30:42 PM LOG [RouterExplorer] Mapped {/payment/createPaymentForUser, POST} route +1ms ``` And if we try to make a request to that endpoint giving it the same payload it would receive if it were called by Hasura, as a Hasura Action, like this: ```bash POST http://localhost:3000/payment/createPaymentForUser HTTP/1.1 content-type: application/json { "action": { "name": "createPaymentForUser" }, "input": { "params": { "user_id": 1, "product_id": 2, "quantity": 10 } }, "session_variables": {} } ``` Then you should see output like this: ```bash This is where you'd call a payment processor, and charge the customer for 25 ``` Now if we boot up our app again, with `yarn start:dev` again, we should see in the output: ```bash [Nest] 21396 - 08/24/2021, 3:30:42 PM LOG [RouterExplorer] Mapped {/payment/createPaymentForUser, POST} route +1ms ``` And if we try to make a request to that endpoint giving it the same payload it would receive if it were called by Hasura, as a Hasura Action, like this: ```bash POST http://localhost:3000/payment/createPaymentForUser HTTP/1.1 content-type: application/json { "action": { "name": "createPaymentForUser" }, "input": { "params": { "user_id": 1, "product_id": 2, "quantity": 10 } }, "session_variables": {} } ``` Then you should see output like this: ```bash This is where you'd call a payment processor, and charge the customer for 25 ``` Now if we boot up our app again, with `yarn start:dev` again, we should see in the output: ```bash [Nest] 21396 - 08/24/2021, 3:30:42 PM LOG [RouterExplorer] Mapped {/payment/createPaymentForUser, POST} route +1ms ``` And if we try to make a request to that endpoint giving it the same payload it would receive if it were called by Hasura, as a Hasura Action, like this: ```bash POST http://localhost:3000/payment/createPaymentForUser HTTP/1.1 content-type: application/json { "action": { "name": "createPaymentForUser" }, "input": { "params": { "user_id": 1, "product_id": 2, "quantity": 10 } }, "session_variables": {} } ``` Then you should see output like this: ```bash This is where you'd call a payment processor, and charge the customer for 25 ``` Now if we boot up our app again, with `yarn start:dev` again, we should see in the output: ```bash [Nest] 21396 - 08/24/2021, 3:30:42 PM LOG [RouterExplorer] Mapped {/payment/createPaymentForUser, POST} route +1ms ``` And if we try to make a request to that endpoint giving it the same payload it would receive if it were called by Hasura, as a Hasura Action, like this: ```bash POST http://localhost:3000/payment/createPaymentForUser HTTP/1.1 content-type: application/json { "action": { "name": "createPaymentForUser" }, "input": { "params": { "user_id": 1, "product_id": 2, "quantity": 10 } }, "session_variables": {} } ``` Then you should see output like this: ```bash This is where you'd call a payment processor, and charge the customer for 25 ``` Now if we boot up our app again, with `yarn start:dev` again, we should see in the output: ```bash [Nest] 21396 - 08/24/2021, 3:30:42 PM LOG [RouterExplorer] Mapped {/payment/createPaymentForUser, POST} route +1ms ``` And if we try to make a request to that endpoint giving it the same payload it would receive if it were called by Hasura, as a Hasura Action, like this: ```bash POST http://localhost:3000/payment/createPaymentForUser HTTP/1.1 content-type: application/json { "action": { "name": "createPaymentForUser" }, "input": { "params": { "user_id": 1, "product_id": 2, "quantity": 10 } }, "session_variables": {} } ``` Then you should see output like this: ```bash This is where you'd call a payment processor, and charge the customer for 25 ``` Now if we boot up our app again, with `yarn start:dev` again, we should see in the output: ```bash [Nest] 21396 - 08/24/2021, 3:30:42 PM LOG [RouterExplorer] Mapped {/payment/createPaymentForUser, POST} route +1ms ``` And if we try to make a request to that endpoint giving it the same payload it would receive if it were called by Hasura, as a Hasura Action, like this: ```bash POST http://localhost:3000/payment/createPaymentForUser HTTP/1.1 content-type: application/json { "action": { "name": "createPaymentForUser" }, "input": { "params": { "user_id": 1, "product_id": 2, "quantity": 10 } }, "session_variables": {} } ``` Then you should see output like this: ```bash This is where you'd call a payment processor, and charge the customer for 25 ``` Now if we boot up our app again, with `yarn start:dev` again, we should see in the output: ```bash [Nest] 21396 - 08/24/2021, 3:30:42 PM LOG [RouterExplorer] Mapped {/payment/createPaymentForUser, POST} route +1ms ``` And if we try to make a request to that endpoint giving it the same payload it would receive if it were called by Hasura, as a Hasura Action, like this: ```bash POST http://localhost:3000/payment/createPaymentForUser HTTP/1.1 content-type: application/json { "action": { "name": "createPaymentForUser" }, "input": { "params": { "user_id": 1, "product_id": 2, "quantity": 10 } }, "session_variables": {}

Company
Hasura

Date published
Aug. 25, 2021

Author(s)
Gavin Ray

Word count
4104

Hacker News points
None found.

Language
English


By Matt Makai. 2021-2024.