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

# Building an "Add Destination" connection form

> Build a dynamic "Add Destination" form to allow your users to test and connect their destinations.

<Steps>
  <Step title="[Server side] Set up scoped auth token generation">
    In your backend, build an API endpoint that makes a `POST` to `https://api.prequel.co/actions/generate-scoped-auth-token` to request to generate a scoped auth token from the Prequel API. Be sure to pass your API Key as a header (`X-API-KEY`) in the request as well as the desired API version (`X-Prequel-Api-Version`). Your API endpoint should return the scoped token that was returned from the Prequel API.

    *Note: If you are using a self-hosted deployment or a non-US deployment, replace `api.prequel.co` with the appropriate host.*

    ### POST request body

    This request body is for the API Version `2023-12-01`. Your `POST` request body should have the following structure:

    | Parameter            | Type     | Notes                                                                                                                                                                             |
    | -------------------- | :------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `recipient_id`       | `string` | The Recipient ID for your customer. This value should be derived in your backend from a server-side token or your preferred method to associate user sessions with Recipient IDs. |
    | `destination`        | `Object` | This is the `Destination` object sent from the Prequel React hooks. You can perform any validation on the object that you wish and send it on to the Prequel API.                 |
    | `application_origin` | `string` | The application host for your React app that will be making requests to the Prequel API using the generated scoped auth token.                                                    |

    ##### Example request body

    ```json title="Example request body" icon="brackets-curly" expandable theme={null}
    {
      "recipient_id": "1abc234d-efg5-6h7i-j8k9-012345lm6nop",
      "destination": <destination_object>,
      "application_origin": "app.example.co"
    }
    ```

    ### Getting the recipient ID

    The `recipient_id` is Prequel's unique identifier for the recipient to which the destination is being added. In your system, there is a separate identifier for that customer. You can either you can look them up using the Prequel API or track Prequel's recipient UUIDs in your system.

    ### POST response body

    The `POST` response body will have the following structure:

    | Parameter      | Type     | Notes                                                                                         |
    | -------------- | :------- | :-------------------------------------------------------------------------------------------- |
    | `scoped_token` | `string` | The scoped auth token generated for the specific destination context supplied in the request. |

    ##### Example response body

    ```json title="Example response body" icon="brackets-curly" expandable theme={null}
    {
      "scoped_token": "eyJhb..."
    }
    ```

    Note: scoped auth tokens have a TTL of one hour.
  </Step>

  <Step title="[Client side] Set up fetchToken calls">
    In your frontend, define a set of `fetchToken` functions. These functions will consume various inputs (see examples below) and query the API endpoint you built in Step 1 to generate auth tokens required by the Prequel API. These `fetchToken` functions will be used as parameters to and called by Prequel's React hooks.

    There are two `fetchToken` types you will need to implement, depending on the hooks you use:

    ```typescript title="fetchToken types" icon="code" theme={null}
    export type FetchAuthToken = () => Promise<string>;
    export type FetchAuthTokenWithDestination = (d: PreparedDestination) => Promise<string>;
    ```

    ### Example: `FetchAuthToken`

    Required by: `useModels`, `useProducts` hooks

    This function contains an empty request body (as should your subsequent request to the Prequel API) because there is no relevant customer-specific scoping required for the fetching of your organization's models and products.

    ```typescript title="fetchToken" icon="code" expandable theme={null}
    const fetchToken: (d: PreparedDestination) => Promise<string> = (destination) =>
      fetch(`https://api.example.co/generate-auth-token`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: {},
      })
        .then((response: Response) => response.json())
        .then((body) => body.scoped_token)
        .catch((reason) => {
          console.error(reason);
      });
    ```

    ### Example: `FetchAuthTokenWithDestination`

    Required by: `useCreateDestination`, `useTestConnection` hooks

    This function passes a destination object as the request body (in addition to - not in place of - to subsequent requests to the Prequel API) so that the generated token can be appropriately scoped to a specific destination.

    ```typescript title="fetchTokenWithDestination" icon="code" expandable theme={null}
    const fetchTokenWithDestination: (d: PreparedDestination) => Promise<string> = (destination) =>
      fetch(`https://api.example.co/generate-auth-token`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify(destination),
      })
        .then((response: Response) => response.json())
        .then((body) => body.scoped_token)
        .catch((reason) => {
          console.error(reason);
      });
    ```
  </Step>

  <Step title="[Client side] Install Prequel React hooks">
    Install the `@prequel/react` package from [npm](https://www.npmjs.com/package/@prequel/react).

    ```bash title="Install @prequel/react" icon="terminal" theme={null}
    npm install @prequel/react@latest
    ```
  </Step>

  <Step title="[Client side] Render the form">
    In your frontend, define a `DestinationForm` component. The component will leverage the `useDestination` hook and the `useDestinationForm` hook to render a dynamic form that can be used to test & add a new destination. A brief example is show below. See [our sample React app](https://github.com/prequel-co/react-example/blob/main/src/DestinationForm.tsx) for a more comprehensive implementation.

    ```tsx title="DestinationForm" icon="react" expandable theme={null}
    const DestinationForm = () => {
      const formRef = useRef<HTMLFormElement>(null);
      const [destination, setDestination] = useDestination({
        name: "Customer A Snowflake Destination", // Readable name for the destination
      });
      const destinationForm = useDestinationForm(
        destination,
        PREQUEL_ORG_ID,                 // the UUID for your organization in Prequel
        {
          includeInternalFields: true,  // Optional (default false): hide any non-user facing fields like "name"
          host: PREQUEL_HOST,           // Optional (default api.prequel.co): the host url of your Prequel API
          vendorQuery: "snowflake"      // Optional (default undefined): vendor to filter the form to
        }
      );
      const createDestination = useCreateDestination(
        fetchTokenWithDestination,  // the fetchToken function you created in step 2
        "app.example.co",           // the origin of your React app (used to allow CORS)
        PREQUEL_HOST                // Optional (default api.prequel.co): the host url of your Prequel API
      );

      // This is the PreparedDestination you can pass to testConnection and createDestination
      const preparedDestination = useMemo(
        () => prepareDestinationWithForm(destination, destinationForm),
        [destination, destinationForm]
      );

      if (!destinationForm) {
        return (
          <div className="d-flex justify-content-center">
            <Spinner animation="border" role="status">
              <span className="visually-hidden">Loading...</span>
            </Spinner>
          </div>
        );
      }

      return (
        <Form ref={formRef} className="d-flex flex-column" onSubmit={onSubmit}>
          {destinationForm.map((section) => (
            <Fragment key={section.id}>
              <Form.Group>
            		{/* Render the section title and subtitle... */}
                {section.fields.map((field) => {
                  {/* Render the field... */}
                })}
              </Form.Group>
            </Fragment>
          ))}
          <Button type="submit">Submit form</Button>
        </Form>
      );
    }
    ```
  </Step>

  <Step title="[Client side] Test and create destinations">
    Once your form is rendering as desired, you should augment form functionality to test a destination connection before creating that destination. It's recommended that you only allow users to create destinations that are connecting successfully. You can leverage the `useTestConnection` hook as demonstrated below. See [our sample React app](https://github.com/prequel-co/react-example/blob/main/src/TestConnection.tsx) for a more comprehensive implementation.

    ```tsx title="TestConnectionComponent" icon="react" expandable theme={null}
    const TestConnectionComponent = ({ preparedDestination }) => {
    	const [testRunning, setTestRunning] = useState(false);
      const [testResult, setTestResult] = useState<string | null>(null);
      const testConnection = useTestConnection(
        fetchTokenWithDestination,  // the fetchToken function you created in step 2
        "app.example.co",           // the origin of your React app (used to allow CORS)
        PREQUEL_HOST                // the host url of your Prequel API
      );

      async function testDestinationConnection() {
        setTestRunning(true);
        setTestResult("Testing new connection...");
        const { data, message } = await testConnection(preparedDestination);
        if (data?.status === "success") {
          setTestResult("Connection test successful.");
        } else {
          setTestResult(message);
        }
        setTestRunning(false);
      }

      return (
        <div>
        {/* Render connection information as desired... */}
        </div>
      )
    }
    ```
  </Step>
</Steps>
