# Use the YouTrack JavaScript API

The `@jetbrains/youtrack-scripting-api` package is shared by workflow rules and supported backend app modules. Use the examples on this page to work with entities and collections exposed by this API. Most examples use a workflow rule context, but the entity operations are the same in other backend modules when the corresponding entity is available.

## Where to Write Backend Scripts

Choose the development flow that matches the module you are building.

* To create a workflow and add modules in YouTrack, use the [web-based workflow editor](Web-based-Workflow-Editor.html). For a guided example, see [JavaScript Workflow Quick Start Guide](Quick-Start-Guide-Workflows-JS.html).

* To write workflow packages in an IDE, install the scripting packages and upload the workflow to YouTrack. For setup instructions, see [Using an External Code Editor](js-workflow-external-editor.html).

* To build HTTP handlers, custom MCP tools, or packages that combine backend and frontend modules, use the [app development flow](apps-quick-start-guide.html).

## Accessing Properties, Custom Fields, and Issue Links

Follow these guidelines to access issue properties, values in custom fields, and issue links.

Predefined Issue Fields
: Predefined issue fields like `summary`, `description`, and `reporter` are all properties of the `issue` entity.
:
:
:
:
:
:
: ```JAVASCRIPT
: const summary = issue.summary;
: issue.summary = "Just a bug";
: ```

Custom Field Values
: References to values in a custom field vary based on whether the field stores single or multiple values.
:
:
:
: * For fields that store single values, you can reference the value directly: ```JAVASCRIPT const state = issue.fields.State; if (state && issue.fields.is(ctx.State, ctx.State.Fixed)) { // Do stuff } issue.fields.State = ctx.State.Open; ```
:
: * For fields that store multiple values, the values are stored as a set. The operations that are available for sets are described in more detail in the next section. ```JAVASCRIPT const versions = issue.fields["Fix versions"]; versions.forEach(function (v) { subtask.fields["Fix versions"].add(v); }); ```
:
:
:
: When you need to inspect the field definition itself, get the `ProjectCustomField` from the project and read its `typeName`. Multi-value custom field types use the `[*]` suffix, for example `user[*]` or `version[*]`. Single-value bundle, user, and group field types use `[1]`; scalar field types such as `integer` or `string` do not use a cardinality suffix.
:
:
:
:
: ```JAVASCRIPT
: const assigneeField = issue.project.findFieldByName('Assignee');
: if (assigneeField && assigneeField.typeName.endsWith('[*]')) {
: // Assignee stores multiple users in this project.
: }
: ```
:
:
:
: This runtime property is different from the `multi` property in [rule requirements](requirements.html). Use `multi: true` when a rule requires a field to store multiple values; use `typeName` when a rule needs to inspect an existing field.
:
:
:
: > **Note:**
: > Custom fields that contain spaces or other non-alphabetic characters must be set in quotation marks and brackets.
:
:
:
: When you compare custom field values that are represented by Workflow API entities, use API helper methods instead of comparing display names or object references. The `issue.fields.is(ctx.Field, ctx.Field.Value)` method checks the current value, `issue.fields.was(ctx.Field, ctx.Field.Value)` checks the previous value, and `issue.fields.becomes(ctx.Field, ctx.Field.Value)` returns `true` only when the field changed in the current transaction and its new value matches the expected value.

Aliases
: If you set the `alias` property for a custom field in the `requirements` statement, you can use this alias to access values for the field as well. For example, if the alias for the Fix versions field is set to `FV`:
:
:
:
:
: ```JAVASCRIPT
: issue.fields.FV.forEach(function (v) {
: // Do stuff
: });
: ```
:
:
:
: > **Note:**
: > Aliases only work for the issue that's accessible as `ctx.issue`. To access a custom field for a newly created issue, refer to it as `newIssue.fields['Custom Field']`.

Issue Links
: Issue link types are accessed by their inward or outward name. For example, `relates to` or `parent for`. Issue links are always treated as a set.
:
:
:
:
: ```JAVASCRIPT
: const parent = issue.links["subtask of"].first();
: parent.links["parent for"].add(issue);
: ```
:
:
:
: > **Note:**
: > Issue link types that contain spaces or other non-alphabetic characters must be set in quotation marks and brackets.

Note that custom fields and issue link types that contain spaces or other non-alphabetic characters must be set in quotation marks and brackets.

## Working with Set Objects

There are several groups of operations you can do with multiple values (returned as Set<value type>):

* Access them directly (`first()`, `last()`, `get(index)`) or by iterator (`entries()`, `values()`).

* Traverse over all values in Set with `forEach(visitor)`.

* Look for values with `find(predicate)` and check if a value is in Set with `has(value)`.

* Check size with `isEmpty()`, `isNotEmpty()` and `size` property.

