# Raw Request Actions

Components contain actions that wrap a large number of API endpoints. But, some APIs are vast with thousands of endpoints (only some of which are relevant to integrations). Not every endpoint that an app offers is represented by an action in the component.

That's where an **HTTP Raw Request** action is useful. Raw request actions allow you to send a request to any endpoint that an API offers, using an HTTP client that is already authenticated with the third-party. Most built-in components include raw request actions, and depending on the API they may include an action for generic HTTP requests or an action for GraphQL requests. This document details how to use raw request actions in your integration.

## Determining what endpoint to specify[​](#determining-what-endpoint-to-specify "Direct link to Determining what endpoint to specify")

You can determine the endpoint URL to use by looking at the API's documentation. For example, the [Asana API documentation](https://developers.asana.com/reference/rest-api-reference) lists all of the endpoints that they offer. To [Get audit log events](https://developers.asana.com/reference/getauditlogevents) from Asana, you need to send a GET request to `https://app.asana.com/api/1.0/workspaces/{workspace_gid}/audit_log_events`.

The Asana component helper text notes that it fills in the base URL `https://app.asana.com/api/1.0` for you. So, you would need to construct the remaining `/workspaces/{workspace_gid}/audit_log_events` portion of the URL.

![Raw request URL input with config variable template](/docs/assets/images/url-input-5e39dd4891ee0d9ca4ec0867fbbdb20c.png)

The comments and example you see when you first input the URL are provided by the component developer and let you know what the base URL is and what the rest of the path should look like.

Override a raw request base URL

You can override a raw request base URL by specifying a fully qualified URL in the URL input. For example, if you wanted to send a request to `https://my-api.example.com/some/endpoint` from the Asana raw request component, you can specify that full URL in the URL input and the component's base URL will be ignored.

## Sending JSON to an API using raw request[​](#sending-json-to-an-api-using-raw-request "Direct link to Sending JSON to an API using raw request")

The majority of modern APIs expect JSON data in the request body. You can send JSON data to an API using a raw request action by constructing a JSON string in the `data` input.

include a content-type header

Most JSON-based APIs require that you specify a `content-type` header of `application/json` when sending JSON data. Otherwise, you may see an error from the API that the request body is not valid JSON. If you reference a JavaScript object in the `data` input, the component will automatically set the `content-type` header to `application/json` for you. If you specify a JSON string, manually include the `content-type` header in the `Headers` input.

![Raw request JSON content type header](/docs/assets/images/json-content-type-98f56660bfb62db30b6bf5f808c860a4.png)

Additionally, you can reference a JavaScript object in the `data` input, and the component will automatically convert it to a JSON string for you. This is useful if you have a code step that constructs a JavaScript object that you want to send to the API.

![Raw request data from a JavaScript object](/docs/assets/images/javascript-object-data-input-ce6b3d93a7c909014369f56e742272c1.png)

## Sending non-JSON text to an API using raw request[​](#sending-non-json-text-to-an-api-using-raw-request "Direct link to Sending non-JSON text to an API using raw request")

