Migration guide - 2023-10

A guide to help developers migrate to monday API version 2023-10

Version 2023-10 contains a number of changes to improve the scalability and reliability of the monday GraphQL API. Many of these changes are breaking, so you'll need to migrate your applications to use a slightly different query structure when making your calls.

This guide is long; use the table of contents on the right to skip to the information you need.

❗️

No more extensions

As of March 5th, customers can no longer request an extension. All customers must migrate to a new API version as we near the March 18th deadline.


General information

Rationale

We've migrated our platform to a new data infrastructure called mondayDB, to support the next level of scale in our platform. We know that our customers are using monday in larger and larger workflows, and we want to design our system to support them.

Not only will the new API be faster, but it also has a rich sorting and filtering layer. In the past applications would have to implement board filtering themselves, but now it's handled by the API.

Migration steps

Application developers

Review the changes to the API and adjust your applications to use version 2023-10 or later.

A fully-migrated app will:

  • Use the API-Version header in all API calls
  • Specify an API version of 2023-10 or later (2024-01, 2024-04, etc)
  • Make API calls to the new version with no errors

Account admins

Review the applications that are using the API and ensure that you have a migration plan for each:

  • Marketplace applications - It is the app developer's responsibility to update their apps. Most apps will likely not require any workflow changes from you
  • Internally developed applications - Your developers will need to rewrite some of the code to use our new APIs
  • Zapier, Make, and other 3rd party connectors - may require updates, pending implementation by the integration developer

Users of 3rd party integration tools like Zapier and Make

Third-party integration providers like Make, Zapier, and Workato are currently updating their connectors. They will send you communication on what updates your workflows will need.

If you're making direct GraphQL queries with these tools, you will need to review your queries and update them.

Summary of changes

