Building an "Add Destination" Connection Form

This guide walks through dynamically building an "Add Destination" form to allow your users to test & connect their destination.

Step 1: [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 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:

ParameterTypeNotes
application_originstringThe application host for your React app that will be making requests to the Prequel API using the generated scoped auth token.
destinationObjectvendor: the vendor of the destination to be created/updated.
host: the host url of the destination to be created/updated.
recipient_id: the Prequel UUID of the Recipient to which the destination belongs.
bucket_name: the bucket name of the destination to be created/updated, if applicable.
Example request body
{
  "application_origin": "app.example.co",
  "recipient_id": "1abc234d-efg5-6h7i-j8k9-012345lm6nop",
  "destination": <destination_object>
}

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 API or track Prequel's recipient UUIDs in your system.

POST response body

The POST response body will have the following structure:

ParameterTypeNotes
scoped_tokenstringThe scoped auth token generated for the specific destination context supplied in the request.
Example response body
{
  "scoped_token": "eyJhb..."
}

Note: scoped auth tokens have a TTL of one hour.

Step 2: [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:

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.


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.

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 3: [Client side] Install Prequel React hooks

Install the @prequel/react package from npm.

npm install @prequel/react@latest

Step 4: [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 for a more comprehensive implementation.


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
    false, 					// Optional (default false): hide any non-user facing fields like "name"
    PREQUEL_HOST,		// Optional (default api.prequel.co): the host url of your Prequel API
    "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 							  // 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>
  );
}

export default DestinationForm;

Step 5: [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 for a more comprehensive implementation.

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) {
      setTestResult("Connection test successful.");
    } else {
      setTestResult(message);
    }
    setTestRunning(false);
  }
  
  return (
    <div>
    {/* Render connection information as desired... */}
    </div>
  )
}