For non-JSON text data, like XML, CSV, etc., you can use the [Change Data Format](https://prismatic.io/docs/components/change-data-format.md) component to serialize data into the appropriate format. Like JSON data, ensure that you specify an appropriate `content-type` header (e.g. `text/csv`, `application/xml`, etc.).

## Sending form data to an API using raw request[​](#sending-form-data-to-an-api-using-raw-request "Direct link to Sending form data to an API using raw request")

Form data inputs are useful for sending data to APIs that expect a content type `application/x-www-form-urlencoded`. Form data is largely used when you need to send several types of data in a single request, such as a file along with some metadata.

To send form data, first ensure that you have cleared the `data` input - you can't send both data and form data together. Then, specify form data key/value pairs. In the below example, we send both a simple string `userid` and an XML payload `person-xml`. We also send a file `profile-picture` by referencing a picture from a previous step, and we give the file a name using the `File Data File Names` input:

![Raw request form data inputs](/docs/assets/images/form-data-inputs-19d0830e518da1e4f9176bfd98c3884e.png)

Serialize JSON before sending

Unlike the `data` input, form data inputs cannot accept JavaScript objects. Serialize the JavaScript object into a JSON string (or XML string, etc.) before referencing it.

## Sending custom parameters and headers using raw request[​](#sending-custom-parameters-and-headers-using-raw-request "Direct link to Sending custom parameters and headers using raw request")

The `Query Parameters` input allows you to specify custom query parameters to send to the API (that's the `?key=value` portion of the URL). While you could specify query parameters in the URL input through a [template input](https://prismatic.io/docs/integrations/low-code-integration-designer/passing-data-between-steps.md#template-inputs), string concatenation is prone to encoding issues. The `Query Parameters` input ensures that your query parameters are properly URI-encoded.

The `Headers` input allows you to specify custom headers to send to the API. In addition to the usual `Content-Type` header, you may need to specify other headers like `Accept` or a custom header like `X-Tenant-ID`.

![Raw request parameters and headers inputs](/docs/assets/images/parameters-and-headers-8513ec999d4a2bc57d26c3a7dedf1800.png)

## Response data types for raw request actions[​](#response-data-types-for-raw-request-actions "Direct link to Response data types for raw request actions")

The `Response Type` input allows you to specify how you would like the response data to be formatted.

* `json` is the default response type and will return the response data as a JavaScript object. This type assumes that the API returns `application/json` response data.
* `text` will return the response data as a string. This type assumes that the API returns `text/plain`, `text/html`, or other text-based response data.
* `arraybuffer` is used when you expect a binary file response. Use this if you expect a file, like a PDF, image, etc.

## Debugging raw request actions[​](#debugging-raw-request-actions "Direct link to Debugging raw request actions")

If you're having trouble with a raw request action, you can toggle the `Debug` input to see the full request and response data in logs (remember to toggle it back before deploying to production!). This is useful for debugging issues with the request body, headers, etc.

It is also useful to use a tool like [Postman Echo](https://learning.postman.com/docs/developer/echo-api/) to echo back the request that you're sending. To use Postman echo, set the URL to `https://postman-echo.com/post` and the `HTTP Method` to `POST`. The raw request step's result will contain the full request data that you sent, which you can compare to the API's documentation to ensure that you're sending the correct data.

Another tool that is useful for debugging raw requests is the [mendhak/http-https-echo](https://hub.docker.com/r/mendhak/http-https-echo) Docker image. This Docker container will print and echo any request that it receives. To run the Docker container and expose its port, run the following command:

```bash
docker run -p 8080:8080 -p 8443:8443 --rm -t mendhak/http-https-echo:latest

```

In a separate terminal, run [ngrok](https://ngrok.com/) to expose the Docker container to the internet:

```bash
ngrok http 8080

```

`ngrok` will give you a public URL, like `https://73ed-123-45-67-89.ngrok-free.app`, which you can use as the URL in your raw request action. When you run your integration, you can see the full request data in the Docker container's logs.

![Raw request ngrok echo](/docs/assets/images/ngrok-echo-02a0e9eb1fe0ca99e242e074fd0cea2d.png)

tip

If you can get an HTTP request to work in a tool like `curl` or [Postman](https://www.postman.com/) but cannot get it to work in a raw request action, send both the raw request and Postman request to your `ngrok` / echo endpoint. Comparing the two requests side-by-side can help you identify what is different between the two requests.

## Building an HTTP raw request action in your custom component[​](#building-an-http-raw-request-action-in-your-custom-component "Direct link to Building an HTTP raw request action in your custom component")

You can build your own raw request actions for your custom components. That's useful if you have a large API but don't have the development resources to build out actions for every endpoint. A raw request action can be used by your integration builders to send requests to any endpoint that your API offers.

For an example raw request action, see our [GitHub examples repo](https://github.com/prismatic-io/examples/blob/main/components/asana/src/actions/rawRequest.ts) for the code that backs our Asana component's [raw request action](https://prismatic.io/docs/components/asana.md#rawrequest).

Prismatic components use standard inputs and a `sendRawRequest` function to build built-in raw request actions. You can import the same inputs and function from the custom component SDK, `@prismatic-io/spectral`, so you don't need to write that code yourself.

Example raw request action from the Asana component

```ts
import { action } from "@prismatic-io/spectral";
import {
  inputs as httpClientInputs,
  sendRawRequest,
} from "@prismatic-io/spectral/dist/clients/http";
import { connectionInput } from "../inputs";

const rawRequest = action({
  display: {
    label: "Raw Request",
    description: "Send a raw HTTP request to Asana API",
  },
  inputs: {
    connection: connectionInput,
    ...httpClientInputs,
    url: {
      // Optional; this adds component-specific instructions to the URL input
      ...httpClientInputs.url,
      comments:
        "Input the path only (/goals), The base URL is already included (https://app.asana.com/api/1.0). For example, to connect to https://app.asana.com/api/1.0/goals, only /goals is entered in this field.",
      example: "/goals",
    },
  },
  perform: async (context, { connection, ...httpClientInputs }) => {
    const asanaToken =
      connection?.token?.access_token || connection?.fields?.apiKey;
    const { data } = await sendRawRequest(
      "https://app.asana.com/api/1.0", // Change this to your API's base URL
      httpClientInputs,
      { Authorization: `Bearer ${asanaToken}` }, // Authorization methods vary by API
    );
    return { data };
  },
});

export default rawRequest;

```

You can likely copy and paste the above code, changing the helpful `comments` and `example` for the `URL` input and changing the base URL and authorization header to match your API.

## Sending GraphQL requests using raw request[​](#sending-graphql-requests-using-raw-request "Direct link to Sending GraphQL requests using raw request")

Some APIs, like [Fluent Commerce](https://prismatic.io/docs/components/fluent-commerce.md), are GraphQL-based. These built-in components generally have `Generic GraphQL Request` actions that you can use to send GraphQL requests.

The generic GraphQL request action has a `Query or Mutation` input, which is the GraphQL query or mutation that you want to send. It's wise to parameterize queries and mutations using variables (to avoid QL-injection issues).

Most generic GraphQL request actions have a `Variables` input, which is a key/value input where you can specify variables and their values that your mutation uses. It also generally includes a `Variables Object` input if you would like to provide a key/value object from a previous step. `Variables` and `Variables Object` are merged together and can be used in tandem.

For example, suppose we want to send this mutation:

```graphql
mutation myMutation(
  $customerName: String!
  $customerDescription: String!
  $labels: [String]
) {
  createCustomer(
    input: {
      name: $customerName
      description: $customerDescription
      labels: $labels
    }
  ) {
    id
  }
}

```

You could reference `customerName` from a previous step but also supply `customerDescription` or `labels` from a previous step using the `Variables Object` input:

![GraphQL Raw request variables object input](/docs/assets/images/graphql-variables-object-input-39fb2997895e337e5433063476f4f03f.png)

Construct GraphQL queries and mutations first using a GraphQL client

GraphQL APIs often offer a web-based GraphQL explorer where you can construct queries and mutations. We recommend using a GraphQL client tool to construct your query or mutation first and then copy/pasting it into the `Query or Mutation` input.

## Building a GraphQL raw request action in a custom component[​](#building-a-graphql-raw-request-action-in-a-custom-component "Direct link to Building a GraphQL raw request action in a custom component")

A generic GraphQL raw request action is similar to an HTTP raw request action, but instead of generic data inputs it has inputs for the query/mutation to run and the parameterized variables to use.

This example requires three additional dependencies:

```bash
npm install graphql graphql-request lodash.merge

```

In the below example, we use the `graphql-request` library to prepare and send the GraphQL request (including the query/mutation and parameterized variables). `lodash.merge` is used to merge the `Variables` and `Variables Object` inputs together, as you may want to specify some variables in the UI and reference other variables as an object from a previous step.

The portions of code you will need to change are the GraphQL API URL, and any custom authorization headers that your API requires.

Example GraphQL raw request action

```ts
import { GraphQLClient } from "graphql-request";
import {
  Connection,
  action,
  component,
  connection,
  input,
  util,
} from "@prismatic-io/spectral";
import merge from "lodash.merge";

const createClient = (connection: Connection) =>
  new GraphQLClient(
    "https://app.prismatic.io/api", // Replace this URL with your API
    {
      headers: { Authorization: `Bearer ${connection.fields.apiKey}` }, // Authorization methods vary by API
    },
  );

const genericGraphQLQuery = action({
  display: {
    label: "Generic GraphQL Query",
    description: "Issue a query or mutation against the GraphQL API",
  },
  inputs: {
    connection: input({
      label: "Connection",
      type: "connection",
      required: true,
    }),
    query: input({
      label: "Query or Mutation",
      type: "code",
      required: true,
      language: "graphql",
      clean: util.types.toString,
    }),
    // Variables are presented in the UI as a key-value pair list, and are handy
    // if you know ahead of time how many variables your query includes
    variables: input({
      label: "Variables",
      type: "string",
      required: false,
      collection: "keyvaluelist",
      clean: (val: any) => util.types.keyValPairListToObject(val),
    }),
    // Variables Object is presented in the UI as a JSON editor, and is handy
    // if you don't know ahead of time how many variables your query includes,
    // or if you want to reference entire key/value objects from previous steps.
    variablesObject: input({
      label: "Variables Object",
      type: "code",
      language: "json",
      required: false,
      clean: (value) => (value ? util.types.toObject(value) : {}),
    }),
  },
  perform: async (context, params) => {
    const client = createClient(params.connection);
    const data = await client.request(
      params.query,
      merge(params.variables, params.variablesObject), // Merge the two variables inputs together
    );
    return { data };
  },
});

```