Major changes: These are the changes that will affect most applications and API users.

  1. Queries that use items on the group- or board-level must nest them in the items_page object
  2. Queries that use items_by_column_values must now use items_page_by_column_values
  3. Deprecated: text field returns empty values for mirror, dependency and connect boards columns when using the generic column_values object. You should retrieve this data by querying the display_value field through the relevant implementation (i.e., MirrorValue, DependencyValue, or BoardRelationValue.

Minor changes: These are changes that will affect some applications.

  1. Required argument:column_type argument is now required when creating a column
  2. Type changed: type field on column object is now ColumnType, not string
  3. Type changed: All IDs now use the ID type, not Int
  4. Updated: fields on thecolumn_values object have changed
  5. Deprecated:pos field on boards and columns objects
  6. Deprecated: newest_first argument on boards object. Use the order_by argument instead.

Bug fixes: We fixed some bugs, so bad API calls will now return more descriptive errors:

  1. Empty parentheses will now return an error
  2. String arguments that are not surrounded by quotation marks will return an error

Timeline

July 2023 – version 2023-10 is announced and available for preview

October 2023 – version 2023-10 becomes stable (no new changes), but 2023-07 will still be used by default

15 January 2024 (gradual release) – version 2023-07 is no longer supported, 2024-01 is API default

Common patterns & usage

We've compiled a list of some common patterns used in monday apps and how to migrate each one. Click an option to skip to the relevant section.

  1. Pagination – any queries that return more than 500 items
  2. Apps using the items_by_column_values query
  3. Any query that retrieves the value of Mirror, Connect Boards, or Dependency columns
  4. Apps that return the whole board for in-app filtering and sorting
  5. Apps that query large amounts of board data for aggregation and other data manipulation
  6. Apps using strongly-typed languages
  7. Apps using the create_column mutation

Testing and development

Previewing version 2023-10

You can use the new version of the API in two ways:

  1. Using the version selection button in the API playground (instructions)
  2. Passing the following header in your HTTP requests: API-Version: 2023-10 (instructions)
  3. Using the setApiVersion('2024-01') method in the Javascript SDK (instructions).

Getting help

If you have questions about the new version or the migration process, please let us know in our developer community:

  1. Sign up for an account at community.monday.com
  2. Navigate to the "monday apps & developers" section
  3. Post a new topic

Cursor Pagination

Offset pagination (old method)

In version 2023-07, you would return pages of items like this:

query {
 boards (ids: 4579863192) {
  items (page:1, limit:5) {
   id
  }
 }
}
{
  "data": {
    "boards": [
      {
        "items": [
          {
            "id": "4579863854"
          },
          {
            "id": "4579863873"
          },
          {
            "id": "4579863895"
          },
          {
            "id": "4579863910"
          },
          {
            "id": "4579863934"
          }
        ]
      }
    ]
  },
  "account_id": 1111111
}

Cursor pagination (new method)

In 2023-10, you need to use the new items_page object, which represents a filtered set of items retrieved from the monday database. You can return a cursor value which points to the next page of results and can be used to paginate through large data sets. When there are no more results, the cursor will be null.

query {
  boards (ids: 4579863192) {
    items_page (limit: 5) {
      cursor
      items {
        id
      }
    }
  }
}
{
  "data": {
    "boards": [
      {
        "items_page": {
          "cursor": "MSw0NTc5ODYzMTkyLFRWX2ljOWt2MVpnTjFjelRadUR3Vyw3LDV8MTIyMTY3OTk4OA",
          "items": [
            {
              "id": "4579863854"
            },
            {
              "id": "4579863873"
            },
            {
              "id": "4579863895"
            },
            {
              "id": "4579863910"
            },
            {
              "id": "4579863934"
            }
          ]
        }
      }
    ]
  },
  "account_id": 111111
}

Querying items_page can have a high complexity cost since it must be nested within a boards query. To save on complexity, you can use the next_items_page object at the root of your query and pass the cursor argument to return the next page.

query {
  next_items_page (limit: 5, cursor:"MSw0NTc5ODYzMTkyLFRWX2ljOWt2MVpnTjFjelRadUR3Vyw3LDV8MTIyMTY3OTk4OA") {
    cursor
    items {
      id
    }
  }
}
{
  "data": {
    "next_items_page": {
      "cursor": null,
      "items": [
        {
          "id": "9876543210"
        }
      ]
    }
  },
  "account_id": 1234567
}

Here is a Javascript implementation of pagination with the cursor object and next_items_page:

import mondaySdk from "monday-sdk-js";
const monday = mondaySdk();
monday.setApiVersion('2023-10');

async function itemspageWithLimit() {
  const currentBoardId = 3944151369;

  const firstPage = await monday.api(
    `query {
    	boards (ids: ${currentBoardId}) {
     	  items_page (limit:1) {
 	        cursor
    	    items {
            id
           }
         }
       }
     }`,
    { apiVersion: "2023-10" }
  );
  const firstPageItems = firstPage.data.boards[0].items_page.items;

  var cursor = firstPage.data.boards[0].items_page.cursor;

  while (cursor) {
    // loop will stop when cursor is null
    const nextPage = await monday.api(
      `query {
        next_items_page (limit:1, cursor: "${cursor}") {
          cursor
          items {
            id
          }
        }
      }`,
      { apiVersion: "2023-10" }
    );
    const thisPage = nextPage.data.next_items_page.items; 
    cursor = nextPage.data.next_items_page.cursor;
  }
  console.log("All items retrieved");
}

Migrating the items_by_column_values query

The previous API had an items_by_column_values and items_by_multiple_column_values query to return items that matched a specific column value.

In version 2023-10 you should use the new items_page_by_column_values query(which takes the same data as items_by_column_values). Here's an example that returns items whose status is "Done":

query {
  items_page_by_column_values(
    	board_id:4892484081, 
    	columns:[{column_id: "status", column_values: "Done"}]
  ) {
    items {
      id
    }
  }
}
query {
  complexity {
    query
  }
  items_by_column_values(board_id:4892484081, column_id:"status", column_value:"Done") {
    id
  }
}

Reading the Connect Boards, Dependency, or Mirror column

In the past, you could only retrieve the ID of items that are linked in a Connect Boards or Dependency column. You would need to make another API call to look up the linked items by their ID.

text field will return null, use display_value to get stringified data

In 2023-10, the text field will return null for all Connect Boards, Dependency, and Mirror columns.

Use a frasgment and specify the display_value instead:

query {
  boards (ids:4892484081) {
    items_page(limit:5) {
      items {
        id
        column_values {
          id
          ...on DependencyValue {
            display_value
          }
          ...on MirrorValue {
            display_value
          }
          ...on BoardRelationValue {
            display_value
          }
        }
      }
    }
  }
}

If you want to get really fancy, you can also use an alias to rename display_value to "text", so you can parse the data the same way. This will not work if you use the text field in another part of your query.

query {
  items (ids:872058183) {
    column_values {
      ...on BoardRelationValue {
        text: display_value
      }
    }
  }
}
{
  "data": {
    "items": [
      {
        "column_values": [
          {
            "id": "connect_boards",
            "text": "Item 1, Item 2, Item 3"
          }
        ]
      }
    ]
  },
  "account_id": 1825528
}

Using linked_items to retrieve rich item data

Now you can use the linked_items field to query the linked items in the same API call. Here is an example:

query {
  boards (ids:4892484081) {
    items_page(limit:5) {
      items {
        id
        column_values {
          ...on DependencyValue {
            Dependencies: linked_items {
              name
              id
            }
          }
        }
      }
    }
  }
}
query {
  boards (ids:4892484081) {
    items_page(limit:5) {
      items {
        id
        column_values {
          ...on BoardRelationValue {
            LinkedItems: linked_items {
              name
              id
            }
          }
        }
      }
    }
  }
}

If you're using the Mirror column, now you can use the MirrorValue interface that exposes data on the linked items:

query {
  complexity {
    query
  }
  boards (ids:4892484081) {
    items_page(limit:5) {
      items {
        id
        column_values {
          ...on MirrorValue {
            MirroredItems: mirrored_items {
              linked_board_id
            }
          }
        }
      }
    }
  }
}

Filtering and sorting with items_page

The old API didn't have a robust filtering or sorting capabilities. As a workaround, many applications would retrieve the whole board's data and filter/sort inside their application code.

Now, items_page exposes a rich filtering and sorting layer. You can ask monday to do the sorting for you, and just retrieve the relevant results.

If your application is currently retrieving the board to do its own filtering, you should instead using the filtering available in item_page.

Filtering for a single column value

You can filter for items matching a specific value in a specific column, by adding a set of rules to the query_params argument. This query looks for items that have "Project Alpha" in the "text" column.

query {
  boards (ids:4877868371) {
    items_page (
      query_params: {
        rules: [
          {
            column_id: "text",
            compare_value: ["Project Alpha"],
            operator: any_of
          },
        ],
        operator: or
      },
      
    ) {
      items {
        name
      }
    }
  }
}

Filtering for text matches

You can filter for items that contain a specific substring, using the contains_text operator. This returns all items that contain the string "Project" in the "text" column.

query {
  boards (ids:4877868371) {
    items_page (
      query_params: {
        rules: [
          {
            column_id: "text",
            compare_value: ["Project"],
            operator: contains_text
          },
        ]
      },
      
    ) {
      items {
        name
      }
    }
  }
}

Filtering by date range

You can return items whose dates are between two values, using the between operator:

query {
  boards (ids:4877868371) {
    items_page (
      query_params: {
        rules: [
          {
            column_id: "date4",
            compare_value: ["2023-07-01", "2023-07-30"],
            operator: between
          }
        ] 
      }
    ) {
      items {
        name
      }
    }
  }
}

Filtering for relative dates

You can filter for relative dates using keywords like "TODAY", "YESTERDAY", "ONE_MONTH_FROM_NOW" and more:

query {
  boards (ids:4877868371) {
    items_page (
      query_params: {
        rules: [
          {
            column_id: "date4",
            compare_value: ["TODAY", "YESTERDAY"],
            operator: any_of
          }
        ] 
    	}
    ) {
      items {
        name
      }
    }
  }
}

Filtering for multiple static values

You can return items that match one or more static values by sending a list of items as the compare_value:

query {
  boards (ids:4877868371) {
    items_page (
      query_params: {
        rules: [
          {
            column_id: "text",
            compare_value: ["Vinod", "Susan"],
            operator: any_of
          }
        ] 
      }
    ) {
      items {
        name
      }
    }
  }
}

Multiple filter rules

You can filter for multiple rules at a time. Notice each rule has an operator, and the query as a whole has one too:

query {
  boards (ids:4877868371) {
    items_page (
      query_params: {
        rules: [
          {
            column_id: "text",
            compare_value: ["Vinod"],
            operator: any_of
          },
          {
            column_id: "date4",
            compare_value: ["TODAY"],
            operator: any_of
          }
        ],
        operator: or
      },
      
    ) {
      items {
        name
      }
    }
  }
}

Sorting by a single column

You can sort the item set by its column values, including with mutliple sort criteria (subsorts):

query {
  boards (ids:4877868371) {
    items_page(query_params: {
      order_by: [
        {column_id:"numbers_1", direction: asc}
      ]
    }) {
      items {
        id
        name
      }
    }
  }
}

Sorting multiple columns (subsorts)

You can also sort by multiple columns. The sequence of the OrderBy arguments will determine which sort is applied first.

query {
  boards (ids:4877868371) {
    items_page(query_params: {
      order_by: [
        {column_id:"status", direction: desc},
        {column_id:"numbers", direction: asc}
      ]
    }) {
      items {
        id
      }
    }
  }
}

Returning the whole board for data aggregation

The items_page object will return a maximum of 500 items at a time. If you need to get all the data from a single board, you will need to use cursor-based pagination.

You can see some examples of using cursor pagination in section 1 of this guide: Cursor Pagination


Apps written in any strongly-typed language - TypeScript, Python, Java, etc.

Version 2023-10 has some type changes to improve the consistency of our GraphQL API.

Specifically, we aligned our schema so all fields representing numerical IDs now use the ID type. As a return value, it's expressed as a string. When used in an argument (input), it accepts either string or integer values.

If your app is strongly typed, your code may throw an error when this type changes because your application will expect an integer but receive a string.

To solve this, please review your API calls to check if you use any affected fields. Then, ensure your code accepts both integer and string values.

Note: Even though you could convert the strings to integers, we don't recommend it, as the structure of these IDs may change in the future.


Using the create_column mutation

The create_column mutation now takes a column_type argument that specifies what kind of column is created.

In 2023-07, the column type argument was a string and inconsistent between query and mutations. For example, querying the checkbox column would return Checkbox as its type, but you'd need to send boolean as the column type to create it.

In 2023-10, we've aligned all the column types into consistent enum values.

Apps that used the string value in the past now need to use the enum value. We've compiled this table that lists every column and its corresponding string and enum values: Columns migration reference