---
title: "Example functions - Workflow Functions"
description: "Copy-paste example functions - HTTP requests, reading and writing Shopify store data, using input variables and secrets - from small to larger workflows."
canonical: "https://docs.workflow-functions.app/examples"
---

# Example functions

Copy-paste starting points you can drop into a new function and adapt. Every function has the same shape - `export default async function (input, ctx)` - where `input` is the JSON from Flow (or the **Variables** tab) and `ctx` gives you `ctx.log`, `ctx.fetch`, and (when enabled) `ctx.shopify.graphql`. New here? Start with [How to create a function](https://docs.workflow-functions.app/how-to-create-a-function.md) and [Passing variables into your function](https://docs.workflow-functions.app/passing-variables-into-your-function.md).

## Small: the basics

No permissions or setup needed - paste one in and press **Test**.

```javascript
export default async function (input, ctx) {
  const response = await ctx.fetch('https://jsonplaceholder.typicode.com/todos/1');

  if (!response.ok) {
    throw new Error(`Request failed with status ${response.status}`);
  }

  const json = await response.json();
  ctx.log('Fetched todo:', json);

  return json;
}
```

```javascript
export default async function (input, ctx) {
  // Everything Flow (or the Variables tab) sends arrives as `input`.
  ctx.log('Received input:', input);

  return {
    received: input,
    itemCount: Array.isArray(input.items) ? input.items.length : 0,
  };
}
```

## Medium: variables, secrets, and store data

These read from `input`, use a stored **secret**, or call the Shopify Admin API. For the Shopify one, turn on **Needs Shopify data** on the Settings tab and grant the matching permission. See [Permissions and store data access](https://docs.workflow-functions.app/permissions-and-store-data-access.md) and [Creating and using secrets](https://docs.workflow-functions.app/creating-and-using-secrets.md).

```javascript
export default async function (input, ctx) {
  // secrets.WEBHOOK_URL is set on the Secrets page; input.message comes from Flow.
  const res = await ctx.fetch(secrets.WEBHOOK_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ text: input.message ?? 'Hello from Shopify Flow' }),
  });

  if (!res.ok) {
    throw new Error(`Webhook failed with status ${res.status}`);
  }

  return { delivered: true, status: res.status };
}
```

```javascript
export default async function (input, ctx) {
  // Needs "Needs Shopify data" + Read products.
  // input.productId is a GID like "gid://shopify/Product/123".
  const res = await ctx.shopify.graphql(
    `query GetProduct($id: ID!) {
      product(id: $id) {
        id
        title
        totalInventory
      }
    }`,
    { id: input.productId }
  );

  if (res.errors) {
    throw new Error('Shopify error: ' + JSON.stringify(res.errors));
  }

  return res.data.product;
}
```

## Larger: real workflows

Query several records or make a change in the store. Both need **Needs Shopify data** on and the matching permission granted.

```javascript
export default async function (input, ctx) {
  // Needs "Needs Shopify data" + Read orders.
  const first = Math.min(Math.max(parseInt(input.first ?? 10, 10) || 10, 1), 100);

  const res = await ctx.shopify.graphql(
    `query RecentOrders($first: Int!) {
      orders(first: $first, reverse: true, sortKey: CREATED_AT) {
        edges {
          node {
            id
            name
            currentTotalPriceSet { shopMoney { amount currencyCode } }
          }
        }
      }
    }`,
    { first }
  );

  if (res.errors) {
    throw new Error('Shopify error: ' + JSON.stringify(res.errors));
  }

  const orders = res.data.orders.edges.map((edge) => edge.node);
  let total = 0;
  let currency = null;
  for (const order of orders) {
    total += Number(order.currentTotalPriceSet?.shopMoney?.amount ?? 0);
    currency = currency ?? order.currentTotalPriceSet?.shopMoney?.currencyCode ?? null;
  }

  ctx.log(`Summed ${orders.length} orders: ${total.toFixed(2)} ${currency ?? ''}`);

  return {
    count: orders.length,
    total: Number(total.toFixed(2)),
    currency,
    orders,
  };
}
```

```javascript
export default async function (input, ctx) {
  // Needs "Needs Shopify data" + Write customers.
  // input.customerId is a GID like "gid://shopify/Customer/123"; input.tag defaults to "vip".
  const tag = input.tag ?? 'vip';

  const res = await ctx.shopify.graphql(
    `mutation AddTag($id: ID!, $tags: [String!]!) {
      tagsAdd(id: $id, tags: $tags) {
        node { id }
        userErrors { field message }
      }
    }`,
    { id: input.customerId, tags: [tag] }
  );

  const errors = res.errors ?? res.data?.tagsAdd?.userErrors;
  if (errors && errors.length) {
    throw new Error('Tagging failed: ' + JSON.stringify(errors));
  }

  ctx.log(`Tagged ${input.customerId} with "${tag}"`);
  return { tagged: input.customerId, tag };
}
```

> [!NOTE]
> **Before you run a Shopify example**
> - Turn on **Needs Shopify data** on the Settings tab and grant the permission the code needs (Read products / Read orders / Write customers).
> - Pass a real id on the **Variables** tab - the **Find product**, **Latest order**, and **Latest customer** buttons fill one in for you.
> - `ctx.shopify.graphql(query, variables)` returns `{ data, errors }` - always check `errors`. Store ids are GIDs like `gid://shopify/Product/123`. The access token never enters your code.
