> ## Documentation Index
> Fetch the complete documentation index at: https://docs.on-it.no/llms.txt
> Use this file to discover all available pages before exploring further.

# Getting Started

> Getting started with NXT triggers. The replacement for SQL triggers for Business NXT.

<a href="https://businessnxt.dev/" target="_blank">
  <img src="https://mintcdn.com/onitas/VA-10adAY-BAmUwu/images/nxt-triggers-hero.png?fit=max&auto=format&n=VA-10adAY-BAmUwu&q=85&s=c5d37a6e84c8b565e5c2550078f50313" alt="Hero" noZoom className="rounded-lg shadow" width="1287" height="646" data-path="images/nxt-triggers-hero.png" />
</a>

## Granting Access

To get started with NXT Triggers with a Visma Business NXT Customer, you need to grant it access. Go to [Connect Application](https://nxt.ninja/library/lyt_AJXleB5Gvy6PyzbZM0Op) and
add `isv_nxt_triggers` as a user with connected application activated.

You also have to make sure that you are a user with supervisor rights in the Business NXT customer.

## Creating a Trigger

To create or edit a trigger, open [NXT Triggers](https://businessnxt.dev) and select the company you want to work with under triggers. Then press `+ Create Trigger` and select the table and event you want to trigger on.

## Your first trigger

When you create a new trigger, you will be met with a code editor. Here you can write your trigger code in TypeScript.

All triggers must export a default function, which is an async function that takes an object with the following properties.

| Property            | Description                                                                                                                                                                                                                                   |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| event               | Details about the event that triggered the trigger. Read about [the event object](event-object).                                                                                                                                              |
| db                  | This is a minimal client to interact with the Business NXT client. This is automatically authenticated to interact with the client that sent the event.                                                                                       |
| createGraphQLClient | A function that creates a GraphQL client using the `VISMA_CLIENT_ID` and `VISMA_CLIENT_SECRET` you've defined under settings.                                                                                                                 |
| printOrderDocument  | Creates a PDF document from an order document using the NXT Ninja Document Printer. This allows for more customizations than the regular document printer in Business NXT.                                                                    |
| sendEmail           | Sends an email using Postmark. Required properties are `{ To: "", From: "", Subject: "", TextBody: ""}`. This requires the secret `POSTMARK` to be set.                                                                                       |
| sendToPrinter       | Sends a document to a printer using [PrintNode.com](https://printnode.com). Required properties are `{ printerId: 0, document: "base64 data of PDF"}`. The PrintNode API token must be set as `PRINTNODE` under settings.                     |
| useIndepotent       | A function that ensures that a function is only run once, even if it's called multiple times. The first argument is the key, and the second is the function. The value returned from the function must be serializable to JSON, or undefined. |
| changedColumns      | An object with the columns that have changed in the event. This is `undefined` for `DELETE` events.                                                                                                                                           |
| hasColumnChanged    | A function that checks if a column has changed. This takes the column name as a parameter and returns true or false. Will always return `false` for `DELETE` events.                                                                          |

### About indepotency

A big difference between SQL triggers and NXT triggers is that a NXT trigger might be retried. Since we are running in the cloud, we can't guarantee that the services we are calling are idempotent. To ensure that a part of a trigger is only run once, even if it's called multiple times, you can use the `useIndepotent` function.

```typescript useIndepotent theme={null}
// the key must be unique for the function you are running
const result = await useIndepotent("unique-sample-key", async () => {
  const value = await someAsyncFunction();
  return value; // Must be serializable to JSON
});
```

### Example usage in triggers

<CodeGroup>
  ```typescript sendEmail theme={null}
  const msg: Message = {
    To: name ? `${name} <${emailAddress}>` : emailAddress,
    From: "no-reply@businessnxt.email",
    Subject: `${documentType} - ${ordDoc.orderNo} - ${ordDoc.name?.trim()}`,
    TextBody: "Se vedlagt ordredokument",
    Attachments: [
      {
        Content: pdf,
        ContentID: "orderdocument",
        ContentType: "application/pdf",
        Name: fileName,
      },
    ],
  };

  await useIndepotent("send-email", async () => {
    const rsp = await sendEmail(msg);
    console.log("sent status", rsp);
  });
  ```

  ```typescript printOrderDocument theme={null}
  const pdf = await printOrderDocument(event.primaryKeys.orderDocumentNo, {
    base64: true,
  });
  ```

  ```typescript sendToPrinter theme={null}
  const pdf =
    orderDocumentPdf.data?.useCompany?.orderDocument_processings?.downloadPdf;

  console.log(details, orderDocumentPdf);
  const orderDocument = details.data?.useCompany?.orderDocument?.items?.[0];
  invariant(orderDocument, "Order document is required");

  const createdBy = details.data.useCompany.createdBy.items?.[0];

  const printerId = createdBy?.group12;
  if (printerId) {
    const rsp = await sendToPrinter({
      printerId,
      document: pdf.fileBytes,
    });
    console.log("Sent to printer", rsp);
  }
  ```
</CodeGroup>

### Base Trigger

This is the starting point for your new trigger. It logs the event object to the console, and by using the debugger you can see the output from the trigger.

<CodeGroup>
  ```typescript typescript theme={null}
  import { HandlerFunctionArgs } from "businessnxt-dev";

  export default async function ({
    event,
    db,
  }: HandlerFunctionArgs<"associate", Env>) {
    if (event.event !== "DELETE") {
      const associate = await db.associate.findFirst({
        filter: event.filter,
        columns: {
          // add the columns you'd like returned here. Use the form { column1: true, column2: true }
        },
      });

      console.log({ associate });
    }
  }
  ```
</CodeGroup>