* Modify content with `add(element)`, `delete(element)` and `clear()`.

* Get the current changes for a Set object with the `added`, `deleted` and `isChanged` properties.

For a detailed description of this object, see [Set](v1-Set.html).

## Calling Methods

```JAVASCRIPT
// Call entity method:
const stateCF = issue.project.findFieldByName('State');
// Call static method:
const p = entities.Project.findByKey('INT');
// Call issue constructor:
const newIssue = new entities.Issue(ctx.currentUser, issue.project, "Subtask");
```

## Finding Specific Entities

There are two ways to find a specific entity, like an issue or a user, and use it in a workflow script:

1. Add it in the [requirements](requirements.html) and reference it in the [context](context.html). Use this approach as often as you can, as it is the most reliable. If the specified entity is not found in your database, the script is not executed.

2. If the first option is not applicable for whatever reason, use the `findBy*` and `find*By*` methods from the workflow API:

Use `findBy*` methods to find a single occurrence of a specific entity:

```JAVASCRIPT
const issue = entities.Issue.findById('MP-23'); // an entities.Issue or null
const projectByName = entities.Project.findByName('Music Production'); // an entities.Project or null
const projectByKey = entities.Project.findByKey('MP'); // an entities.Project or null
const user = entities.User.findByLogin('jane.smith'); // an entities.User or null
const userGroup = entities.UserGroup.findByName('MP team'); // an entities.UserGroup or null
const agiles = entities.Agile.findByName('MP Scrum'); // a Set of entities.Agile
const tags = entities.IssueTag.findByName('production'); // a Set of entities.IssueTag
const queries = entities.SavedQuery.findByName('MP Backlog'); // a Set of entities.SavedQuery
```

Use `find*By*` methods to find child entities:

```JAVASCRIPT
const sprint = agiles.first().findSprintByName('Sprint 23'); // an entities.Sprint or null
const priorityField = projectByKey.findFieldByName('Priority'); // an entities.ProjectCustomField or null
const major = field.findValueByName('Major'); // an entities.Field or null
const critical = field.findValueByOrdinal(1); // an entities.Field or null
const assigneeField = projectByKey.findFieldByName('Assignee');
const jane = assigneeField.findValueByLogin('jane.smith'); // an entities.User or null
const groupField = projectByKey.findFieldByName('Requestors');
const newBand = groupField.findValueByName('New Band'); // an entities.UserGroup or null
```

## Finding Multiple Issues

Sometimes you might want to find a set of issues that match certain criteria and process them in the scope of a single rule. For example, you can compile a list of issues and send it as an email message. In these situations, the [search](v1-search.html) API can help. Here's a simple example:

```JAVASCRIPT
const entities = require('@jetbrains/youtrack-scripting-api/entities');
const search = require('@jetbrains/youtrack-scripting-api/search');
const workflow = require('@jetbrains/youtrack-scripting-api/workflow');

exports.rule = entities.Issue.onChange({
  title: 'Do not allow developers to have more than 1 issue in progress per project',
  action: (ctx) => {
    const issue = ctx.issue;
    if (issue.isReported &&
        (issue.fields.becomes(ctx.State, ctx.State['In Progress']) ||
            issue.fields.isChanged(ctx.Assignee)) &&
        (issue.fields.Assignee || {}).login === ctx.currentUser.login) {
      // First, we build a search query that checks the project that the issue belongs to and returns all the issues
      // that are assigned to current user except for this issue.
      const query = 'for: me State: {In Progress} issue id: -' + issue.id;
      const inProgress = search.search(issue.project, query, ctx.currentUser);
      // If any issues are found, we get the first one and warn the user.
      if (inProgress.isNotEmpty()) {
        const otherIssue = inProgress.first();
        const message = 'Dear ' + ctx.currentUser.login + ', please close <a href="' +
            otherIssue.url + '">' + otherIssue.id + '</a> first!';
        workflow.check(false, message);
      }
    }
  },
  requirements: {
    State: {
      type: entities.State.fieldType,
      'In Progress': {}
    },
    Assignee: {
      type: entities.User.fieldType
    }
  }
});
```

## Debugging Workflow Scripts

To inspect values while a workflow rule runs, add logging statements to the script:

```JAVASCRIPT
console.log('Processing issue ' + ctx.issue.id);
console.warn('Expected value was not found');
console.error('External request failed');
```

Workflow script messages are displayed in the Console pane of the workflow editor, not in the browser developer console. For YouTrack Server installations, these messages are also written to the `workflow.log` file.

For more information about the console, see [Console Toolbar](Web-based-Workflow-Editor.html#workflow-editor-console-toolbar). For additional diagnostic guidance, see [Troubleshooting Workflows](troubleshooting-workflows.html).

