# Comfortable Documentation

## Welcome to Comfortable 👋

#### Comfortable is a headless CMS that enables fast and easy content editing for websites and apps.

🙋‍♂️[Follow Comfortable on Twitter](https://twitter.com/cmftable) to stay informed about updates and new features

🛠[Check out GitHub](https://github.com/cmftable) for examples and SDKs

## Getting started&#x20;

This introduction will get you a quick overview of the basic things to know, when working with Comfortable.

### Accounts

To get started you'll need to [create an account](https://app.comfortable.io/signup). Each account can have repositories for themselves or can be invited to join and collaborate within foreign repositories at an appropriate access level.

### Repositories

Spaces for projects are called repositories. A repository is the place where you keep all the content related to a single project.

### Documents

Each [document](/concepts-1/documents) represents a content entity. That might be a page, a product, some content element, or anything else.&#x20;

### Document Types

[Document types](/concepts-1/document-types) define reusable schemas for documents. You could for example create a document type called `Page`, `Product` or `Article`.

### Collections&#x20;

[Collections](/concepts-1/collections) are powerful filters for documents that can be used as endpoints and be managed through the web interface. Create lists of products, categories, display articles or elements of a section of your site, collect dates, or aggregate sets of data. Do almost anything you would do with client-side filtering, but keep the content accessible and comprehensible for your editors.

### Content Tree

The[ Content Tree](/concepts-1/content-tree) holds all of the collections and linked documents to provide developers and editors with structure and orientation.


# Content Repositories

A Content Repository is a separate space with an individual set of documents, assets, users, etc. You can create as many repositories as you like. Usually one per project.

All of your repositories are listed on the dashboard page. From there you can quickly switch between different projects.

{% hint style="success" %}
A user can see any repository in his dashboard he's associated with. He will not be able to see repositories he's not assigned to as collaborator.
{% endhint %}

## Creating a Repository

Content repositories can be created from the dashboard page by clicking the 'Create New Repository' Button. Follow the provided steps on the next pages to complete the information for your new repository.

You can choose different plans for a repository, depending how much users, languages or performance you need. [Take look at the pricing page](https://comfortable.io/pricing) or [contact us](mailto:support@cmft.io) to get help choosing the right plan.

## Deleting a Repository

A repository can be deleted by navigating to the 'Settings' Page. You'll find a 'Delete Repository'-Button in the 'General' Section.

{% hint style="success" %}
Deleting a repository will automatically cancel a paid subscription
{% endhint %}

## Administration

The person who creates a repository will be assigned to the user role `Owner`. This role has all permissions and can invite other users like Administrators or Developers.

{% hint style="info" %}
A repository owner can not be removed from a project. [Contact support](mailto:support@cmft.io) if you need an owner transfer.
{% endhint %}

{% hint style="info" %}
Only the repository owner can provide payment details and make changes to the plan subscription.
{% endhint %}


# Content Types

Content types, define reusable schemas for documents. You can for example create a content type called `Page` or `Product` to re-use a certain set of fields.

## Adding Content Types

To create a content type:

1. Navigate to the page 'Content Types' by clicking the double-square icon in the menu bar on the left.
2. On this page you'll find a 'Create Type'-Button at the top right corner.
3. Enter `Name` and `Api-Id` and click 'Save'*.*
4. Now you can start editing your new content type.

## Editing Content Types

On the page 'Content Types' you'll see an overview of existing content types. You can simply click one of them to enter the edit mode and to add and remove fields or to change their configuration.

{% hint style="warning" %}
If you remove a field with existing content, the content of these fields will be **deleted**.
{% endhint %}

### Field Types

| **Name**    | **Options**                  | **Validation**                              | **Translatable** | **API Output**                                        |
| ----------- | ---------------------------- | ------------------------------------------- | ---------------- | ----------------------------------------------------- |
| Text        | Single Line, Multi Line      | Required, E-Mail, Min. length, Max. length  | ✔️               | `string`                                              |
| Richtext    | –                            | Required, Min. length, Max. length          | ✔️               | `[plain, html]`                                       |
| Markdown    | –                            | Required                                    | ✔️               | `string`                                              |
| Number      | Integer, Decimal             | Required, Min. value, Max. value            | ✔️               | `number`                                              |
| Select      | Select, Radio, Checkboxes    | Required                                    | ✔️               | `string\|array`                                       |
| Assets      | –                            | Required, Min. count, Max. count, Filetypes | ✔️               | `array`                                               |
| Date & Time | Date, Date & Time, Time only | Required                                    | ✖️               | <p><code>string</code> <br><em>ISO 8601, UTC</em></p> |
| Relation    | One to one, One to many      | Required, Min. count, Max. count            | ✖️               | `object\|array`                                       |
| Location    | –                            | Required                                    | ✖️               | `object`                                              |
| Boolean     | –                            | –                                           | ✖️               | `boolean`                                             |
| JSON        | –                            | –                                           | ✖️               | `object`                                              |
| Color       | –                            | Required                                    | ✖️               | `string`                                              |

#### Select Field Configuration

Fields for `select`, `radio` or `checkbox` fields can be configured in a multiline textfield.

#### **Pattern**

`label|value|option`

**1st Segment:** `label`\
The label name for the field.

**2nd Segment:** `value`\
Defines the value for the field.

{% hint style="info" %}
If no value is defined, the value will be the label name.
{% endhint %}

**3rd Segment:** `option`\
Options are optional and must be prefixed with underscore *`_`*

The following options are available:

| **Option**  | **Purpose**                                                                              |
| ----------- | ---------------------------------------------------------------------------------------- |
| `_default`  | Defines which values are selected by default. Can be used multiple times for checkboxes. |
| `_disabled` | Disables a field.                                                                        |

**Separation**

`label|value|option` must be separated by pipe: `|`

![Select field configuration example](/files/-LBQI5-HPxySomVPx-8V)

### Main Field

The Main Field is a special attribute that can be be applied for `text`, `richtext`, `number` and `select` fields. Its main purpose is to provide a identifier for documents to help you navigating faster through lists. For example, for the Content Type `Author` you would probably choose the field `Name` to be the Main Field.

{% hint style="info" %}
A Main Field is always required. If you choose a field to be the Main Field, it will be set to be required automatically.
{% endhint %}

### Sections

Sections can be used to group a set of fields. This is useful for editors and comes handy if you need to encapsulate some fields for the REST API.

{% code title="Sections Example Document" %}

```javascript
...,
"fields": {
    "title": "Garret Ginner Pro",
    "type": "skateboard",
    "description": {
        "text": "...",
        "dimensions": "...",
        "type_of_wood": "..."
    },
    ...
},
...
```

{% endcode %}

We are planning to make sections repeatable soon. This will enable your editors to create multiple instances of a field group. A good example for this would be a band tour with multiple locations.

### Translation

To enable a field to be available for translation, just select the checkbox *Translatable* on the edit- or create field screen and save the configuration.

The translatable fields will appear in the [Writing Room](/concepts-1/documents) and can be accessed by the language tabs.

## Deleting Content Types

1. Navigate to the page 'Content Types' by clicking the double-square icon in the menu bar on the left.
2. Select a content type by activating the checkbox.
3. Click the 'Trashcan'-Butto&#x6E;*.*
4. Confirm.

{% hint style="warning" %}
Deleting a `content type` **will also delete all documents** of this type!
{% endhint %}


# Documents

Documents are content objects and represent an individual entity of data, e.g. a page, a product a single element of a website or a data object for a list. The schema of a `document` is defined in a [content type](/concepts-1/document-types).

## Adding Documents

At the top right corner of the webapp, you'll find a button with a 'Plus'-icon. On the dropdown menu you can choose from which type you want to create a document.

![](/files/-LAI0LXlsB5WpqH31NfQ)

## Editing Documents

### The Writing Room

This is the place where editors compose and edit documents. It consists of two main parts, the [Content Area ](/concepts-1/documents#content-area)and the [Sidebar](/concepts-1/documents#sidebar)*.*

### Content Area

All of the fields defined for a `content type` appear on the left, main part of the Writing Room screen:

![](/files/-LCZ7Q6f6bguPRg5Lp22)

### Sidebar

The sidebar gives you overview and control over a document:

![](/files/-LCZ7ofhsUlnntgDRZ1I)

### Revisions

With revisions you can review and restore changes or versions of a document. Click the tab 'Revisions' to view and choose from different versions of your content.

You can preview and restore different versions of a document. If the document is published, you can decide to restore a version as draft.

### Drafts

You can create drafts for published documents if the content needs to be reviewed before it gets published, or if you are being interrupted while updating a document.

{% hint style="info" %}
Draft mode is available when a document has the `published` state.
{% endhint %}

### Publishing

#### Switch Button

Control wether a document is published or unpublished by clicking the desired state.

{% hint style="info" %}
**Save** the document for the publishing state to take effect.
{% endhint %}

#### Scheduled Publishing

Documents, e.g. an event, can be published automatically at a certain point in time. They can also be set to unpublished at a date or time, e.g. a few days after the event was over. \
Leave the field blank if you do not want to enable an option.

If you'd like to remove a document from [collections](/concepts-1/collections) after it was unpublished, simply enable the checkbox 'Unpublish and move to Archive'.

![](/files/-LAI43Q_D2nViZhJfXYO)

### Tags

Tags are used to describe a documents content or purpose. They may also be used as a filter argument to include or exclude documents from collections.

### Languages

You can filter translatable fields of a language by selecting the corresponding tab on the top of the Writing Room. [Locales](/concepts-1/locales) can be managed in the settings area of a repositor&#x79;*.*

![The language tabs](/files/-LANJdXxhHFmqNmTrbV2)

## Deleting Documents

To delete a document you'll need to **move it to the Archive first**. You can do this by either (1) choosing it from any [collection](/concepts-1/collections) or the `All Documents` view by selecting the corresponding checkbox and clicking the 'Trashcan'-Butto&#x6E;*.* Or (2) while in the `Writing Room`, open the Save Buttons context menu on the [sidebar](/concepts-1/documents#sidebar) and click 'Move to Archive'.

The `Archive` collects all of the documents you decided to move their manually, or automatically at a certain point of time, configured in the [Writing Room](/concepts-1/documents#the-writing-room).\
\
**To delete a document permanently:**

1. Navigate to 'Archive' by clicking 'Document Archive' on the bottom of the [Content Tree](/concepts-1/content-tree).
2. Select documents by clicking their checkbox.
3. Once you have selected the documents you want to delete, click the 'Trashcan' Button.
4. Confirm.


# Assets

## Uploading Assets

To make assets available for documents, you can upload them on several places on the web platform.

Use the 'Upload-Asset'-Button on the top right corner of the webapp to access the upload dialog at any time.

![](/files/-LAJFOvMjYGOA6_EOMso)

Or upload an `asset` at time you want to link it in a `document` and select if from the list after the upload has finished:

![](/files/-LAJGzTYcSbp0qLZbWKv)

## Managing and Editing Assets

A list of all uploaded assets is available on the Assets page.

### Fields

Assets are quite similar to documents and have pre-defined fields:

| **Field**                | Editable |
| ------------------------ | -------- |
| Title                    | ✔️       |
| Description              | ✔️       |
| Publish                  | ✔️       |
| Unpublish                | ✔️       |
| Tags                     | ✔️       |
| File                     | ✖️       |
| Filename                 | ✖️       |
| File link (permalink)    | ✖️       |
| Type                     | ✖️       |
| Size                     | ✖️       |
| Dimensions (images only) | ✖️       |
| Created                  | ✖️       |
| Created by               | ✖️       |
| Last modified            | ✖️       |
| Last modified by         | ✖️       |

### Publishing

Assets can be published or unpublished manually or automatically, [just like documents](/concepts-1/documents#the-writing-room). That means, that event if an asset is linked in a published document, it will not be available by API until its state is `Published`.

## Deleting Assets

To delete an asset you can do this by either (1) choosing it from any [collection](https://comfortable.gitbook.io/documentation/~/drafts/-LADZaBiruP_tReVjCY4/primary/concepts/collections) or the 'All Documents' View by selecting the corresponding checkbox and clicking the 'Trashcan'-Butto&#x6E;*.* Or (2) while in the single view, open the 'Save'-Buttons context menu on the [sidebar](https://comfortable.gitbook.io/documentation/~/drafts/-LADZaBiruP_tReVjCY4/primary/concepts/documents#sidebar) and click 'Move to Archive'.


# Content Tree

The Content Tree helps to navigate a repos content and gives some structure to the backend. It can include [Collections](/concepts-1/collections), Folders and Document Links that **can be nested** to reflect a certain logic or structure and help editors to recognize parts of a website or app, or simply provide lists and links for easier content management.

{% hint style="info" %}
Each node in the tree has its own endpoint with a unique URL slug (API-Id) for HTTP calls. However, nesting does not affect the endpoint URL.
{% endhint %}

## Node Types

The following types of tree nodes are available:

### Collections

`Collections` are aggregated lists of `documents` that match a certain definition. For more info, [see the collections page](/concepts-1/collections).

![](/files/-LAJZfeJ6tnzJMr_6_5k)

### Document Links

`Document Links` are single `documents` linked in the tree. They may represent a unique or important document, for example a landingpage or an important document that will be constantly changed by editors.

![](/files/-LAJWYKp2iwZci3QnnSR)

### Folders

`Folders` can be used to group collections or document links. By now this is their only function and they have no endpoint, but there are some features planned for the future.

![](/files/-LAJ_Q4xHyZmwf9Jb-BY)

## Nodes

### Adding nodes

To add a node, simply click or drag the corresponding icon above the tree and enter a **name** and `API-Id`. For Document Links you can select the document to be linked from the list on the bottom.

### Editing nodes

**Folders and Document Links** can be edited by right-clicking a node and selecting 'Edit'. This will bring up a dialog in which you can change the name, API-Id or the linked document.

**Collections** have their own configuration page. Right-click a node and select 'Edit' or enter the collection view by left-click and find the 'Configuration'-Button on the top right.

### Removing nodes

You can either right-click a node an select 'Delete', or switch into edit mode to delete a node.

{% hint style="warning" %}
Be careful when deleting a node, this will also delete the URL endpoint!
{% endhint %}

## Document Archive

On the bottom of the Content Tree you will find the  `Document Archive`. It collects all archived `documents`. In the archive you select documents to delete them permanently.

![](/files/-LANiTjmrILG8pCpRWWh)


# Collections

With collections you can define a set of filters that is applied to all documents in a repository. The result is a subset of documents that match these rules.

Create lists of products, categories, display articles or elements of a section of your site, collect dates or aggregate sets of data.

Your collections are displayed in the [Content Tree](/concepts-1/content-tree). When you click on a collection node, a list of documents matching the collection query will be displayed. The list will be constantly updated as you're adding, removing or making changes to documents.

This makes it easy for editors to recognize and manage content or certain parts of their website or application.

To create complex collections or dynamic filters inside your application, take a look at the [API filters](/apis/rest-api/filters) documentation.

## Collection Endpoints

Each collection provides a REST endpoint to use in your application. You can also add additional or override existing filters when making the request.

For more info see [collection endpoints](/apis/rest-api/endpoints#collections) or [API filters](/apis/rest-api/filters).

{% hint style="success" %}
Use the **API Explorer** tab to inspect a collections result.
{% endhint %}

## Adding Collections

1. To add a collection you'll need to add a `collection node` to the [Content Tree](/concepts-1/content-tree) by clicking or dragging the icon above the tree.
2. Enter a name for the collection (this will also be the name of the tree node) and an `API-Id` to define a `URL slug` for the API endpoint.

![](/files/-LANzUbTwnb6fhg7xVEm)

After saving, a node will be added to the Content Tree that can be moved or nested anywhere you like. For the API a collection is always unique by its URL slug and it makes no difference where the node is located in the tree.

## Configuration

1. Right-click the collection node in the tree
2. Click 'Configuration'*.*

Or:

1. Switch to the `collection view` by clicking the tree node.
2. Click the 'Configuration'-Button on the right top.

### Content Type

Choose the type of document the collection rules will be applied to. For multiple types, have a look at [API filters](/apis/rest-api/filters).

### Include Tags

Add documents to the collections that have the given `tags`.

### Exclude Tags

Exclude documents from the collections that have given `tags`.

### Filters and Conditions

Filters are conditional expressions and can be used like you would write an if statement. You can also use **multiple conditions** by using `AND` and `OR`.&#x20;

The following field types and operators can be used to create a condition:

| **Field Type**           | **Operators**                                                                                          |
| ------------------------ | ------------------------------------------------------------------------------------------------------ |
| `text`                   | **=**, **!=**, **IN**, **LIKE**, **NOT LIKE**, **EMPTY**, **NOT EMPTY**                                |
| `richtext`               | **=**, **!=**, **IN**, **LIKE**, **NOT LIKE**, **EMPTY**, **NOT EMPTY**                                |
| `number`                 | **=**, **!=**, **IN**, **LIKE**, **NOT LIKE**, **EMPTY**, **NOT EMPTY**, **>**, **>=**, **<** , **<=** |
| `date`/`time`/`datetime` | **=** , **!=**, **IN**, **LIKE**, **NOT LIKE**, **EMPTY**, **NOT EMPTY**, **>**, **>=**, **<**, **<=** |
| `boolean`                | **=**                                                                                                  |
| `select`                 | **=,** **!=, IN,** **LIKE**, **NOT LIKE**, **EMPTY**, **NOT EMPTY**, **>**, **>=**, **<**, **<=**      |
| `color`                  | **=,** **!=**, **IN**, **EMPTY**, **NOT EMPTY**                                                        |
| `checkbox`               | **IN, NOT IN, EMPTY, NOT EMPTY**                                                                       |

### Operators

Most operators are self-explaining if you are familiar with logical operators. However, here is a quick reference.

| **Operator** | **Convention**   | **Explanation**                                                                                          |
| ------------ | ---------------- | -------------------------------------------------------------------------------------------------------- |
| `=`          | equal            | Validates true for a matching value                                                                      |
| `!=`         | not equal        | Validates true for non-matching values                                                                   |
| `>`          | greater than     | Validates true if the field value is greater than the specified value                                    |
| `>=`         | greater or equal | Validates true if the field value is greater or equal to the specified value                             |
| `<`          | lower than       | Validates true if the field value is lower than the specified value                                      |
| `<=`         | lower or equal   | Validates true if the field value is lower or equal to the specified value                               |
| `LIKE`       | like             | Validates true if a field value matches. Possible usage: `startsWith%`, `%endsWith`, `%contains%`        |
| `NOT LIKE`   | not like         | Validates true if a field value does not match. Possible usage: `startsWith%`, `%endsWith`, `%contains%` |
| `EMPTY`      | empty            | Validates true if the field value is empty                                                               |
| `NOT EMPTY`  | not empty        | Validates true if the field value is not empty                                                           |
| `IN`         | in               | Validates true if a value exists in an array                                                             |
| `NOT IN`     | not in           | Validates true if a value does not exist in an array                                                     |

### Sorting

To order the list of documents, choose one or multiple fields and a direction for each field.

Field types available for sorting:

* `text`
* `richtext`
* `number`
* `date/time/datetime`
* `boolean`
* `location`
* `select`
* `color`

## Deleting Collections

1. Switch to the `collection view` by clicking the tree node.
2. Click the 'Configuration'-Butto&#x6E;*.*
3. Find and click the 'Delete Collection'-Button on the bottom of the configuration page.
4. Confirm.

This will remove the collection node from the tree and disable the endpoint.

{% hint style="success" %}
Deleting a collection will not delete any documents.
{% endhint %}


# Webhooks

You can subscribe to system events to make HTTP requests and trigger callbacks in your own application.

## Adding Webhooks

1. Navigate to the 'Settings' Page and select the tab *'*&#x57;ebhooks'
2. Click the 'Create New Webhook'-Button
3. You will be redirected to a new page to configure your Webhook

## Configuration

### Name

The `name` field only purpose is to identify it in the list view.

### Url

The `URL` to deliver requests to.

### Headers

You can add custom headers by defining a `key` and a `value`*.* There is no limit for the number of headers.

### Hooks

| **Source**       | **Create** | **Save** | **Delete** | **Publish** | **Unpublish** | **Archive** | **Unarchive** |
| ---------------- | ---------- | -------- | ---------- | ----------- | ------------- | ----------- | ------------- |
| **Document**     | ✔️         | ✔️       | ✔️         | ✔️          | ✔️            | ✔️          | ✔️            |
| **Asset**        | ✔️         | ✔️       | ✔️         | ✔️          | ✔️            | ✖️          | ✖️            |
| **Content Type** | ✔️         | ✔️       | ✔️         | ✖️          | ✖️            | ✖️          | ✖️            |
| **Collection**   | ✔️         | ✔️       | ✔️         | ✖️          | ✖️            | ✖️          | ✖️            |

## Deleting Webhooks

1. Navigate to the 'Settings' Page and select the tab *'*&#x57;ebhooks'.
2. Use the 'Pencil'-Button in a Webhook row to switch to the edit view.
3. Click the 'Delete Webhook'-Button and confirm.


# Team

## Users

Each account represents an unique user who is identified by his E-Mail Address. Users can create repositories and invite other users to collaborate within a repo, or be invited themselves. \
All repositories connected to an account are displayed on the personal dashboard page.

### Roles

| **Role name** | **Description**                                                                 |
| ------------- | ------------------------------------------------------------------------------- |
| `Owner`       | The user who created the repository, has full access.                           |
| `Admin`       | Can manage everything within a repo, except for owner transfers.                |
| `Developer`   | Can manage everything within a repo, except for owner transfers.                |
| `Editor`      | An Editor can only access and manage content objects like Documents and Assets. |

*More granular access settings and custom roles are yet to come. Stay tuned!*

### Inviting Users

1. Navigate to the 'Team' page by clicking the 'Team'-Icon in the menu bar on the left.&#x20;
2. Click the 'Add Member'-Button and enter the E-Mail Address of the user you want to invite.
3. Select a role for the user.
4. Click the 'Invite'-Button.

If a user ***has already registered an account***, an E-Mail will be sent to inform him that he was granted access to a repository. The repository will be visible on his personal dashboard page.

If a user ***has no account***, he will receive an E-Mail that informs him that he was invited to join a repository. The E-Mail includes a link to complete his profile. Once a user has set a password and completed his  details, the account is operational and he is being added to the repo.

{% hint style="info" %}
An invitation will be revoked and the incomplete user profile will be deleted automatically after 7 days without confirmation.
{% endhint %}

### Revoking Access

To remove one or more users from a repository:

1. Navigate to the 'Team' page by clicking the 'Team'-Icon in the menu bar on the left.
2. Select a user by clicking the checkbox beneath the account row.
3. Click the 'Trashcan'-Butto&#x6E;*.*
4. Confirm.

The changes will take effect immediately.


# Locales

Locales define all the languages available in your repository. You can add, edit their display name or delete them in the repository setting&#x73;*.*

{% hint style="info" %}
The option 'Translatable' can be enabled **per-field** in content type settings.
{% endhint %}

## Adding Locales

1. Navigate to the page 'General Settings' by clicking the 'Settings'-Icon in the menu bar on the left.&#x20;
2. Use the 'Add Language'-Button to add a language to the list.
3. Choose the `Language Key`*,* e.g. `en_US`.
4. Type in a name that will be displayed in the [language tabs of the writing room](/concepts-1/documents#editing-documents).
5. *Optional:* Set a fallback language. In case there is no translation given for a particular field, the fallback language will be used for output instead.
6. Save by clicking the 'Checkmark'-Butto&#x6E;*.*

## Editing Locales

1. Navigate to the 'General Settings' page by clicking the 'Settings'-Icon in the menu bar on the left.&#x20;
2. Click the 'Pencil'-Button in a language row to switch to the edit view.
3. Make your changes.
4. Save by clicking the 'Checkmark'-Butto&#x6E;*.*

## Deleting Locales

1. Navigate to the 'General Settings' page by clicking the 'Settings'-Icon in the menu bar on the left.&#x20;
2. Click the 'Pencil'-Button in a language row to switch to the edit view.
3. Click the 'Trashcan'-Button in a language row.
4. Confirm.

{% hint style="danger" %}
When deleting a locale, all content of this language will be deleted permanently.
{% endhint %}


# RESTful API

## Introduction

#### API Base URL

{% code title="" %}

```http
https://api.cmft.io/v1/
```

{% endcode %}

#### Basic Query Structure

{% tabs %}
{% tab title="URL" %}

```bash
https://api.cmft.io/v1/<repoName>/<endpoint>?key=<apiKey>
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl 'https://api.cmft.io/v1/<repoName>/<endpoint>' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
window.fetch('https://api.cmft.io/v1/<repoName>/<endpoint>', {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}
{% endtabs %}

#### Example Document Structure

```javascript
{
  "fields": {
    "title": "Roses are red"
  },
  "meta": {
    "id": "314159265358979323",
    "contentType": "poem",
    "repository": "314159265358979323",
    "revision": 1,
    "tags": [],
    "createdAt": "2018-01-01T00:00:00.000Z",
    "updatedAt": "2018-01-01T00:00:00.000Z"
  }
}
```

## API Keys

The API Key is mandatory and can be included in the `Authorization Header` or the `URL` for `GET` requests. One key is always valid for the associated repository.

### Creating API Keys

By default there will be an auto-generated API Key for your repository. However, you can always create additional keys.

1. Navigate to the 'Settings' page and select the tab 'API Keys'.
2. On the field below,
3. Click the 'Create New Key'-Button.

### Editing

The keys are generated by our API, therefore you can only change the name you use to describe a keys purpose.

1. Navigate to the 'Settings' page and select the tab 'API Keys'.
2. Click the 'Pencil'-Button when hovering a key in the list.
3. Make your changes.
4. Confirm by clicking the 'Checkmark'-Button

### Revoking or Deleting

You can delete any additional API Keys.

1. Navigate to the 'Settings' Page and select the tab 'API Keys'.
2. Click the 'Pencil'-Button when hovering a key in the list.
3. Click the 'Trashcan'-Butto&#x6E;*.*
4. Confirm.

### Types of Keys

Currently there are just `read-only` keys. Once we've published the write API, we're going to offer different types of keys.

### Usage

{% hint style="info" %}
The API Key can be either included as Query Parameter in the URL or in the Authorization Header.\
However when making `POST` requests, the key must be provided in the **Authorization Header**.&#x20;
{% endhint %}


# Endpoints

## All Documents

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/<repoName>/documents`

This endpoint provides all documents of a repository.

#### Path Parameters

| Name      | Type   | Description                          |
| --------- | ------ | ------------------------------------ |
| repoName  | string | The API identifier of the repository |
| documents | string | The endpoint to query all documents  |

#### Headers

| Name          | Type   | Description                                                                                               |
| ------------- | ------ | --------------------------------------------------------------------------------------------------------- |
| Authorization | string | Provide the API Key in the Authorization Header. For `GET` requests it can be provided as path parameter. |

{% tabs %}
{% tab title="200 " %}

```
```

{% endtab %}
{% endtabs %}

## Single Document

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/<repoName>/documents/<documentId>`

#### Path Parameters

| Name       | Type   | Description           |
| ---------- | ------ | --------------------- |
| documents  | string | The documents slug    |
| documentId | string | The Id for a document |

#### Headers

| Name          | Type   | Description                                                                                               |
| ------------- | ------ | --------------------------------------------------------------------------------------------------------- |
| Authorization | string | Provide the API Key in the Authorization Header. For `GET` requests it can be provided as path parameter. |

{% tabs %}
{% tab title="200 " %}

```
```

{% endtab %}
{% endtabs %}

## Single Document Alias

<mark style="color:blue;">`GET`</mark> `https://api.comft.io/v1/<repoName>/alias/<myAliasName>`

You can create an alias for a single document by linking it in the Content Tree.\
Once an alias is created, it is possible to switch out the document that's connected to that alias.

#### Path Parameters

| Name        | Type   | Description                    |
| ----------- | ------ | ------------------------------ |
| alias       | string | The alias slug                 |
| myAliasName | string | The name of the alias (API Id) |

#### Headers

| Name          | Type   | Description                                                                                               |
| ------------- | ------ | --------------------------------------------------------------------------------------------------------- |
| Authorization | string | Provide the API Key in the Authorization Header. For `GET` requests it can be provided as path parameter. |

{% tabs %}
{% tab title="200 " %}

```
```

{% endtab %}
{% endtabs %}

## Document Collections

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/<repoName>/collections/<collectionName>`

#### Path Parameters

| Name           | Type   | Description                          |
| -------------- | ------ | ------------------------------------ |
| repoName       | string | The API identifier of the repository |
| collectionName | string | The API ID for a Collection          |

#### Headers

| Name          | Type   | Description                                                                                               |
| ------------- | ------ | --------------------------------------------------------------------------------------------------------- |
| Authorization | string | Provide the API Key in the Authorization Header. For `GET` requests it can be provided as path parameter. |

{% tabs %}
{% tab title="200 " %}

```
```

{% endtab %}
{% endtabs %}

Document Collections work almost the same as the All Documents endpoint. The key difference is, that collections come a predefined set of documents, matching certain characteristics. These filters can be set in the Comfortable Web Application.

For more info on Collections, see the following page:

{% content-ref url="/pages/-LADZF1lElL5njx1B53b" %}
[Collections](/concepts-1/collections)
{% endcontent-ref %}

## Single Asset

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/<repoName>/assets/<assetId>`

#### Path Parameters

| Name    | Type   | Description         |
| ------- | ------ | ------------------- |
| assets  | string | The assets endpoint |
| assetId | string | The Id for an asset |

#### Headers

| Name          | Type   | Description                                                                                               |
| ------------- | ------ | --------------------------------------------------------------------------------------------------------- |
| Authorization | string | Provide the API Key in the Authorization Header. For `GET` requests it can be provided as path parameter. |

{% tabs %}
{% tab title="200 " %}

```
```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
When querying documents, add`embedAssets=true` as URL parameter or as a query option to include assets by default. This eliminates the need to perform single asset queries and reduces the number of API calls.\
[See Example](/apis/rest-api/examples/single-documents#including-assets-for-documents)
{% endhint %}


# API Reference

## Introduction

The content delivery API provides common query parameters like `limit`, `offset` and `sorting`, but also offers advanced methods such as `filters` , `locale` for list endpoints like [All Documents](/apis/rest-api/endpoints#all-documents) and [Collections](/apis/rest-api/endpoints#document-collections).

Queries are submitted per `GET` method either by query parameters e.g. or as an URI Encoded JSON Object.

Queries longer than 11kB that can't be submitted using the `GET` method, and must be sent using the `POST` method. The payload is a JSON Object ([Example](/apis/rest-api/examples/sorting#post-request))

{% hint style="success" %}
`POST` requests up to 2kB are also supported by the API CDN Cache.
{% endhint %}

## List of available Parameter Names

* `limit`
* `offset`
* [`sorting`](/apis/rest-api/filters/sorting)
* [`filters`](/apis/rest-api/filters/filters)
* [`locale`](/apis/rest-api/filters/localisation)
* [`fields`](/apis/rest-api/filters/reducing-payloads)
* [`includes`](/apis/rest-api/filters/includes)
* [`embedAssets`](/apis/rest-api/filters/includes#embed-assets)
* [`includeTags`](/apis/rest-api/filters/filters#include-tags)
* [`excludeTags`](/apis/rest-api/filters/filters#exclude-tags)
* [`search`](/apis/rest-api/filters/search)

## API CDN Cache

Each request gets cached by a global CND Cache provided by [Fastly](https://www.fastly.com/). An API CDN Cache does not only boost the speed of responses for edge cases and in general, but also greatly increases the reliability and robustness of an API.

The first API call will hit our servers to provide the response. This response is cached and every additional call for the same route will be provided from the cache as long as the returned data for this call doesn't change.

## Field Types

| **Name**    | **API Output**  |
| ----------- | --------------- |
| Text        | `string`        |
| Richtext    | `{plain, html}` |
| Number      | `number`        |
| Select      | `string\|array` |
| Assets      | `array`         |
| Date & Time | `string`        |
| Relation    | `object\|array` |
| Location    | `object`        |
| Boolean     | `boolean`       |
| JSON        | `object`        |
| Color       | `string`        |


# Sorting

Apply sorting for list endpoints like [All Documents](/apis/rest-api/endpoints#all-documents) and [Document Collections](/apis/rest-api/endpoints#document-collections). Each sorting parameter refers to one field. It is possible to provide a single or multiple sorting parameters.

Possible values for the sorting parameter are:

| **Value**      | **Function**                        |
| -------------- | ----------------------------------- |
| `ASC` or `1`   | Returns the list ordered ascending  |
| `DESC` or `-1` | Returns the list ordered descending |

{% hint style="info" %}
Lists are ordered descending by default.
{% endhint %}

## Sorted query results

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/<repoName>/<endpoint>?sorting[<fields|meta>.<fieldName>]=<direction>`

#### Path Parameters

| Name     | Type   | Description                                 |
| -------- | ------ | ------------------------------------------- |
| repoName | string | The API identifier of the repository        |
| endpoint | string | The endpoint you'd like to request          |
| sorting  | string | Provide the order direction `ASC` or `DESC` |

#### Headers

| Name          | Type   | Description        |
| ------------- | ------ | ------------------ |
| Authorization | string | Provide an API Key |

{% tabs %}
{% tab title="200 " %}

```javascript
// Response ordered ascending by title
"data": [
    {
      "fields": {
        "title": "Roses are red"
      },
      "meta": {
        "id": "314159265358979323",
        "contentType": "poem",
        "repository": "314159265358979323",
        "revision": 1,
        "tags": [],
        "createdAt": "2018-01-01T00:00:00.000Z",
        "updatedAt": "2018-01-01T00:00:00.000Z"
      }
    },
    {
      "fields": {
        "title": "Violets are blue"
      },
      "meta": {
        "id": "314159265358979323",
        "contentType": "poem",
        "repository": "314159265358979323",
        "revision": 1,
        "tags": [],
        "createdAt": "2018-01-01T00:00:00.000Z",
        "updatedAt": "2018-01-01T00:00:00.000Z"
      }
    }
]
```

{% endtab %}
{% endtabs %}

## Field Types

Sorting can be applied for the following field types:

* `Text`
* `Richtext`
* `Number`
* `Date & Time`
* `Boolean`
* `Location`
* `Select`
* `Color`

## Examples

Find some examples on the query examples page:

{% content-ref url="/pages/-LEUFxUpSqcfapg6AIxr" %}
[Sorting](/apis/rest-api/examples/sorting)
{% endcontent-ref %}


# Filters

Apply one or multiple filters for list endpoints like [All Documents](/apis/rest-api/endpoints#all-documents) and [Document Collections](/apis/rest-api/endpoints#document-collections). Each filter parameter refers to one field.

Filters can be submitted per URL with brackets or as URI Encoded JSON Object. Check out the [examples page](/apis/rest-api/examples/filters) to see how it's done.

## Filter documents

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/<repoName>/<endpoint>?filters[0][*.<fields|meta>.<fieldName>][operator]=<value>`

#### Path Parameters

| Name     | Type   | Description                                                         |
| -------- | ------ | ------------------------------------------------------------------- |
| repoName | string | The API identifier of the repository                                |
| endpoint | string | The endpoint you'd like to request                                  |
| filter   | string | Provide a filter with square brackets or as URI Encoded JSON Object |

{% tabs %}
{% tab title="200 " %}

```javascript
// Example: Filter by contenType 'poem'
"data": [
    {
      "fields": {
        "title": "Roses are red"
      },
      "meta": {
        "id": "314159265358979323",
        "contentType": "poem",
        "repository": "314159265358979323",
        "revision": 1,
        "tags": [],
        "createdAt": "2018-01-01T00:00:00.000Z",
        "updatedAt": "2018-01-01T00:00:00.000Z"
      }
    },
    {
      "fields": {
        "title": "Violets are blue"
      },
      "meta": {
        "id": "314159265358979323",
        "contentType": "poem",
        "repository": "314159265358979323",
        "revision": 1,
        "tags": [],
        "createdAt": "2018-01-01T00:00:00.000Z",
        "updatedAt": "2018-01-01T00:00:00.000Z"
      }
    }
]
```

{% endtab %}
{% endtabs %}

## Filter Types

### Field Values

| **Parameter Name** | **Description**                                                                                                                                         |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `filters`          | Filter which documents are returned in a query by checking the value with a [relational operator](/apis/rest-api/filters/filters#relational-operators). |

**Notation**

```http
filters[0][*.<fields|meta>.<fieldName>][operator]=<value>
```

Apply a filter to **multiple content types** with a wildcard (\*):

```http
*.<fields|meta>.<fieldName>
```

Or refer to a **single document type** using the following notation:&#x20;

```http
<contentType>.<fields|meta>.<fieldName>
```

#### Basic Example

```
filters[0][*.fields.category][equal]=spaceships
```

Or as JSON Object:

```javascript
"filters": [
  {
    "*.fields.category": {
        "equal": "spaceships"
      }
  }
]
```

For more examples, have a look at the [filters examples page](/apis/rest-api/examples/filters).

{% hint style="warning" %}
**Filters for nested fields**\
When applying filters for fields nested in sections, you'll have to *omit the section property key*.\
The path for the filter would be `*.fields.myField`, instead of `*.fields.mySection.myField`
{% endhint %}

### Include Tags

Decide if a document is included in a list by checking its tags.

| **Parameter Name** | **Description**                            |
| ------------------ | ------------------------------------------ |
| `includeTags`      | One of the tags must match for a document. |

**Notation**

```http
includeTags=<tag1>,<tag2>
```

### Exclude Tags

If a document has one of the stated tags, it will not be included in a response.

| **Parameter Name** | **Description**                                                 |
| ------------------ | --------------------------------------------------------------- |
| `excludeTags`      | Document will not be included if it has one of the stated tags. |

**Notation**

```http
excludeTags=<tag1>,<tag2>
```

## Relational Operators

Use Relational Operators to evaluate a fields value. Here is a reference for the available operators:

| **Notation**       | **Convention**      | **Explanation**                                                                                          |
| ------------------ | ------------------- | -------------------------------------------------------------------------------------------------------- |
| `equal`            | equal               | Validates true for a matching value                                                                      |
| `notEqual`         | not equal           | Validates true for non-matching values                                                                   |
| `greaterThan`      | greater than        | Validates true if the field value is greater than the specified value                                    |
| `greaterThanEqual` | greater or equal to | Validates true if the field value is greater or equal to the specified value                             |
| `lessThan`         | less than           | Validates true if the field value is lower than the specified value                                      |
| `lessThanEqual`    | less or equal to    | Validates true if the field value is lower or equal to the specified value                               |
| `like`             | like                | Validates true if a field value matches. Possible usage: `startsWith%`, `%endsWith`, `%contains%`        |
| `notLike`          | not like            | Validates true if a field value does not match. Possible usage: `startsWith%`, `%endsWith`, `%contains%` |
| `empty`            | empty               | Validates true if the field value is empty                                                               |
| `notEmpty`         | not empty           | Validates true if the field value is not empty                                                           |
| `in`               | in                  | Validates true if a value exists in an array                                                             |
| `notIn`            | not in              | Validates true if a value does not exists in an array                                                    |

## Logical Operators

Use Logical Operators to connect filters. Available operators are:

* `AND`
* `OR`

### Query String Notation

```
filters[0][*.fields.category][equal]=spaceships&filters[1][and][*.fields.color][equal]=red
```

### JSON Notation

```javascript
"filters": [
  {
    "*.fields.category": {
        "equal": "spaceships"
      }
  },
  {
    "and": {
      "*.fields.color": {
        "equal": "red"
      }
    }
  },
  {
    "or": {
      "*.meta.id": {
        "equal": "314159265358979323"
      }
    }
  }
]
```

## Limitations

{% hint style="info" %}
Filters are currently not available for relation fields. This feature is planned and we will update the documentation as soon as it lands.
{% endhint %}

## Examples

Find some examples on the filters example page:

{% content-ref url="/pages/-LEUZEdhBdqcF81jfgMb" %}
[Filters](/apis/rest-api/examples/filters)
{% endcontent-ref %}


# Localisation

Localisation is controlled by the `locale` parameter. If no parameter is given, the response includes only the default language of the repository.

Possible values for `locale` are `<languageKey>` or `all`

| **Value**                     | **Function**                                                                                                                 |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `<languageKey>`, e.g. `en_US` | Returns the field values for the specified locale, or their fallback (if configured).                                        |
| `all`                         | Returns all available locales as field value, prefixed by their language key. (See the response in the query example below). |

## Set localisation for query results

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/<repoName>/<endpoint>?locale=<languageKey>`

#### Path Parameters

| Name     | Type   | Description                                     |
| -------- | ------ | ----------------------------------------------- |
| repoName | string | The API identifier of the repository            |
| endpoint | string | The endpoint you'd like to request              |
| locale   | string | Provide the language Key, e.g. `en_US` or `all` |

{% tabs %}
{% tab title="200 " %}

```javascript
// Response with a specific language key or the default language
{
  "fields": {
    "title": "Roses are red"
  },
  "meta": {
    "id": "314159265358979323",
    "contentType": "poem",
    "repository": "314159265358979323",
    "revision": 1,
    "tags": [],
    "createdAt": "2018-01-01T00:00:00.000Z",
    "updatedAt": "2018-01-01T00:00:00.000Z"
  }
}

// Response with locale="all"
{
  "fields": {
    "title": {
      "en_US": "Roses are red",
      "de_DE": "Rosen sind rot"
    }
  },
  "meta": {
    "id": "314159265358979323",
    "contentType": "poem",
    "repository": "314159265358979323",
    "revision": 1,
    "tags": [],
    "createdAt": "2018-01-01T00:00:00.000Z",
    "updatedAt": "2018-01-01T00:00:00.000Z"
  }
}
```

{% endtab %}
{% endtabs %}

## Examples

Find some example on the collections example page:

{% content-ref url="/pages/-LEUZGK6bjs\_6YDrz\_rM" %}
[Collections](/apis/rest-api/examples/collections)
{% endcontent-ref %}


# Fields

You can control which fields should be included in the documents returned by a query. Provide the field names with the `fields` parameter.

The fields parameter works for single documents as well as for list endpoints.

## Control which fields are included for a response

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/<repoName>/<endpoint>?fields=fields|meta|includes(<field1>,<field2>)`

#### Path Parameters

| Name     | Type   | Description                                                       |
| -------- | ------ | ----------------------------------------------------------------- |
| repoName | string | The API identifier of the repository                              |
| endpoint | string | The endpoint you'd like to request                                |
| fields   | string | Provide the field API IDs you'd like to be included in a document |

{% tabs %}
{% tab title="200 " %}

```javascript
// Query with 'title' as field
{
  "fields": {
    "title": "Roses are red"
  },
  "meta": {
    "id": "314159265358979323",
    "contentType": "poem",
    "repository": "314159265358979323",
    "revision": 1,
    "tags": [],
    "createdAt": "2018-01-01T00:00:00.000Z",
    "updatedAt": "2018-01-01T00:00:00.000Z"
  }
}
```

{% endtab %}
{% endtabs %}

## Scopes

Returns only this scope or the stated sub-fields (e.g. `title`). There are three scopes:

* `fields`
* `meta`
* `includes`

## Value Syntax

| **Syntax** | **Description**                                       |
| ---------- | ----------------------------------------------------- |
| `a,b,c`    | comma-separated list will select multiple fields      |
| `a/b/c`    | path will select a field from its parent              |
| `a(b,c)`   | sub-selection will select many fields from a parent   |
| `a/*/c`    | the star \* wildcard will select all items in a field |

## Examples

Find some examples on the collection examples page:

{% content-ref url="/pages/-LEUZGK6bjs\_6YDrz\_rM" %}
[Collections](/apis/rest-api/examples/collections)
{% endcontent-ref %}


# Includes

With the parameter `includes` you can aggregate relations for a document that were defined by `relation fields` for a `content type`.

## Define a general number of included relation levels

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/<repoName>/<endpoint>?includes=<level>`

#### Path Parameters

| Name     | Type   | Description                          |
| -------- | ------ | ------------------------------------ |
| repoName | string | The API identifier of the repository |
| endpoint | string | The endpoint you'd like to query     |
| include  | number | The levels of includes               |

{% tabs %}
{% tab title="200 " %}

```javascript
// Example for 'includes=1' (aggregate first level)
data: [
{
  "fields": {
    "title": "Roses are red",
    "author": {
      "meta": {
        "id": "323979853562951413",
        "contentType": "author"
      }
    }
  },
  "meta": {
    "id": "314159265358979323",
    "contentType": "poem",
    "repository": "123456789011121415",
    "revision": 1,
    "tags": [],
    "createdAt": "2018-01-01T00:00:00.000Z",
    "updatedAt": "2018-01-01T00:00:00.000Z"
  }
}
],
includes: [
  author: [
    {
      "fields": {
        "name": "Victor Hugo"
      },
      "meta": {
        "id": "323979853562951413",
        "contentType": "author",
        "repository": "123456789011121415",
        "revision": 1,
        "tags": [],
        "createdAt": "2018-01-01T00:00:00.000Z",
        "updatedAt": "2018-01-01T00:00:00.000Z"
      }
    }
  ]
]

```

{% endtab %}
{% endtabs %}

A level of `1` would aggregate all relations, from **all relation fields**, of the first level. \
A level of `2` would also return all of the relations to those documents of the first level. The maximum number of levels is `5`.

## Include relations from specific fields

<mark style="color:blue;">`GET`</mark> `https://api.comft.io/v1/<repoName>/<endpoint>?includes[<field>]=1`

#### Path Parameters

| Name     | Type   | Description                          |
| -------- | ------ | ------------------------------------ |
| repoName | string | The API identifier of the repository |
| endpoint | string | The endpoint you'd like to query     |
| include  | string | The field path you'd like to include |

{% tabs %}
{% tab title="200 " %}

```javascript
/*
 * Example for 'includes[*.fields.author]=1'
 * (aggregate the author field, specifically)
 */
 
data: [
{
  "fields": {
    "title": "Roses are red",
    "author": {
      "meta": {
        "id": "323979853562951413",
        "contentType": "author"
      }
    }
  },
  "meta": {
    "id": "314159265358979323",
    "contentType": "poem",
    "repository": "123456789011121415",
    "revision": 1,
    "tags": [],
    "createdAt": "2018-01-01T00:00:00.000Z",
    "updatedAt": "2018-01-01T00:00:00.000Z"
  }
}
],
includes: [
  author: [
    {
      "fields": {
        "name": "Victor Hugo"
      },
      "meta": {
        "id": "323979853562951413",
        "contentType": "author",
        "repository": "123456789011121415",
        "revision": 1,
        "tags": [],
        "createdAt": "2018-01-01T00:00:00.000Z",
        "updatedAt": "2018-01-01T00:00:00.000Z"
      }
    }
  ]
]

```

{% endtab %}
{% endtabs %}

If you just want to include the relations from a single or multiple **specific fields**, you can define these fields in your query.

#### Notation

The notation for fields is similar to filters. You can either include a `contentType` as field prefix, or a wildcard (\*).

```http
includes[<contentType>.fields.<fieldName>]=1
```

or

```http
includes[*.fields.<fieldName>]=1
```

To query multiple levels (relation of a relation...) with the field syntax, fields can be chained:

```http
includes[<contentType>.fields.<fieldName>.fields.<fieldName>]=1
```

## Field types

Field types which contain references are the following:

* `relation field`
* `asset field`

## Embed Assets

From a technical perspective, assets are relations and by default they are treated like that.

But sometimes assets, for example images, can be seen as a piece of content that you want to be included within a document field. For this case, you can use the `embedAssets` parameter.

#### Notation

```
embedAssets=true
```

## Examples

Find some examples on the includes example page:

{% content-ref url="/pages/-LEUdHv6HXaSTZ4NHr0P" %}
[Includes](/apis/rest-api/examples/includes)
{% endcontent-ref %}


# Search

Perform a Full-Text search query by using the `search` parameter.\
You can apply a search query for `all documents`, `collections`, or combine it with [`filters`](/apis/rest-api/examples/filters).

## Search query

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/<repoName>/<endpoint>?search=<searchTerm>`

#### Path Parameters

| Name     | Type   | Description                          |
| -------- | ------ | ------------------------------------ |
| repoName | string | The API identifier of the repository |
| endpoint | string | The endpoint you'd like to request   |
| search   | string | Provide the search term              |

#### Headers

| Name          | Type   | Description        |
| ------------- | ------ | ------------------ |
| Authorization | string | Provide an API Key |

{% tabs %}
{% tab title="200 " %}

```
```

{% endtab %}
{% endtabs %}

## Seach Features

### Stemming

The search supports [stemming](https://en.wikipedia.org/wiki/Stemming).

{% hint style="info" %}
You can control the locale for a search with the [`locale parameter`](/apis/rest-api/filters/localisation). The default locale for the search is the same as a repositories default locale.
{% endhint %}

### Stop Words

The search supports language-specific stop words (e.g. in English, the, an, a, and, etc.)

### Exclude Words

Exclude words from the search by prefixing them with `-` (e.g. `Flowers -Sunflowers`)

## Supported Languages

**Stemming** and **Stop Words** are supported for the following languages:

* danish (da)
* dutch (nl)
* english (en)
* finnish (fi)
* french (fr)
* german (de)
* hungarian (hu)
* italian (it)
* norwegian (nb)
* portugese (pt)
* romanian (ro)
* russian (ru)
* spanish (es)
* swedish (sv)
* turkish (tr)
* arabic (ara)
* dari (prs)
* iranian persian (pes)
* urdu (urd)
* simplified chinese or hans (zhs)
* traditional chinese or hant (zht)


# Query Examples


# Collections

## Fetching Collections

This demonstrates a basic query for a `collection`.

{% hint style="info" %}
All examples can be used with`/documents` Endpoint as well.
{% endhint %}

{% tabs %}
{% tab title="JavaScript" %}

```javascript
window.fetch('https://api.cmft.io/v1/<repo>/collections/<collection>', {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl 'https://api.cmft.io/v1/<repo>/<collection>' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}
{% code title="" %}

```http
https://api.cmft.io/v1/<repo>/collections/<collection>?key=<apiKey>
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Sample Request

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/demo/collections/news?key=8MIO994Ley6bqyAlQAHqutiDh4g5Heck`

#### Query Parameters

| Name | Type   | Description |
| ---- | ------ | ----------- |
| key  | string | API Key     |

{% tabs %}
{% tab title="200 " %}

```javascript
{
  "status": 200,
  "meta": {
    "count": 2,
    "limit": 25,
    "offset": 0,
    "total": 2
  },
  "data": [
    {
      "fields": {
        "title": "Hello World!",
        "date": "2018-07-06T13:00:00.000Z",
        "text": {
          "html": "<p>Lorem ipsum dolor sit amet, consetetur <strong>sadipscing elitr, sed diam</strong> nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.</p>",
          "plain": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\ntempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At\nvero eos et accusam et justo duo dolores et ea rebum."
        },
        "images": [
          {
            "meta": {
              "id": "968393840635547648",
              "contentType": "_asset"
            }
          }
        ],
        "relatedNews": [],
        "author": [
          {
            "meta": {
              "id": "968388973430968320",
              "contentType": "author"
            }
          }
        ]
      },
      "meta": {
        "id": "968390655497867264",
        "contentType": "news",
        "repository": "968388392691830784",
        "revision": 5,
        "tags": [],
        "createdAt": "2018-07-06T13:05:24.000Z",
        "updatedAt": "2018-07-06T13:23:08.000Z"
      }
    },
    {
      "fields": {
        "title": "Simple Blog Post",
        "date": "2018-07-04T22:00:00.000Z",
        "text": {
          "html": "<h1>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.</h1>",
          "plain": "LOREM IPSUM DOLOR SIT AMET, CONSETETUR SADIPSCING ELITR, SED DIAM NONUMY EIRMOD\nTEMPOR INVIDUNT UT LABORE ET DOLORE MAGNA ALIQUYAM ERAT, SED DIAM VOLUPTUA. AT\nVERO EOS ET ACCUSAM ET JUSTO DUO DOLORES ET EA REBUM."
        },
        "images": [
          {
            "meta": {
              "id": "968393848009134080",
              "contentType": "_asset"
            }
          }
        ],
        "relatedNews": [
          {
            "meta": {
              "id": "968390655497867264",
              "contentType": "news"
            }
          }
        ],
        "author": [
          {
            "meta": {
              "id": "968388973430968320",
              "contentType": "author"
            }
          }
        ]
      },
      "meta": {
        "id": "968395804664537088",
        "contentType": "news",
        "repository": "968388392691830784",
        "revision": 1,
        "tags": [],
        "createdAt": "2018-07-06T13:25:51.000Z",
        "updatedAt": "2018-07-06T13:25:51.000Z"
      }
    }
  ]

```

{% endtab %}
{% endtabs %}

## Response Shaping

Let's say we want to display a news page with a teaser view for each article. To reduce the payload we're going to do a request and tell the API to include only the fields `title` , `text` and `image` for each document.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
// Define query
var query = {
  "fields": "fields(title,text,image)"
};
​
// Stringify and URI Encode the Object
query = encodeURIComponent(JSON.stringify(query));

window.fetch(`https://api.cmft.io/v1/<repo>/collections/<collection>?query=${query}`, {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl 'https://api.cmft.io/v1/<repo>/collections/<collection>?fields=fields(title,text,image)' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}

```http
https://api.cmft.io/v1/<repo>/collections/<collection>?fields=fields(title,text,image)&key=<apiKey>
```

{% endtab %}
{% endtabs %}

## Sample Request

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/demo/collections/news?fields=fields(title,text,image)&key=8MIO994Ley6bqyAlQAHqutiDh4g5Heck`

#### Path Parameters

| Name | Type   | Description |
| ---- | ------ | ----------- |
| key  | string | API Key     |

{% tabs %}
{% tab title="200 " %}

```javascript
{
  "status": 200,
  "meta": {
    "count": 2,
    "limit": 25,
    "offset": 0,
    "total": 2
  },
  "data": [
    {
      "fields": {
        "title": "Hello World!",
        "text": {
          "html": "<p>Lorem ipsum dolor sit amet, consetetur <strong>sadipscing elitr, sed diam</strong> nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.</p>",
          "plain": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\ntempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At\nvero eos et accusam et justo duo dolores et ea rebum."
        }
      }
    },
    {
      "fields": {
        "title": "Simple Blog Post",
        "text": {
          "html": "<h1>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.</h1>",
          "plain": "LOREM IPSUM DOLOR SIT AMET, CONSETETUR SADIPSCING ELITR, SED DIAM NONUMY EIRMOD\nTEMPOR INVIDUNT UT LABORE ET DOLORE MAGNA ALIQUYAM ERAT, SED DIAM VOLUPTUA. AT\nVERO EOS ET ACCUSAM ET JUSTO DUO DOLORES ET EA REBUM."
        }
      }
    }
  ]
}
```

{% endtab %}
{% endtabs %}

## Reduced scopes

This example query makes a collection request that will only return the `meta` scope for each document.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
window.fetch('https://api.cmft.io/v1/<repo>/<collection>?fields=meta', {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl 'https://api.cmft.io/v1/<repo>/<collection>?fields=meta' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}

```http
https://api.cmft.io/v1/demo/collections/news?fields=meta&key=8MIO994Ley6bqyAlQAHqutiDh4g5Heck
```

{% endtab %}
{% endtabs %}

## Sample Request

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/demo/collections/news?fields=meta&key=8MIO994Ley6bqyAlQAHqutiDh4g5Heck`

#### Path Parameters

| Name | Type   | Description |
| ---- | ------ | ----------- |
| key  | string | API Key     |

{% tabs %}
{% tab title="200 " %}

```javascript
{
  "status": 200,
  "meta": {
    "count": 2,
    "limit": 25,
    "offset": 0,
    "total": 2
  },
  "data": [
    {
      "meta": {
        "id": "968390655497867264",
        "contentType": "news",
        "repository": "968388392691830784",
        "revision": 5,
        "tags": [],
        "createdAt": "2018-07-06T13:05:24.000Z",
        "updatedAt": "2018-07-06T13:23:08.000Z"
      }
    },
    {
      "meta": {
        "id": "968395804664537088",
        "contentType": "news",
        "repository": "968388392691830784",
        "revision": 1,
        "tags": [],
        "createdAt": "2018-07-06T13:25:51.000Z",
        "updatedAt": "2018-07-06T13:25:51.000Z"
      }
    }
  ]
}
```

{% endtab %}
{% endtabs %}

## Reduced fields for relations

This query returns a full list of documents, but for the relations the payload is limited to the fields `name` and `id`.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
// Define query
var query = {
  "fields": "includes(news(fields(title),meta(id))),fields,meta"
};

// Stringify and URI Encode the Object
query = encodeURIComponent(JSON.stringify(query));

window.fetch(`https://api.cmft.io/v1/<repo>/<collection>?query=${query}`, {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl 'https://api.cmft.io/v1/<repo>/<collection>?includes=1&fields=includes(news(fields(title),meta(id))),fields,meta' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}

```http
https://api.cmft.io/v1/<repo>/collections/<collection>?includes=1&fields=includes(news(fields(title),meta(id))),fields,meta&key=<apiKey>
```

{% endtab %}
{% endtabs %}

#### The parameter explained

```javascript
includes( // the 1st level scope 'includes'
  news( // select 'news' in 'includes'
    fields( // select the content fields for the related type
      title
    ),
    meta( // select the meta fields for the related type
      id
    )
  )
)
```

## Sample Request

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/demo/collections/news?includes=1&fields=includes(news(fields(title),meta(id))),fields,meta`

#### Path Parameters

| Name | Type   | Description |
| ---- | ------ | ----------- |
| key  | string | API Key     |

{% tabs %}
{% tab title="200 " %}

```javascript
{
  "status": 200,
  "meta": {
    "count": 2,
    "limit": 25,
    "offset": 0,
    "total": 2
  },
  "data": [
    {
      "fields": {
        "title": "Hello World!",
        "date": "2018-07-06T13:00:00.000Z",
        "text": {
          "html": "<p>Lorem ipsum dolor sit amet, consetetur <strong>sadipscing elitr, sed diam</strong> nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.</p>",
          "plain": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\ntempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At\nvero eos et accusam et justo duo dolores et ea rebum."
        },
        "images": [
          {
            "meta": {
              "id": "968393840635547648",
              "contentType": "_asset"
            }
          }
        ],
        "relatedNews": [],
        "author": [
          {
            "meta": {
              "id": "968388973430968320",
              "contentType": "author"
            }
          }
        ]
      },
      "meta": {
        "id": "968390655497867264",
        "contentType": "news",
        "repository": "968388392691830784",
        "revision": 5,
        "tags": [],
        "createdAt": "2018-07-06T13:05:24.000Z",
        "updatedAt": "2018-07-06T13:23:08.000Z"
      }
    },
    {
      "fields": {
        "title": "Simple Blog Post",
        "date": "2018-07-04T22:00:00.000Z",
        "text": {
          "html": "<h1>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.</h1>",
          "plain": "LOREM IPSUM DOLOR SIT AMET, CONSETETUR SADIPSCING ELITR, SED DIAM NONUMY EIRMOD\nTEMPOR INVIDUNT UT LABORE ET DOLORE MAGNA ALIQUYAM ERAT, SED DIAM VOLUPTUA. AT\nVERO EOS ET ACCUSAM ET JUSTO DUO DOLORES ET EA REBUM."
        },
        "images": [
          {
            "meta": {
              "id": "968393848009134080",
              "contentType": "_asset"
            }
          }
        ],
        "relatedNews": [
          {
            "meta": {
              "id": "968390655497867264",
              "contentType": "news"
            }
          }
        ],
        "author": [
          {
            "meta": {
              "id": "968388973430968320",
              "contentType": "author"
            }
          }
        ]
      },
      "meta": {
        "id": "968395804664537088",
        "contentType": "news",
        "repository": "968388392691830784",
        "revision": 1,
        "tags": [],
        "createdAt": "2018-07-06T13:25:51.000Z",
        "updatedAt": "2018-07-06T13:25:51.000Z"
      }
    }
  ],
  "includes": {
    "news": [
      {
        "fields": {
          "title": "Hello World!"
        },
        "meta": {
          "id": "968390655497867264"
        }
      }
    ]
  }

```

{% endtab %}
{% endtabs %}

## Applying filters to collection queries

You can apply additional filters on all documents in a collection. Find examples with *multiple filters* on the [filters examples page](/apis/rest-api/examples/filters).

### Filter by checkbox state

In this example we're going to check the availability of a product based on a simple checkbox field.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
// Define query
var query = {
  "filters": [
    {
      "*.fields.inStock": true
    }
  ]
};

// Stringify and URI Encode the Object
query = encodeURIComponent(JSON.stringify(query));

// Make the request
window.fetch(`https://api.cmft.io/v1/<repo>/collections/<collection>?query=${query}`, {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl -g 'https://api.cmft.io/v1/<repo>/collections/<collection>?filters[0][*.fields.inStock][equal]=true' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}

```http
https://api.cmft.io/v1/<repo>/collections/<collection>?filters[0][*.fields.inStock][equal]=true&key=<apiKey>
```

{% endtab %}
{% endtabs %}

## Sample Request

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/demo/collections/products?filters[0][*.fields.inStock][equal]=true&key=8MIO994Ley6bqyAlQAHqutiDh4g5Heck`

#### Path Parameters

| Name | Type   | Description |
| ---- | ------ | ----------- |
| key  | string | API Key     |

{% tabs %}
{% tab title="200 " %}

```javascript
{
  "status": 200,
  "meta": {
    "count": 1,
    "limit": 25,
    "offset": 0,
    "total": 1
  },
  "data": [
    {
      "fields": {
        "title": "Beautiful Bicycle",
        "description": {
          "html": "<p>Aluminium Frame; Disc Brakes; Shimano XTR;</p>",
          "plain": "Aluminium Frame; Disc Brakes; Shimano XTR;"
        },
        "price": 1399,
        "relatedProducts": [],
        "rating": "5",
        "inStock": true,
        "images": [
          {
            "meta": {
              "id": "969465200912769024",
              "contentType": "_asset"
            }
          }
        ]
      },
      "meta": {
        "id": "969464870959452160",
        "contentType": "products",
        "repository": "968388392691830784",
        "revision": 4,
        "tags": [],
        "createdAt": "2018-07-09T12:13:57.000Z",
        "updatedAt": "2018-07-09T12:30:01.000Z"
      }
    }
  ]
}
```

{% endtab %}
{% endtabs %}

## Collection queries with localisation

Lets query a collection in a specific Language

{% tabs %}
{% tab title="JavaScript" %}

```javascript
window.fetch('https://api.cmft.io/v1/<repo>/collections/<collection>?locale=en', {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl 'https://api.cmft.io/v1/<repo>/collections/<collection>?locale=en' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}

```http
https://api.cmft.io/v1/<repo>/collections/<collection>?key=<apiKey>&locale=en
```

{% endtab %}
{% endtabs %}

## Sample Request

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/demo/collections/news?locale=en&key=8MIO994Ley6bqyAlQAHqutiDh4g5Heck`

#### Path Parameters

| Name | Type   | Description |
| ---- | ------ | ----------- |
| key  | string | API Key     |

{% tabs %}
{% tab title="200 " %}

```javascript
{
  "status": 200,
  "meta": {
    "count": 2,
    "limit": 25,
    "offset": 0,
    "total": 2
  },
  "data": [
    {
      "fields": {
        "title": "Hello World!",
        "date": "2018-07-06T13:00:00.000Z",
        "text": {
          "html": "<p>Lorem ipsum dolor sit amet, consetetur <strong>sadipscing elitr, sed diam</strong> nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.</p>",
          "plain": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\ntempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At\nvero eos et accusam et justo duo dolores et ea rebum."
        },
        "images": [
          {
            "meta": {
              "id": "968393840635547648",
              "contentType": "_asset"
            }
          }
        ],
        "relatedNews": [],
        "author": [
          {
            "meta": {
              "id": "968388973430968320",
              "contentType": "author"
            }
          }
        ]
      },
      "meta": {
        "id": "968390655497867264",
        "contentType": "news",
        "repository": "968388392691830784",
        "revision": 5,
        "tags": [],
        "createdAt": "2018-07-06T13:05:24.000Z",
        "updatedAt": "2018-07-06T13:23:08.000Z"
      }
    },
    {
      "fields": {
        "title": "Simple Blog Post",
        "date": "2018-07-04T22:00:00.000Z",
        "text": {
          "html": "<h1>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.</h1>",
          "plain": "LOREM IPSUM DOLOR SIT AMET, CONSETETUR SADIPSCING ELITR, SED DIAM NONUMY EIRMOD\nTEMPOR INVIDUNT UT LABORE ET DOLORE MAGNA ALIQUYAM ERAT, SED DIAM VOLUPTUA. AT\nVERO EOS ET ACCUSAM ET JUSTO DUO DOLORES ET EA REBUM."
        },
        "images": [
          {
            "meta": {
              "id": "968393848009134080",
              "contentType": "_asset"
            }
          }
        ],
        "relatedNews": [
          {
            "meta": {
              "id": "968390655497867264",
              "contentType": "news"
            }
          }
        ],
        "author": [
          {
            "meta": {
              "id": "968388973430968320",
              "contentType": "author"
            }
          }
        ]
      },
      "meta": {
        "id": "968395804664537088",
        "contentType": "news",
        "repository": "968388392691830784",
        "revision": 1,
        "tags": [],
        "createdAt": "2018-07-06T13:25:51.000Z",
        "updatedAt": "2018-07-06T13:25:51.000Z"
      }
    }
  ]
}
```

{% endtab %}
{% endtabs %}

## Query all Languages

## Sample Request

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/demo/collections/news?locale=all&key=8MIO994Ley6bqyAlQAHqutiDh4g5Heck`

#### Path Parameters

| Name | Type   | Description |
| ---- | ------ | ----------- |
| key  | string | API Key     |

{% tabs %}
{% tab title="200 " %}

```javascript
{
  "status": 200,
  "meta": {
    "count": 2,
    "limit": 25,
    "offset": 0,
    "total": 2
  },
  "data": [
    {
      "fields": {
        "title": {
          "en": "Hello World!",
          "de": ""
        },
        "date": "2018-07-06T13:00:00.000Z",
        "text": {
          "en": {
            "html": "<p>Lorem ipsum dolor sit amet, consetetur <strong>sadipscing elitr, sed diam</strong> nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.</p>",
            "plain": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\ntempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At\nvero eos et accusam et justo duo dolores et ea rebum."
          },
          "de": {
            "html": "",
            "plain": ""
          }
        },
        "images": {
          "en": [
            {
              "meta": {
                "id": "968393840635547648",
                "contentType": "_asset"
              }
            }
          ],
          "de": []
        },
        "relatedNews": [],
        "author": [
          {
            "meta": {
              "id": "968388973430968320",
              "contentType": "author"
            }
          }
        ]
      },
      "meta": {
        "id": "968390655497867264",
        "contentType": "news",
        "repository": "968388392691830784",
        "revision": 5,
        "tags": [],
        "createdAt": "2018-07-06T13:05:24.000Z",
        "updatedAt": "2018-07-06T13:23:08.000Z"
      }
    },
    {
      "fields": {
        "title": {
          "en": "Simple Blog Post",
          "de": ""
        },
        "date": "2018-07-04T22:00:00.000Z",
        "text": {
          "en": {
            "html": "<h1>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.</h1>",
            "plain": "LOREM IPSUM DOLOR SIT AMET, CONSETETUR SADIPSCING ELITR, SED DIAM NONUMY EIRMOD\nTEMPOR INVIDUNT UT LABORE ET DOLORE MAGNA ALIQUYAM ERAT, SED DIAM VOLUPTUA. AT\nVERO EOS ET ACCUSAM ET JUSTO DUO DOLORES ET EA REBUM."
          },
          "de": {
            "html": "",
            "plain": ""
          }
        },
        "images": {
          "en": [
            {
              "meta": {
                "id": "968393848009134080",
                "contentType": "_asset"
              }
            }
          ],
          "de": []
        },
        "relatedNews": [
          {
            "meta": {
              "id": "968390655497867264",
              "contentType": "news"
            }
          }
        ],
        "author": [
          {
            "meta": {
              "id": "968388973430968320",
              "contentType": "author"
            }
          }
        ]
      },
      "meta": {
        "id": "968395804664537088",
        "contentType": "news",
        "repository": "968388392691830784",
        "revision": 1,
        "tags": [],
        "createdAt": "2018-07-06T13:25:51.000Z",
        "updatedAt": "2018-07-06T13:25:51.000Z"
      }
    }
  ]
}
```

{% endtab %}
{% endtabs %}


# Single Documents

## Basic document query

Demonstrates how to query a single `document`.

### Query by Document ID

{% tabs %}
{% tab title="JavaScript" %}

```javascript
window.fetch('https://api.cmft.io/v1/<repo>/documents/<documentID>', {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl 'https://api.cmft.io/v1/<repo>/documents/<documentID>' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}

```http
https://api.cmft.io/v1/<repo>/documents/<documentID>?key=<apiKey>
```

{% endtab %}
{% endtabs %}

### Query by Document Alias

You can create an alias for a single document by linking it in the Content Tree. Once an alias is created, it is possible to switch out the document that's connected to that alias.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
window.fetch('https://api.cmft.io/v1/<repo>/alias/<myAliasName>', {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl 'https://api.cmft.io/v1/<repo>/alias/<myAliasName>' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}

```http
https://api.cmft.io/v1/<repo>/alias/<myAliasName>?key=<apiKey>
```

{% endtab %}
{% endtabs %}

## Sample Request by ID

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/demo/documents/968390655497867264?key=8MIO994Ley6bqyAlQAHqutiDh4g5Heck`

#### Path Parameters

| Name | Type   | Description |
| ---- | ------ | ----------- |
| key  | string | API Key     |

{% tabs %}
{% tab title="200 " %}

```javascript
{
  "fields": {
    "title": "Hello World!",
    "date": "2018-07-06T13:00:00.000Z",
    "text": {
      "html": "<p>Lorem ipsum dolor sit amet, consetetur <strong>sadipscing elitr, sed diam</strong> nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.</p>",
      "plain": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\ntempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At\nvero eos et accusam et justo duo dolores et ea rebum."
    },
    "images": [
      {
        "meta": {
          "id": "968393840635547648",
          "contentType": "_asset"
        }
      }
    ],
    "relatedNews": [],
    "author": [
      {
        "meta": {
          "id": "968388973430968320",
          "contentType": "author"
        }
      }
    ]
  },
  "meta": {
    "id": "968390655497867264",
    "contentType": "news",
    "repository": "968388392691830784",
    "revision": 5,
    "tags": [],
    "createdAt": "2018-07-06T13:05:24.000Z",
    "updatedAt": "2018-07-06T13:23:08.000Z"
  }
}
```

{% endtab %}
{% endtabs %}

## Including Assets for Documents

When querying documents, add `embedAssets=true` as URL parameter or as a query option to include assets by default, within a documents fields.\
This eliminates the need to perform single asset queries and reduces the number of API calls.

{% hint style="info" %}
`embedAssets` works for single documents and document lists as well.
{% endhint %}

{% tabs %}
{% tab title="JavaScript" %}

```javascript
// Define query
var query = {
  "embedAssets": true
};
​
// Stringify and URI Encode the Object
query = encodeURIComponent(JSON.stringify(query));

window.fetch(`https://api.cmft.io/v1/<repo>/documents/<documentID>?query=${query}`, {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl 'https://api.cmft.io/v1/<repo>/documents/<documentID>&embedAssets=true' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}

```http
https://api.cmft.io/v1/<repo>/documents/<documentID>?embedAssets=true&key=<apiKey>
```

{% endtab %}
{% endtabs %}

## Sample Request with Assets included

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/demo/documents/968390655497867264?embedAssets=true&key=8MIO994Ley6bqyAlQAHqutiDh4g5Heck`

#### Path Parameters

| Name        | Type    | Description                         |
| ----------- | ------- | ----------------------------------- |
| embedAssets | boolean | Truthy if assets should be included |
| key         | string  | API Key                             |

{% tabs %}
{% tab title="200 " %}

```
```

{% endtab %}
{% endtabs %}

## Response Shaping: Document query with reduced fields

Let's say we need a document, but not all of its content. In this query example we're going to request a document only with the fields `title`, `text` and `image`.

To learn more about defined fields, take a look at the [fields query reference page](/apis/rest-api/filters/reducing-payloads).

{% tabs %}
{% tab title="JavaScript" %}

```javascript
// Define query
var query = {
  "fields": "fields(title,text,image)"
};
​
// Stringify and URI Encode the Object
query = encodeURIComponent(JSON.stringify(query));

window.fetch(`https://api.cmft.io/v1/<repo>/documents/<documentID>?query=${query}`, {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl 'https://api.cmft.io/v1/<repo>/documents/<documentID>&fields=fields(title,text,images)' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}

```http
https://api.cmft.io/v1/<repo>/documents/<documentID>?fields=fields(title,text,images)&key=<apiKey>
```

{% endtab %}
{% endtabs %}

## Sample Request

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/demo/documents/968390655497867264?fields=fields(title,text,images)&key=8MIO994Ley6bqyAlQAHqutiDh4g5Heck`

#### Path Parameters

| Name   | Type   | Description                                   |
| ------ | ------ | --------------------------------------------- |
| fields | string | The fields that should be returned by the API |
| key    | string | API Key                                       |

{% tabs %}
{% tab title="200 " %}

```javascript
{
  "fields": {
    "title": "Hello World!",
    "text": {
      "html": "<p>Lorem ipsum dolor sit amet, consetetur <strong>sadipscing elitr, sed diam</strong> nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.</p>",
      "plain": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\ntempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At\nvero eos et accusam et justo duo dolores et ea rebum."
    },
    "images": [
      {
        "meta": {
          "id": "968393840635547648",
          "contentType": "_asset"
        }
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}

## Document query with localisation

Example for requesting a document in one specific language (German). This will return all fields that have translation enabled and completed, in German.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
window.fetch('https://api.cmft.io/v1/<repo>/documents/<documentID>?locale=de', {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl 'https://api.cmft.io/v1/<repo>/documents/<documentID>&locale=de' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}

```http
https://api.cmft.io/v1/<repo>/documents/<documentID>?key=<apiKey>&locale=de
```

{% endtab %}
{% endtabs %}


# Single Assets

## Basic asset query

Demonstrates the basic query for a single `asset`.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
window.fetch('https://api.cmft.io/v1/<repo>/assets/<assetID>', {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl 'https://api.cmft.io/v1/<repo>/assets/<assetID>' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}

```http
https://api.cmft.io/v1/<repo>/assets/<assetID>?key=<apiKey>
```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
When querying documents, add`embedAssets=true` as URL parameter or as a query option to include assets by default. This eliminates the need to perform single asset queries and reduces the number of API calls. \
[See example](/apis/rest-api/examples/single-documents#including-assets-for-documents)
{% endhint %}

## Response Shaping: Asset query with reduced fields

Like documents, assets have fields which can be defined when making a query. In this example, we're going to query an asset just with its target URL.

To learn how to make a request with multiple fields, take a look at the [fields query reference page](/apis/rest-api/filters/reducing-payloads).

{% tabs %}
{% tab title="JavaScript" %}

```javascript
window.fetch('https://api.cmft.io/v1/<repo>/assets/<assetID>?fields=fields(file(url))', {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl 'https://api.cmft.io/v1/<repo>/assets/<assetID>?fields=fields(file(url))' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}

```http
https://api.cmft.io/v1/<repo>/assets/<assetID>?key=<apiKey>&fields=fields(file(url))
```

{% endtab %}
{% endtabs %}

## Asset query with localisation

Example for requesting an asset in one specific language (German).

{% tabs %}
{% tab title="JavaScript" %}

```javascript
window.fetch('https://api.cmft.io/v1/<repo>/assets/<assetID>?locale=de', {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl 'https://api.cmft.io/v1/<repo>/assets/<assetID>?locale=de' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}

```http
https://api.cmft.io/v1/<repo>/assets/<assetID>?key=<apiKey>&locale=de
```

{% endtab %}
{% endtabs %}


# Sorting

## Order response by creation date

Query a `collection` and order the results ascending by creation date.

{% tabs %}
{% tab title="JavaScript" %}
{% code title="GET" %}

```javascript
// Define query
var query = {
  "sorting": {
    "meta.createdAt": "ASC"
  }
};

// Stringify and URI Encode the Object
query = encodeURIComponent(JSON.stringify(query));

// Submit the query
window.fetch(`https://api.cmft.io/v1/<repo>/<collection>?query=${query}`, {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endcode %}
{% endtab %}

{% tab title="CURL" %}

```bash
curl -g 'https://api.cmft.io/v1/<repo>/<collection>?sorting[meta.createdAt]=ASC' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}
{% code title="" %}

```http
https://api.cmft.io/v1/<repo>/<collection>?key=<apiKey>&sorting[meta.createdAt]=ASC
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Order response by field value

Query the `documents` endpoint and order the results ascending by title.

{% tabs %}
{% tab title="JavaScript" %}
{% code title="" %}

```javascript
// Define query
var query = {
  "sorting": {
    "fields.title": "ASC"
  }
};

// Stringify and URI Encode the Object
query = encodeURIComponent(JSON.stringify(query));

// Submit the query
window.fetch(`https://api.cmft.io/v1/<repo>/documents?query=${query}`, {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endcode %}
{% endtab %}

{% tab title="CURL" %}

```bash
curl -g 'https://api.cmft.io/v1/<repo>/documents?sorting[fields.title]=ASC' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}
{% code title="" %}

```http
https://api.cmft.io/v1/<repo>/documents?key=<apiKey>&sorting[fields.title]=ASC
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Sample Request

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/demo/documents?sorting[fields.title]=ASC&key=8MIO994Ley6bqyAlQAHqutiDh4g5Heck`

#### Query Parameters

| Name    | Type   | Description |
| ------- | ------ | ----------- |
| key     | string | API Key     |
| sorting | object |             |

{% tabs %}
{% tab title="200 " %}

```javascript
{
  "status": 200,
  "meta": {
    "count": 5,
    "limit": 25,
    "offset": 0,
    "total": 5
  },
  "data": [
    {
      "fields": {
        "title": "Simple Blog Post",
        "date": "2018-07-04T22:00:00.000Z",
        "text": {
          "html": "<h1>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.</h1>",
          "plain": "LOREM IPSUM DOLOR SIT AMET, CONSETETUR SADIPSCING ELITR, SED DIAM NONUMY EIRMOD\nTEMPOR INVIDUNT UT LABORE ET DOLORE MAGNA ALIQUYAM ERAT, SED DIAM VOLUPTUA. AT\nVERO EOS ET ACCUSAM ET JUSTO DUO DOLORES ET EA REBUM."
        },
        "images": [
          {
            "meta": {
              "id": "968393848009134080",
              "contentType": "_asset"
            }
          }
        ],
        "relatedNews": [
          {
            "meta": {
              "id": "968390655497867264",
              "contentType": "news"
            }
          }
        ],
        "author": [
          {
            "meta": {
              "id": "968388973430968320",
              "contentType": "author"
            }
          }
        ]
      },
      "meta": {
        "id": "968395804664537088",
        "contentType": "news",
        "repository": "968388392691830784",
        "revision": 1,
        "tags": [],
        "createdAt": "2018-07-06T13:25:51.000Z",
        "updatedAt": "2018-07-06T13:25:51.000Z"
      }
    },
    {
      "fields": {
        "title": "Nice Bicycle",
        "description": {
          "html": "<p>Aluminium Frame; Disc Brakes; Shimano XT;</p>",
          "plain": "Aluminium Frame; Disc Brakes; Shimano XT;"
        },
        "price": 1399,
        "relatedProducts": [],
        "rating": "3",
        "inStock": false,
        "images": [
          {
            "meta": {
              "id": "969465200912769024",
              "contentType": "_asset"
            }
          }
        ]
      },
      "meta": {
        "id": "969468642649575424",
        "contentType": "products",
        "repository": "968388392691830784",
        "revision": 2,
        "tags": [],
        "createdAt": "2018-07-09T12:28:56.000Z",
        "updatedAt": "2018-07-09T12:29:23.000Z"
      }
    },
    {
      "fields": {
        "title": "Hello World!",
        "date": "2018-07-06T13:00:00.000Z",
        "text": {
          "html": "<p>Lorem ipsum dolor sit amet, consetetur <strong>sadipscing elitr, sed diam</strong> nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.</p>",
          "plain": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\ntempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At\nvero eos et accusam et justo duo dolores et ea rebum."
        },
        "images": [
          {
            "meta": {
              "id": "968393840635547648",
              "contentType": "_asset"
            }
          }
        ],
        "relatedNews": [],
        "author": [
          {
            "meta": {
              "id": "968388973430968320",
              "contentType": "author"
            }
          }
        ]
      },
      "meta": {
        "id": "968390655497867264",
        "contentType": "news",
        "repository": "968388392691830784",
        "revision": 5,
        "tags": [],
        "createdAt": "2018-07-06T13:05:24.000Z",
        "updatedAt": "2018-07-06T13:23:08.000Z"
      }
    },
    {
      "fields": {
        "title": "Christian Schwerdt",
        "shorthand": "CS"
      },
      "meta": {
        "id": "968388973430968320",
        "contentType": "author",
        "repository": "968388392691830784",
        "revision": 1,
        "tags": [],
        "createdAt": "2018-07-06T12:58:43.000Z",
        "updatedAt": "2018-07-06T12:58:43.000Z"
      }
    },
    {
      "fields": {
        "title": "Beautiful Bicycle",
        "description": {
          "html": "<p>Aluminium Frame; Disc Brakes; Shimano XTR;</p>",
          "plain": "Aluminium Frame; Disc Brakes; Shimano XTR;"
        },
        "price": 1399,
        "relatedProducts": [],
        "rating": "5",
        "inStock": true,
        "images": [
          {
            "meta": {
              "id": "969465200912769024",
              "contentType": "_asset"
            }
          }
        ]
      },
      "meta": {
        "id": "969464870959452160",
        "contentType": "products",
        "repository": "968388392691830784",
        "revision": 4,
        "tags": [],
        "createdAt": "2018-07-09T12:13:57.000Z",
        "updatedAt": "2018-07-09T12:30:01.000Z"
      }
    }
  ]
}
```

{% endtab %}
{% endtabs %}

## Using multiple sorting parameters

You can apply multiple order parameters:

{% tabs %}
{% tab title="JavaScript" %}

```javascript
// Define query
var query = {
  "sorting": {
    "fields.title": 'ASC',
    "meta.createdAt": 'DESC'
  }
};

// Stringify and URI Encode the Object
query = encodeURIComponent(JSON.stringify(query));

// Submit the query
window.fetch(`https://api.cmft.io/v1/<repo>/documents?query=${query}`, {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl -g 'https://api.cmft.io/v1/<repo>/documents?sorting[fields.title]=ASC&sorting[meta.createdAt]=DESC' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}

```http
https://api.cmft.io/v1/<repo>/documents?key=<apiKey>&sorting[fields.title]=ASC&sorting[meta.createdAt]=DESC
```

{% endtab %}
{% endtabs %}

## Sample Request

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/demo/documents?sorting[fields.title]=ASC&sorting[meta.createdAt]=DESC&key=8MIO994Ley6bqyAlQAHqutiDh4g5Heck`

#### Query Parameters

| Name    | Type   | Description |
| ------- | ------ | ----------- |
| key     | string | API Key     |
| sorting | object |             |

{% tabs %}
{% tab title="200 " %}

```javascript
{
  "status": 200,
  "meta": {
    "count": 5,
    "limit": 25,
    "offset": 0,
    "total": 5
  },
  "data": [
    {
      "fields": {
        "title": "Simple Blog Post",
        "date": "2018-07-04T22:00:00.000Z",
        "text": {
          "html": "<h1>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.</h1>",
          "plain": "LOREM IPSUM DOLOR SIT AMET, CONSETETUR SADIPSCING ELITR, SED DIAM NONUMY EIRMOD\nTEMPOR INVIDUNT UT LABORE ET DOLORE MAGNA ALIQUYAM ERAT, SED DIAM VOLUPTUA. AT\nVERO EOS ET ACCUSAM ET JUSTO DUO DOLORES ET EA REBUM."
        },
        "images": [
          {
            "meta": {
              "id": "968393848009134080",
              "contentType": "_asset"
            }
          }
        ],
        "relatedNews": [
          {
            "meta": {
              "id": "968390655497867264",
              "contentType": "news"
            }
          }
        ],
        "author": [
          {
            "meta": {
              "id": "968388973430968320",
              "contentType": "author"
            }
          }
        ]
      },
      "meta": {
        "id": "968395804664537088",
        "contentType": "news",
        "repository": "968388392691830784",
        "revision": 1,
        "tags": [],
        "createdAt": "2018-07-06T13:25:51.000Z",
        "updatedAt": "2018-07-06T13:25:51.000Z"
      }
    },
    {
      "fields": {
        "title": "Nice Bicycle",
        "description": {
          "html": "<p>Aluminium Frame; Disc Brakes; Shimano XT;</p>",
          "plain": "Aluminium Frame; Disc Brakes; Shimano XT;"
        },
        "price": 1399,
        "relatedProducts": [],
        "rating": "3",
        "inStock": false,
        "images": [
          {
            "meta": {
              "id": "969465200912769024",
              "contentType": "_asset"
            }
          }
        ]
      },
      "meta": {
        "id": "969468642649575424",
        "contentType": "products",
        "repository": "968388392691830784",
        "revision": 2,
        "tags": [],
        "createdAt": "2018-07-09T12:28:56.000Z",
        "updatedAt": "2018-07-09T12:29:23.000Z"
      }
    },
    {
      "fields": {
        "title": "Hello World!",
        "date": "2018-07-06T13:00:00.000Z",
        "text": {
          "html": "<p>Lorem ipsum dolor sit amet, consetetur <strong>sadipscing elitr, sed diam</strong> nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.</p>",
          "plain": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\ntempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At\nvero eos et accusam et justo duo dolores et ea rebum."
        },
        "images": [
          {
            "meta": {
              "id": "968393840635547648",
              "contentType": "_asset"
            }
          }
        ],
        "relatedNews": [],
        "author": [
          {
            "meta": {
              "id": "968388973430968320",
              "contentType": "author"
            }
          }
        ]
      },
      "meta": {
        "id": "968390655497867264",
        "contentType": "news",
        "repository": "968388392691830784",
        "revision": 5,
        "tags": [],
        "createdAt": "2018-07-06T13:05:24.000Z",
        "updatedAt": "2018-07-06T13:23:08.000Z"
      }
    },
    {
      "fields": {
        "title": "Christian Schwerdt",
        "shorthand": "CS"
      },
      "meta": {
        "id": "968388973430968320",
        "contentType": "author",
        "repository": "968388392691830784",
        "revision": 1,
        "tags": [],
        "createdAt": "2018-07-06T12:58:43.000Z",
        "updatedAt": "2018-07-06T12:58:43.000Z"
      }
    },
    {
      "fields": {
        "title": "Beautiful Bicycle",
        "description": {
          "html": "<p>Aluminium Frame; Disc Brakes; Shimano XTR;</p>",
          "plain": "Aluminium Frame; Disc Brakes; Shimano XTR;"
        },
        "price": 1399,
        "relatedProducts": [],
        "rating": "5",
        "inStock": true,
        "images": [
          {
            "meta": {
              "id": "969465200912769024",
              "contentType": "_asset"
            }
          }
        ]
      },
      "meta": {
        "id": "969464870959452160",
        "contentType": "products",
        "repository": "968388392691830784",
        "revision": 4,
        "tags": [],
        "createdAt": "2018-07-09T12:13:57.000Z",
        "updatedAt": "2018-07-09T12:30:01.000Z"
      }
    }
  ]
}
```

{% endtab %}
{% endtabs %}


# Filters

## General Usage

## Request using query parameters

Query a `collection` and apply a filter for a specific field.

{% tabs %}
{% tab title="URL" %}

```http
https://api.cmft.io/v1/<repo>/collections/<collection>?key=<apiKey>&filters[0][*.fields.rating][equal]=5
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl -g 'https://api.cmft.io/v1/<repo>/collections/<collection>?filters[0][*.fields.category][equal]=spaceships' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
window.fetch('https://api.cmft.io/v1/<repo>/collections/<collection>?filters[0][*.fields.category][equal]=spaceships', {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Find all available[ relational operators](/apis/rest-api/filters/filters#relational-operators) (like `equal` or `greaterThan`, etc.) at the [API Reference](/apis/rest-api/filters/filters).
{% endhint %}

## Sample Request

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/demo/documents?filters[0][*.fields.rating][equal]=5&key=8MIO994Ley6bqyAlQAHqutiDh4g5Heck`

#### Query Parameters

| Name    | Type   | Description |
| ------- | ------ | ----------- |
| key     | string |             |
| filters | array  |             |

{% tabs %}
{% tab title="200 " %}

```javascript
{
  "status": 200,
  "meta": {
    "count": 1,
    "limit": 25,
    "offset": 0,
    "total": 1
  },
  "data": [
    {
      "fields": {
        "title": "Beautiful Bicycle",
        "description": {
          "html": "<p>Aluminium Frame; Disc Brakes; Shimano XTR;</p>",
          "plain": "Aluminium Frame; Disc Brakes; Shimano XTR;"
        },
        "price": 1399,
        "relatedProducts": [],
        "rating": "5",
        "inStock": true,
        "images": [
          {
            "meta": {
              "id": "969465200912769024",
              "contentType": "_asset"
            }
          }
        ]
      },
      "meta": {
        "id": "969464870959452160",
        "contentType": "products",
        "repository": "968388392691830784",
        "revision": 4,
        "tags": [],
        "createdAt": "2018-07-09T12:13:57.000Z",
        "updatedAt": "2018-07-09T12:30:01.000Z"
      }
    }
  ]
}
```

{% endtab %}
{% endtabs %}

## Request using stringified JSON &#x20;

{% tabs %}
{% tab title="JavaScript" %}

```javascript
// Define query
var query = {
  "filters": [
    {
      "*.fields.category": {
        "equal": "spaceships"
      }
    }
  ]
};

// Stringify and URI Encode the Object
query = encodeURIComponent(JSON.stringify(query));

// Make the request
window.fetch(`https://api.cmft.io/v1/<repo>/collections/<collection>?query=${query}`, {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}
{% endtabs %}

## Request using JSON POST Body

For large queries that would exceed the length limit of GET request, use the POST method to perform a request.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
// Define query
var query = {
  "filters": [
    {
      "*.fields.category": {
        "equal": "spaceships"
      }
    }
  ]
};

// Stringify Object
query = JSON.stringify(query);

// Make the request  
window.fetch('https://api.cmft.io/v1/<repo>/collections/<collection>', {
  method: 'post',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  },
  body: JSON.stringify(query)
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl 'https://api.cmft.io/v1/<repo>/collections/<collection>' \
  -H 'Authorization: <apiKey>' \
  -H 'Content-Type: application/json' \
  -X POST \
  -d '{
        "filters": {
          "*.fields.category": {
            "equal": "spaceships"
          }
        }
      }'
```

{% endtab %}
{% endtabs %}

## Using multiple filters in query

{% tabs %}
{% tab title="JavaScript" %}

```javascript
// Define query
var query = {
  "filters": [
    {
      "*.fields.category": {
          "equal": "spaceships"
        }
    },
    {
      "and": {
        "*.fields.color": {
          "equal": "red"
        }
      }
    },
    {
      "or": {
        "*.meta.id": {
          "equal": "314159265358979323"
        }
      }
    }
  ]
};

// Stringify and URI Encode the Object
query = encodeURIComponent(JSON.stringify(query));

// Submit the query
window.fetch(`https://api.cmft.io/v1/<repo>/collections/<collection>?query=${query}`, {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl -g 'https://api.cmft.io/v1/<repo>/aircrafts?filters[0][*.fields.category][equal]=spaceships&filters[1][and][*.fields.color][equal]=red' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}

```http
https://api.cmft.io/v1/<repo>/aircrafts?key=<apiKey>&filters[0][*.fields.category][equal]=spaceships&filters[1][and][*.fields.color][equal]=red
```

{% endtab %}
{% endtabs %}

## Filter documents by content type

{% tabs %}
{% tab title="JavaScript" %}

```javascript
// Define query
var query = {
	"filters": [
    {
      "*.meta.contentType": {
          "equal": "aircrafts"
        }
    }
  ]
};

// Stringify and URI Encode the Object
query = encodeURIComponent(JSON.stringify(query));

// Submit the query
window.fetch(`https://api.cmft.io/v1/<repo>/documents?query=${query}`, {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl -g 'https://api.cmft.io/v1/<repo>/documents?filters[0][*.meta.contentType][equal]=aircrafts' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}

```http
https://api.cmft.io/v1/<repo>/documents?key=<apiKey>&filters[0][*.meta.contentType][equal]=aircrafts
```

{% endtab %}
{% endtabs %}

## Filter documents by tags

{% tabs %}
{% tab title="JavaScript" %}

```javascript
// Define query
var query = {
  "includeTags": [
    "rocket-powered",
    "motorjet"
  ]
};

// Stringify and URI Encode the Object
query = encodeURIComponent(JSON.stringify(query));

// Submit the query
window.fetch(`https://api.cmft.io/v1/<repo>/documents?query=${query}`, {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="URL" %}

```http
https://api.cmft.io/v1/<repo>/documents?key=<apiKey>&includeTags=rocket-powered,flux-capacitor
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl 'https://api.cmft.io/v1/<repo>/documents?includeTags=rocket-powered,motorjet' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}
{% endtabs %}


# Includes

## Include relations by level

This query returns all of the related documents for the first level of a collection for a football team, `footballTeams`. The content type `footballTeam` has a `relations field` to link the players.

{% tabs %}
{% tab title="URL" %}

```http
https://api.cmft.io/v1/<repo>/footballTeams?key=<apiKey>&includes=1
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl 'https://api.cmft.io/v1/<repo>/footballTeams?includes=1' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
// Define query
let query = {
  "includes": 1
};

// Stringify and URI Encode the Object
query = encodeURIComponent(JSON.stringify(query));

// Submit the query
window.fetch(`https://api.cmft.io/v1/<repo>/footballTeams?query=${query}`, {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}
{% endtabs %}

## Sample Request

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/demo/documents?includes=1&key=8MIO994Ley6bqyAlQAHqutiDh4g5Heck`

#### Query Parameters

| Name | Type   | Description |
| ---- | ------ | ----------- |
| key  | string | API Key     |

{% tabs %}
{% tab title="200 " %}

```javascript
{
  "status": 200,
  "meta": {
    "count": 5,
    "limit": 25,
    "offset": 0,
    "total": 5
  },
  "data": [
    {
      "fields": {
        "title": "Christian Schwerdt",
        "shorthand": "CS"
      },
      "meta": {
        "id": "968388973430968320",
        "contentType": "author",
        "repository": "968388392691830784",
        "revision": 1,
        "tags": [],
        "createdAt": "2018-07-06T12:58:43.000Z",
        "updatedAt": "2018-07-06T12:58:43.000Z"
      }
    },
    {
      "fields": {
        "title": "Hello World!",
        "date": "2018-07-06T13:00:00.000Z",
        "text": {
          "html": "<p>Lorem ipsum dolor sit amet, consetetur <strong>sadipscing elitr, sed diam</strong> nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.</p>",
          "plain": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\ntempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At\nvero eos et accusam et justo duo dolores et ea rebum."
        },
        "images": [
          {
            "meta": {
              "id": "968393840635547648",
              "contentType": "_asset"
            }
          }
        ],
        "relatedNews": [],
        "author": [
          {
            "meta": {
              "id": "968388973430968320",
              "contentType": "author"
            }
          }
        ]
      },
      "meta": {
        "id": "968390655497867264",
        "contentType": "news",
        "repository": "968388392691830784",
        "revision": 5,
        "tags": [],
        "createdAt": "2018-07-06T13:05:24.000Z",
        "updatedAt": "2018-07-06T13:23:08.000Z"
      }
    },
    {
      "fields": {
        "title": "Simple Blog Post",
        "date": "2018-07-04T22:00:00.000Z",
        "text": {
          "html": "<h1>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.</h1>",
          "plain": "LOREM IPSUM DOLOR SIT AMET, CONSETETUR SADIPSCING ELITR, SED DIAM NONUMY EIRMOD\nTEMPOR INVIDUNT UT LABORE ET DOLORE MAGNA ALIQUYAM ERAT, SED DIAM VOLUPTUA. AT\nVERO EOS ET ACCUSAM ET JUSTO DUO DOLORES ET EA REBUM."
        },
        "images": [
          {
            "meta": {
              "id": "968393848009134080",
              "contentType": "_asset"
            }
          }
        ],
        "relatedNews": [
          {
            "meta": {
              "id": "968390655497867264",
              "contentType": "news"
            }
          }
        ],
        "author": [
          {
            "meta": {
              "id": "968388973430968320",
              "contentType": "author"
            }
          }
        ]
      },
      "meta": {
        "id": "968395804664537088",
        "contentType": "news",
        "repository": "968388392691830784",
        "revision": 1,
        "tags": [],
        "createdAt": "2018-07-06T13:25:51.000Z",
        "updatedAt": "2018-07-06T13:25:51.000Z"
      }
    },
    {
      "fields": {
        "title": "Beautiful Bicycle",
        "description": {
          "html": "<p>Aluminium Frame; Disc Brakes; Shimano XTR;</p>",
          "plain": "Aluminium Frame; Disc Brakes; Shimano XTR;"
        },
        "price": 1399,
        "relatedProducts": [],
        "rating": "5",
        "inStock": true,
        "images": [
          {
            "meta": {
              "id": "969465200912769024",
              "contentType": "_asset"
            }
          }
        ]
      },
      "meta": {
        "id": "969464870959452160",
        "contentType": "products",
        "repository": "968388392691830784",
        "revision": 4,
        "tags": [],
        "createdAt": "2018-07-09T12:13:57.000Z",
        "updatedAt": "2018-07-09T12:30:01.000Z"
      }
    },
    {
      "fields": {
        "title": "Nice Bicycle",
        "description": {
          "html": "<p>Aluminium Frame; Disc Brakes; Shimano XT;</p>",
          "plain": "Aluminium Frame; Disc Brakes; Shimano XT;"
        },
        "price": 1399,
        "relatedProducts": [],
        "rating": "3",
        "inStock": false,
        "images": [
          {
            "meta": {
              "id": "969465200912769024",
              "contentType": "_asset"
            }
          }
        ]
      },
      "meta": {
        "id": "969468642649575424",
        "contentType": "products",
        "repository": "968388392691830784",
        "revision": 2,
        "tags": [],
        "createdAt": "2018-07-09T12:28:56.000Z",
        "updatedAt": "2018-07-09T12:29:23.000Z"
      }
    }
  ],
  "includes": {
    "author": [
      {
        "fields": {
          "title": "Christian Schwerdt",
          "shorthand": "CS"
        },
        "meta": {
          "id": "968388973430968320",
          "contentType": "author",
          "repository": "968388392691830784",
          "revision": 1,
          "tags": [],
          "createdAt": "2018-07-06T12:58:43.000Z",
          "updatedAt": "2018-07-06T12:58:43.000Z"
        }
      }
    ],
    "news": [
      {
        "fields": {
          "title": "Hello World!",
          "date": "2018-07-06T13:00:00.000Z",
          "text": {
            "html": "<p>Lorem ipsum dolor sit amet, consetetur <strong>sadipscing elitr, sed diam</strong> nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.</p>",
            "plain": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\ntempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At\nvero eos et accusam et justo duo dolores et ea rebum."
          },
          "images": [
            {
              "meta": {
                "id": "968393840635547648",
                "contentType": "_asset"
              }
            }
          ],
          "relatedNews": [],
          "author": [
            {
              "meta": {
                "id": "968388973430968320",
                "contentType": "author"
              }
            }
          ]
        },
        "meta": {
          "id": "968390655497867264",
          "contentType": "news",
          "repository": "968388392691830784",
          "revision": 5,
          "tags": [],
          "createdAt": "2018-07-06T13:05:24.000Z",
          "updatedAt": "2018-07-06T13:23:08.000Z"
        }
      }
    ],
    "_asset": [
      {
        "fields": {
          "title": "picture-01.jpg",
          "description": "",
          "file": {
            "ext": "jpg",
            "path": "968388392691830784/968393840635547648/968393840656523264/picture-01.jpg",
            "size": 87129,
            "filename": "picture-01.jpg",
            "mimetype": "image/jpeg",
            "createdAt": "2018-07-06T13:18:03.000Z",
            "dimensions": {
              "width": 1920,
              "height": 1080
            },
            "originalFile": {
              "name": "picture-01.jpg",
              "size": 87129,
              "type": "image/jpeg"
            },
            "url": "https://images.cmft.io/968388392691830784/968393840635547648/968393840656523264/picture-01.jpg"
          }
        },
        "meta": {
          "id": "968393840635547648",
          "contentType": "_asset",
          "repository": "968388392691830784",
          "revision": 1,
          "tags": [],
          "createdAt": "2018-07-06T13:18:03.000Z",
          "updatedAt": "2018-07-06T13:18:03.000Z"
        }
      },
      {
        "fields": {
          "title": "picture-04.jpg",
          "description": "",
          "file": {
            "ext": "jpg",
            "path": "968388392691830784/968393848009134080/968393848034304000/picture-04.jpg",
            "size": 96972,
            "filename": "picture-04.jpg",
            "mimetype": "image/jpeg",
            "createdAt": "2018-07-06T13:18:03.000Z",
            "dimensions": {
              "width": 1920,
              "height": 1080
            },
            "originalFile": {
              "name": "picture-04.jpg",
              "size": 96972,
              "type": "image/jpeg"
            },
            "url": "https://images.cmft.io/968388392691830784/968393848009134080/968393848034304000/picture-04.jpg"
          }
        },
        "meta": {
          "id": "968393848009134080",
          "contentType": "_asset",
          "repository": "968388392691830784",
          "revision": 1,
          "tags": [],
          "createdAt": "2018-07-06T13:18:05.000Z",
          "updatedAt": "2018-07-06T13:18:05.000Z"
        }
      },
      {
        "fields": {
          "title": "photo-1507035895480-2b3156c31fc8",
          "description": "",
          "file": {
            "ext": "jpg",
            "path": "968388392691830784/969465200912769024/969465200933740544/photo-1507035895480-2b3156c31fc8",
            "size": 1171067,
            "filename": "photo-1507035895480-2b3156c31fc8",
            "mimetype": "image/jpeg",
            "createdAt": "2018-07-09T12:15:14.000Z",
            "dimensions": {
              "width": 4758,
              "height": 3172
            },
            "originalFile": {
              "name": "photo-1507035895480-2b3156c31fc8",
              "size": 1171067,
              "type": "image/jpeg"
            },
            "url": "https://images.cmft.io/968388392691830784/969465200912769024/969465200933740544/photo-1507035895480-2b3156c31fc8"
          }
        },
        "meta": {
          "id": "969465200912769024",
          "contentType": "_asset",
          "repository": "968388392691830784",
          "revision": 1,
          "tags": [],
          "createdAt": "2018-07-09T12:15:15.000Z",
          "updatedAt": "2018-07-09T12:15:15.000Z"
        }
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}

## Include relations from specific fields

If the content type footballTeam had more than one `relation field`, e.g. for its trainers or a city, we would query the relations only for the field `players`.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
// Define query
var query = {
  "includes": {
    "*.fields.players": 1
  }
};

// Stringify and URI Encode the Object
query = encodeURIComponent(JSON.stringify(query));

// Submit the query
window.fetch(`https://api.cmft.io/v1/<repo>/footballTeams?query=${query}`, {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl -g 'https://api.cmft.io/v1/<repo>/footballTeams?includes[*.fields.players]=1' \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}

```http
https://api.cmft.io/v1/<repo>/footballTeams?key=<apiKey>&includes[*.fields.players]=1
```

{% endtab %}
{% endtabs %}

## Sample Request

<mark style="color:blue;">`GET`</mark> `https://api.cmft.io/v1/demo/documents?includes[news.fields.author]=1&key=8MIO994Ley6bqyAlQAHqutiDh4g5Heck`

#### Path Parameters

| Name | Type   | Description |
| ---- | ------ | ----------- |
| key  | string | API Key     |

{% tabs %}
{% tab title="200 " %}

```
```

{% endtab %}
{% endtabs %}

## Including Assets in Documents

There is a easy way to have assets shipped within a field of a document, if you don't want to collect it from includes.&#x20;

You can do this for collections or single documents. In the following example we'll fetch a collection of football players and want to have their pictures included within the document. This makes iterating the list, e.g. for a list template, much more comfortable.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
// Define query
var query = {
  "embedAssets": true
};

// Stringify and URI Encode the Object
query = encodeURIComponent(JSON.stringify(query));

// Submit the query
window.fetch(`https://api.cmft.io/v1/<repo>/footballPlayers?query=${query}`, {
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': '<apiKey>'
  }
})
  .then(function (response) { return response.json() })
  .then(function (data) {
    console.log(data)
  })
```

{% endtab %}

{% tab title="CURL" %}

```bash
curl -g 'https://api.cmft.io/v1/<repo>/footballPlayers?embedAssets=true \
  -H 'Authorization: <apiKey>'
```

{% endtab %}

{% tab title="URL" %}

```http
https://api.cmft.io/v1/<repo>/footballPlayers?key=<apiKey>&embedAssets=true
```

{% endtab %}
{% endtabs %}

## Sample Request (TODO)

<mark style="color:blue;">`GET`</mark>&#x20;

#### Path Parameters

| Name | Type   | Description |
| ---- | ------ | ----------- |
| key  | string | API Key     |

{% tabs %}
{% tab title="200 " %}

```
```

{% endtab %}
{% endtabs %}


# Image Manipulation

## imgix

![](/files/-LEjfxIBawUfzF3Ruqq8)

Image assets uploaded to Comfortable are available via [imgix](https://www.imgix.com/), a powerful service offering real-time image processing like resizing, cropping, compression or optimization per device. All of the images are served by a fast CDN.

To learn what you can do with imgix, have a look at the [imgix API Reference](https://docs.imgix.com/apis/url).

## API Image URL

Comfortable automatically detects image assets and makes them available on the at the following URL by default, when you query a document:

{% code title="Image API Base URL" %}

```http
https://images.cmft.io/
```

{% endcode %}

Images can also be retrieved like regular assets at `https://assets.cmft.io/`, but without the power of the image manipulation API.

## Basic Examples

```javascript
// Image URL without manipulation
https://images.cmft.io/<repo>/awesome-image.jpg

// Resizing to 600px
https://images.cmft.io/<repo>/awesome-image.jpg?w=600

// Resizing to 300x100 with fit=crop
https://images.cmft.io/<repo>/awesome-image.jpg?fit=crop&w=300&h=100
```

To see more examples, visit the[ imgix API Reference](https://docs.imgix.com/apis/url).


# JavaScript

### Installation

#### NPM

Run this command:

```bash
npm install comfortable-javascript
```

#### For Usage in Browser

```markup
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/comfortable-javascript@latest/dist/comfortable.min.js"></script>
```

> **Note:** For a specific version replace `@latest` with the version of your choice.

The SDK will be available as a global variable called: `Comfortable`

#### **Downloadable version**

Check out the release Page: <https://github.com/cmftable/comfortable-javascript/releases>

### Include the dependency

```javascript
const Comfortable = require('comfortable-javascript');
```

### Connect to your Repository and make your first request:

```javascript
const api = Comfortable.api('<repository-api-id>', '<api-key>', options);

api.getDocuments()
  .then(result => {
    // futher implementation
  })
  .catch(err => {
    throw err;
  })
```

list of **options** can be found [here](/sdk/javascript#options).

**Full Query example**

```javascript
const api = Comfortable.api('<repository-api-id>', '<api-key>', options);

api.getDocuments({
  limit: 25,
  offset: 0,
  locale: 'en',
  includes: 2,
  includeTags: ['include', 'me'],
  excludeTags: ['exclude', 'this'],
  fields: 'meta,fields(title)',
  embedAssets: true,
  filters: new Comfortable.Filter()
    .addAnd('title', 'like', '%Hello%')
    .addOr('title', 'like', '%World%'),
  sorting: new Comfortable.Sorting()
    .add('date', 'asc')
    .add('title', 'asc'),
  search: 'sport -football +soccer'
})
  .then(result => {
    // futher implementation
  })
  .catch(err => {
    throw err;
  })
```

### Options

| Option   | Type    | Description                                                                     |
| -------- | ------- | ------------------------------------------------------------------------------- |
| useProxy | boolean | enables the usage of a Proxy Endpoint instead of using <https://api.cmft.io/v1> |
| proxy    | string  | Proxy Url (e.g. <https://custom-api.com/v1>)                                    |


# Query Options

## **limit**

**Type:** `number`\
\
defines the maximum number of documents that the API will return for your query.\
`default: 25`  `max: 1000`

**Example usage:**

```javascript
const api = Comfortable.api('<repository-api-id>', '<api-key>');

api.getDocuments({
  limit: 25
})
  .then(result => {
    // futher implementation
  })
  .catch(err => {
    throw err;
  })
```

**Available for:**

* [Query all Documents](/sdk/javascript/query-all-documents)
* [Query a Collection](/sdk/javascript/query-a-collection)

## **offset**

**Type:** `number`\
defines the number of documents which gets skipped in the datasets.\
`default: 0`&#x20;

**Example usage:**

```javascript
const api = Comfortable.api('<repository-api-id>', '<api-key>');

api.getDocuments({
  offset: 0
})
  .then(result => {
    // futher implementation
  })
  .catch(err => {
    throw err;
  })
```

**Available for:**

* [Query all Documents](/sdk/javascript/query-all-documents)
* [Query a Collection](/sdk/javascript/query-a-collection)

## **locale**

**Type:** `string` \
set the Language for  the receiving documents. \
`locale: 'en'` \
to receive all languages, set `locale: 'all'`

**Example usage:**

```javascript
const api = Comfortable.api('<repository-api-id>', '<api-key>');

api.getDocuments({
  locale: 'en'
})
  .then(result => {
    // futher implementation
  })
  .catch(err => {
    throw err;
  })
```

**Available for:**

* [Query all Documents](/sdk/javascript/query-all-documents)
* [Query a Collection](/sdk/javascript/query-a-collection)
* [Query a Single Document](/sdk/javascript/query-a-single-document)
* [Query and Alias](/sdk/javascript/query-an-alias)
* [Query an Asset](/sdk/javascript/query-an-asset)

## **includes**

**Type:** `number`  or `Comfortable.Include` \
defines the aggregation level of related documents\
or pick specific relations which should be aggregated in the query results&#x20;

**Example picking specific relations:**

```javascript
var options = {
    includes: new Comfortable.Include()
        .add('relatedNews')
}
```

**Example with aggregation level:**

```javascript
const api = Comfortable.api('<repository-api-id>', '<api-key>');

api.getDocuments({
  includes: 2 // aggregation level
})
  .then(result => {
    // futher implementation
  })
  .catch(err => {
    throw err;
  })
```

**Available for:**

* [Query all Documents](/sdk/javascript/query-all-documents)
* [Query a Collection](/sdk/javascript/query-a-collection)
* [Query a Single Document](/sdk/javascript/query-a-single-document)
* [Query and Alias](/sdk/javascript/query-an-alias)
* [Query an Asset](/sdk/javascript/query-an-asset)

## **includeTags**

**Type:** `array` \
include documents with certain tags to the query results.

**Example usage:**

```javascript
const api = Comfortable.api('<repository-api-id>', '<api-key>');

api.getDocuments({
  includeTags: ['include', 'me']
})
  .then(result => {
    // futher implementation
  })
  .catch(err => {
    throw err;
  })
```

**Available for:**

* [Query all Documents](/sdk/javascript/query-all-documents)
* [Query a Collection](/sdk/javascript/query-a-collection)

## **excludeTags**

**Type:** `array` \
exclude documents with certain tags from the query results.

**Example usage:**

```javascript
const api = Comfortable.api('<repository-api-id>', '<api-key>');

api.getDocuments({
  excludeTags: ['exclude', 'this']
})
  .then(result => {
    // futher implementation
  })
  .catch(err => {
    throw err;
  })
```

**Available for:**

* [Query all Documents](/sdk/javascript/query-all-documents)
* [Query a Collection](/sdk/javascript/query-a-collection)

## **fields**

**Type:** `string` \
hiding/masking specific fields/parts from the query result.

```javascript
var options = {
    fields: 'meta,fields(title,date)'
}
```

**Example usage:**

```javascript
const api = Comfortable.api('<repository-api-id>', '<api-key>');

api.getDocuments({
  fields: 'meta,fields(title)'
})
  .then(result => {
    // futher implementation
  })
  .catch(err => {
    throw err;
  })
```

**Available for:**

* [Query all Documents](/sdk/javascript/query-all-documents)
* [Query a Collection](/sdk/javascript/query-a-collection)
* [Query a Single Document](/sdk/javascript/query-a-single-document)
* [Query and Alias](/sdk/javascript/query-an-alias)
* [Query an Asset](/sdk/javascript/query-an-asset)

## **embedAssets**

**Type:** `boolean` \
embeds assets to the document which points on them.

**Example usage:**

```javascript
const api = Comfortable.api('<repository-api-id>', '<api-key>');

api.getDocuments({
  embedAssets: true
})
  .then(result => {
    // futher implementation
  })
  .catch(err => {
    throw err;
  })
```

**Available for:**

* [Query all Documents](/sdk/javascript/query-all-documents)
* [Query a Collection](/sdk/javascript/query-a-collection)
* [Query a Single Document](/sdk/javascript/query-a-single-document)
* [Query and Alias](/sdk/javascript/query-an-alias)

## **filters**

**Type:** `Comfortable.Filter` \
collect or reduce documents by certain field values&#x20;

**Example usage:**

```javascript
const api = Comfortable.api('<repository-api-id>', '<api-key>');

api.getDocuments({
  filters: new Comfortable.Filter()
        .addAnd('title', 'like', '%Hello%')
        .addAnd('date', 'greaterThan', '2018-07-04')
        .addOr('topNews', 'equal', true)
})
  .then(result => {
    // futher implementation
  })
  .catch(err => {
    throw err;
  })
```

**Available for:**

* [Query all Documents](/sdk/javascript/query-all-documents)
* [Query a Collection](/sdk/javascript/query-a-collection)

## **sorting**

**Type:** `Comfortable.Sorting` \
sort query results ascending or descending by certain fields

**Example usage:**

```javascript
const api = Comfortable.api('<repository-api-id>', '<api-key>');

api.getDocuments({
  sorting: new Comfortable.Sorting()
    .add('date', 'asc')
    .add('title', 'asc')
})
  .then(result => {
    // futher implementation
  })
  .catch(err => {
    throw err;
  })
```

**Available for:**

* [Query all Documents](/sdk/javascript/query-all-documents)
* [Query a Collection](/sdk/javascript/query-a-collection)

## **search**

**Type:** `string`&#x20;

**Example usage:**

```javascript
const api = Comfortable.api('<repository-api-id>', '<api-key>');

api.getDocuments({
  search: 'sport -football +soccer'
})
  .then(result => {
    // futher implementation
  })
  .catch(err => {
    throw err;
  })
```

**Available for:**

* [Query all Documents](/sdk/javascript/query-all-documents)
* [Query a Collection](/sdk/javascript/query-a-collection)


# Query All Documents

Here we will show you how you can query all documents from your repository.

```javascript
var options = {};

api.getDocuments(options)
  .then(result => {
    // futher implementation
  })
  .catch(err => {
    throw err;
  })
```

### Available query options:

* [limit](/sdk/javascript/query-options#limit)
* [offset](/sdk/javascript/query-options#offset)
* [locale](/sdk/javascript/query-options#locale)
* [includes](/sdk/javascript/query-options#includes)
* [includeTags](/sdk/javascript/query-options#includetags)
* [excludeTags](/sdk/javascript/query-options#excludetags)
* [fields](/sdk/javascript/query-options#fields)
* [embedAssets](/sdk/javascript/query-options#embedassets)
* [filters](/sdk/javascript/query-options#filters)
* [sorting](/sdk/javascript/query-options#sorting)
* [search](/sdk/javascript/query-options#search)


# Query a Collection

Here we will show you how you can query all documents defined by a collection

```javascript
var options = {};

api.getCollection('blogPosts', options)
  .then(result => {
    // futher implementation
  })
  .catch(err => {
    throw err;
  })
```

### Available query options:

* [limit](/sdk/javascript/query-options#limit)
* [offset](/sdk/javascript/query-options#offset)
* [locale](/sdk/javascript/query-options#locale)
* [includes](/sdk/javascript/query-options#includes)
* [includeTags](/sdk/javascript/query-options#includetags)
* [excludeTags](/sdk/javascript/query-options#excludetags)
* [fields](/sdk/javascript/query-options#fields)
* [embedAssets](/sdk/javascript/query-options#embedassets)
* [filters](/sdk/javascript/query-options#filters)
* [sorting](/sdk/javascript/query-options#sorting)
* [search](/sdk/javascript/query-options#search)


# Query a Single Document

Here we will show you how you retrieve a single document.

```javascript
var options = {};

api.getDocument('969464870959452160', options)
  .then(result => {
    // futher implementation
  })
  .catch(err => {
    throw err;
  })
```

### Available query options:

* [locale](/sdk/javascript/query-options#locale)
* [includes](/sdk/javascript/query-options#includes)
* [fields](/sdk/javascript/query-options#fields)
* [embedAssets](/sdk/javascript/query-options#embedassets)


# Query an Alias

Here we will show you how you retrieve a single document behind an alias.

```javascript
var options = {};

api.getAlias('aboutUs', options)
  .then(result => {
    // futher implementation
  })
  .catch(err => {
    throw err;
  })
```

### Available query options:

* [locale](/sdk/javascript/query-options#locale)
* [includes](/sdk/javascript/query-options#includes)
* [fields](/sdk/javascript/query-options#fields)
* [embedAssets](/sdk/javascript/query-options#embedassets)


# Query an Asset

Here we will show you how you retrieve a single asset

```javascript
var options = {};

api.getAsset('968393848009134080', options)
  .then(result => {
    // futher implementation
  })
  .catch(err => {
    throw err;
  })
```

{% hint style="success" %}
You can also use the query option `embedAssets: true` to include assets when making queries for documents.
{% endhint %}

### Available query options:

* [locale](/sdk/javascript/query-options#locale)
* [fields](/sdk/javascript/query-options#fields)


# Example: Filters

Use the following query option to apply filters:

### **filters**

**Type:** `Comfortable.Filter` \
collect or reduce documents by certain field values&#x20;

```javascript
const api = Comfortable.api('<repository-api-id>', '<api-key>');

var options = {
    filters: new Comfortable.Filter()
        .addAnd('title', 'like', '%Hello%')
        .addAnd('date', 'greaterThan', '2018-07-04')
        .addOr('topNews', 'equal', true)
        /** filter by document id **/
        .addAnd('id', 'equals', '123123123432425', 'meta')
        /** filter blogPosts (contentType) by title **/
        .addAnd('title', 'like', '%Hello%', 'fields', 'blogPosts')
        
        // syntax
        // .addAnd(property: string(<id|title|...>), operator: string(<equals|like|...>), value: any, context: string(<fields|meta>), contentType: string(<*|contentTypeApiId>))
        // .addOr(property: string(<id|title|...>), operator: string(<equals|like|...>)), value: any, context: string(<fields|meta>), contentType: string(<*|contentTypeApiId>))
}

api.getDocuments(options)
  .then(result => {
    // futher implementation
  })
  .catch(err => {
    throw err;
  })
```


# Example: Sorting

Use the following query option to apply sorting:

### **sorting**

**Type:** `Comfortable.Sorting` \
sort query results ascending or descending by certain fields

#### Parameters

| Name             | Default  | Type   | Description                                                            |
| ---------------- | -------- | ------ | ---------------------------------------------------------------------- |
| **field API ID** |          | String | The field API ID sorting is applied to                                 |
| **direction**    | `desc`   | String | Sorting direction                                                      |
| **context**      | `fields` | String | Context can be either `meta` (meta fields) or `fields`(content fields) |

```javascript
const api = Comfortable.api('<repository-api-id>', '<api-key>');

var options = {
    sorting: new Comfortable.Sorting()
        .add('createdAt', 'asc', 'meta')
        .add('title', 'asc')
}


api.getDocuments(options)
  .then(result => {
    // futher implementation
  })
  .catch(err => {
    throw err;
  })
```


# Nuxt.js

## Installation

### Installation with yarn <a href="#installation-with-yarn" id="installation-with-yarn"></a>

```bash
yarn add comfortable-nuxt
```

### Installation with npm <a href="#installation-with-npm" id="installation-with-npm"></a>

```bash
npm install comfortable-nuxt
```

### GitHub Repo

{% embed url="<https://github.com/cmftable/comfortable-nuxt>" %}

### The Nuxt.js Project

{% embed url="<https://nuxtjs.org/>" %}

## Setup

{% code title="nuxt.config.js" %}

```javascript
module.exports = {
  modules: [
    'comfortable-nuxt',
  ],

  comfortable: {
    apiKey: 'YOUR_TOKEN',
    repositoryApiId: 'YOUR_REPO_ID'
    // options: {
    //    useProxy: true,
    //    proxy: 'https://your-proxy.com/v1'
    // }
  }
}
  ...
}
```

{% endcode %}

## Usage

The comfortable-nuxt module is a wrapper for the [comfortable-javasctipt SDK](https://github.com/cmftable/comfortable-javascript). \
The documentation with methods and examples for the JavaScript SDK can be found here: <https://docs.comfortable.io/sdk/javascript>

The nuxt module initializes the SDK automatically and exposes API methods as `$cmft` as a Promise. \
SDK Methods like **filters** and **sorting** are exposed as `$Comfortable`.

## Examples

For the following examples we'll use the `await` operator and fetch content for a page, but you can use `$cmft` like a regular promise with any component.

### Fetching documents for a page

```javascript
  export default {
    async asyncData(context) {
      const options = {
        embedAssets: true
      };
      const documents = await context.$cmft.getDocuments(options);

      return {
        documents: documents.data,
      }
    }
  }

  /**
   * With filters:
   */

   export default {
    async asyncData(context) {
      const options = {
        embedAssets: true,
        filters: new context.$Comfortable.Filter()
        .addAnd('slug', 'equal', context.params.slug)
      };
      const documents = await context.$cmft.getDocuments(options);

      return {
        documents: documents.data,
      }
    }
  }
```

### Fetching a collection

```javascript
  export default {
    async asyncData(context) {
      const options = {
        embedAssets: true
      };
      const documents = await context.$cmft.getCollection('flyingCars', options);

      return {
        documents: documents.data,
      }
    }
  }
```

### Fetching a single document

```javascript
  export default {
    async asyncData(context) {
      const options = {
        embedAssets: true
      };
      const document = await context.$cmft.getDocument('DOCUMENT_ID', options);

      return {
        document: document.data,
      }
    }
  }
```

### Fetching a document with alias

```javascript
  export default {
    async asyncData(context) {
      const options = {
        embedAssets: true
      };
      const page = await context.$cmft.getAlias('deLorean', options);

      return {
        page: page.data,
      }
    }
  }
```


# PHP

The Comfortable PHP SDK can be installed with [Composer](https://getcomposer.org/). Run this command:

```bash
composer require comfortable/php-sdk
```

### Usage

> **Note:** This version of the SDK requires PHP 5.6 or greater.

#### Include the dependency:

```php
<?php
require_once __DIR__ . '/vendor/autoload.php'; // change path as needed

use Comfortable;
```

#### Connect to your Repository and make your first request: <a href="#connect-to-your-repository-and-make-your-first-request" id="connect-to-your-repository-and-make-your-first-request"></a>

```php
$api = Comfortable\Api::connect('<repository-api-id>', '<api-key>');

try {
  // get all documents stored in comfortable (default limit: 25)
  $results = $api->getDocuments()->execute();  
} catch (\RuntimeException $e) {
  echo 'Comfortalbe SDK returned an error: ' . $e->getMessage();
  exit;
}
```

####

#### Full usage Example

```php
<?php

use Comfortable\Api;
use Comfortable\Filter;
use Comfortable\Sorting;
use Comfortable\Includer;

// connect to your repository
$api = Api::connect('<repository-api-id>', '<api-key>');

$results = $api->getDocuments()
  ->limit(10) // limits the result to 10
  ->offset(25) // skip the first 25 documents
  ->locale('en') // receive the document in english
  ->includes(2) // includes 2 levels of relations
  ->includeByFields(
    (new Includer)
      ->add('relatedNews') // include only the relatedNews instead of all relations
  )
  ->embedAssets(true) // embed assets directly inside a document instead of using them as includes
  ->includeTags(["include", "me"]) // include documents with the "include" or "me" tag
  ->excludeTags(["exclude"]) // exclude documents with the "exclude" tag
  ->search('this is a fulltext search') // perform a fulltext search
  ->fields('fields(title)') // name the fields you want to receive by the api
  ->sorting(
    (new Sorting)
      ->add('id', 'ASC', 'meta') // sort the result ascending by id
      ->add('title', 'DESC') // sort the result descending by title
   )
   ->filter(
     (new Filter)
       ->addAnd('date', 'greaterThan', '01-02-2018') // return only documents greaterThan given date
     )
   ->execute();
```


# Query All Documents

Here we will show you how you can query all documents from your repository.

```php
<?php
$documents = $api->getDocuments()->execute();
```


# Query Single Document

Here we will show you how you retrieve a single document.

```php
<?php
$document = $api->getDocument('981075884394024960')->execute();
```


# Query Collection

Here we will show you how you can query all documents defined by a collection

```php
<?php
$documents = $api->getCollection('topNews')->execute();
```


# Query an Alias

Here we will show you how you retrieve a single document behind an alias.

```php
<?php
$document = $api->getAlias('legalNotice')->execute();
```


# Query an Asset

Here we will show you how you retrieve a single asset

```php
<?php
$document = $api->getAsset('982566074363023360')->execute();
```


# Fulltext Search

Here we will show you how you perform a Fulltext Search against documents.

```php
<?php
// perform a fulltext search against all documents
$documents = $api->getDocuments()
    ->search('Football')
    ->execute();

// perform a fulltext search against collection documents
$collectionDocs = $api->getCollection('topNews')
    ->search('Football -soccer')
    ->execute();
```


# Query by Fields

Here we will show you how you can query documents by a specific fields

```php
<?php
use Comfortable\Filter;

$results = $api->getDocuments()
  ->filter(
    (new Filter)
      ->addAnd('title', 'like', '%Football%')
      ->addOr('title', 'like', '%Soccer%')
    )
    ->execute();

// $results will hold all documents which contains "Football" or "Soccer" in it its title field 
```


# Query by Type

Here we will show you how you can query documents by its content-type.

```php
<?php
use Comfortable\Filter;

$type = 'news';
$results = $api->getDocuments()
  ->filter(
    (new Filter)
      ->addAnd('contentType', 'equal', $type, 'meta')
    )
    ->execute();
```


# Query by Id

Here we will show you how you can query a single or multiple documents by id.

```php
<?php
use Comfortable\Filter;

$id = '981075884394024960';
$results = $api->getDocuments()
  ->filter(
    (new Filter)
      ->addAnd('id', 'equal', $id, 'meta')
    )
    ->execute();
```

## Query by Ids

```php
<?php
use Comfortable\Filter;

$ids = ['981075884394024960', '982513777712959488'];
$results = $api->getDocuments()
  ->filter(
    (new Filter)
      ->addAnd('id', 'in', $ids, 'meta')
    )
    ->execute();
```


# Query by Tags

Here we will show you how you can perform queries based on tags

### Query by IncludeTags

```php
<?php
// $results will be include all documents with the tag "include" or "me"
$results = $api->getDocuments()
  ->includeTags(["include", "me"])
  ->execute();
```

### Query by ExcludeTags

```php
<?php
// $results will exclude all documents with the "exclude me" tag
$results = $api->getDocuments()
  ->excludeTags(["exclude"])
  ->execute();
```

### Combine IncludeTags & ExcludeTags

```php
<?php
// $results will hold all documents with the tag "sports",
// except the documents with the "soccer" tag.
$results = $api->getDocuments()
  ->includeTags(["sports"])
  ->excludeTags(["soccer"])
  ->execute();
```


# Vue Blog Example

In this guide we're going to demonstrate how to quickly build a basic CMS-Powered Blog with Vue.js and Comfortable.

If you're new to Vue.js, we'd recommend checking out this [great Vue.js introduction](https://vuejs.org/v2/guide/) first.

The complete code for this tutorial is [available on GitHub](https://github.com/cmftable/comfortable-vue-blog). You can also explore and play around with it on [CodeSandbox](https://codesandbox.io/s/9jwr8321qr).

## Preparing the CMS – Creating content models

To get started, we'll need a repository for our project and some models for the content. If you don't have a Comfortable account yet, you can [sign up here](https://app.comfortable.io/sign-up). If you already have an account, [log in](https://app.comfortable.io/sign-in). 🙂

Create a new repository for your blog. When you're done, head over to the `Content Types` page to create some models.

![](/files/-LQ8_oHt79ZpXwB-rqn1)

### The Author Model

First things first, for any blogpost there is someone who wrote it. Therefore, we're going to create a Content Type `Author`.

On the Content Types page, click the green `+ Add Content Type` button on the top to create a new type `Author`. Make sure you have checked `Create a Collection` field.

![](/files/-LQ8_vlYVTyNaSAlj4Py)

Next, add some fields for this content type. We'll need:

* `Name` – Field Type: `Text` (Single line) *Hint: You can simply rename the* `Title` *field, which is created automatically for new Content Types.*
* `Avatar` – Field Type: `Asset`

### The Blogpost Model

Switch back to the Content Types page and add another type `Blogpost`. Again, make sure to have the field `Create a Collection` enabled. This time, add the following fields:

* `Title` – Field Type: `Text` (Single line) This will be the title for any blogpost
* `Slug` – Field Type: `Text` (Single line) We're going to create a URL for each Blogpost from this field.
* `Image` – Field Type: `Asset` An image to show with each post.
* `Content` – Field Type: `Richtext` This field will contain a Blogposts main content.
* `Author` – Field Type: `Relation` As each Blogpost has an Author, we'll connect both with a relation. In the field configuration, we'll set a one-to-one relation for the field and select the Content Type `Author`, that we have just created.

![](/files/-LQ8a0WYCD7_0TIOLoac)

### About Page

The last Content Type we are going to create is really simple. We call it About page in this example, but keep it general so you could create standardised pages from it. Create a new type `Page` and skip the Checkbox `Create a Collection` this time. We need the following fields:

* `Title` – Field Type: `Text` (Single line) This will be the page title
* `Content` – Field Type: `Richtext` This field contains the main content for the page

## Create some content

We need to create some content to display in the example. Let's start with some blog posts. Click the `+` Button on the page header and create a new document of type `Blogpost`.

For the first post, you'll also have to create a new `Author`. You can do while creating a blog post, by simply clicking the button `Create new Entry` on the Author field. For the next posts use `Select Relations` to pick the Author from a list.

For the demo, we'd recommend to have at least 3-5 pieces of content in your blog.

Also, don't forget to add the About page.

### Collections & Pages: The Content Tree

As already mentioned, there is a checkbox `Create a Collection` when creating a new Content Type. This option will add a Collection to the Content Tree for this particular type.

Each collection comes with an individual endpoint, which makes it very easy to retrieve content or change the content output.

You can also link individual pages to the Content Tree and create an endpoint for a single page. We'll do that for the About page. Create your page the same way you created blog posts and when you're done, click the `Add Link` Button above the Content Tree:

![](/files/-LQ8a5PlbXxXaNXgeZr9)

![](/files/-LQ8a9Vl6V_KiTcJmYCx)

Any collection or document node on the Content Tree has an individual endpoint to fetch content. If you're using the SDK, like in this example, all we need is the `API ID`. We'll come back to this later.

### Summary

By the end of this section we've learned:

* How to create Content Types (Models)
* How to create Documents (Content)
* How to create Collections and single linked Documents in the Content Tree

## Installing Vue and Dependencies

We'll use the Vue CLI for this example. Run the following command in your terminal, if you haven't already installed Vue CLI.

`npm install -g @vue/cli`

Next, create the Vue app

`vue create --default comfortable-vue-blog`

Switch to the directory that was created by the CLI

`cd comfortable-vue-blog`

We'll use the Vue router for this project

`vue add router`

The CLI will probably ask you if you'd like use the HTML5 history mode. Usually you'd want to choose 'Yes', and redirect any request to `index.html`. You can read more about the HTML5 History Mode in the [documentation](https://router.vuejs.org/guide/essentials/history-mode.html).

Let's install the [Comfortable JavaScript SDK](https://github.com/cmftable/comfortable-javascript) for a convenient way to interact with the API.

`npm install comfortable-javascript --save`

We'll also use lodash

`npm install lodash --save`

## Coding the app

Enough preparations, start your favourite IDE let's finally start hacking. 😃

*Tip: There are some great IDE extensions for Vue to provide syntax highlighting, autocompletion, etc. We'd highly recommend to check them out.*

### Getting started

`router.js`

Let's prepare the routing with Vue Router first. We'll use the existing base route `/` to display a paginated list of all blogposts and create a new route `/blog/:slug` to display individual posts. If you want to learn more about the Vue Router, you can find their documentation here: <https://router.vuejs.org/>

```jsx
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
import BlogPost from './views/BlogPost.vue'
import Page from './views/Page.vue'

Vue.use(Router)

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/blog/:slug',
      name: 'blog-post',
      component: BlogPost
    },
    {
      path: '/:slug',
      name: 'page',
      component: Page
    }
  ]
})
```

`comfortable.js`

Now we're going to set up the Comforable SDK for the app. Create a new file comfortable.js in `src`:

```javascript
import Comfortable from 'comfortable-javascript';

export const comfortable = Comfortable.api('<Your API ID>', '<Your API KEY>');
```

Import this file into any component you want to use Comfortable.

To find your repositories API ID go to the `Settings` page. You'll also find your API Keys in Settings>API Keys.

`App.vue`

The `App` components purpose is to display a header on top of each page and to provide the `<router-view />` component to display components we've defined in `router.js`.

```jsx
<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>
    <main>
      <router-view/>
    </main>
  </div>
</template>

<style>
  @import url('<https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css>');

  #app {
    font-family: 'Avenir', Helvetica, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-align: center;
    color: #2c3e50;
  }

  #nav {
    background-color: #F5F5F5;
    color: #555555;
  }

  #nav a {
    color: #555555;
    text-decoration: none;
    display: inline-block;
    padding: .5rem;
  }

  #nav a:hover {
    text-decoration: underline;
  }

  .content-wrapper {
    max-width: 840px;
    margin: 42px auto;
    padding: 21px;
  }

  a {
    text-decoration: none;
  }
</style>
```

### Display a list of posts

`Home.vue`

On our blog page we want to display all posts and a `Load more` button to paginate the list. Remember we told Vue to load the `Home` Component for the `/` in `router.js`? Now let's have a look at the `Home` Component:

```jsx
<template>
  <div class="home content-wrapper">
    <div v-for="post in posts" :key="post.meta.id">
      <router-link :to="`/blog/${post.fields.slug}`">
        <article>
          <div class="image">
            <img :alt="post.fields.title" :src="`${post.fields.image[0].fields.file.url}?w=840&h=400&fit=crop`">
          </div>
          <h2>{{ post.fields.title }}</h2>
        </article>
      </router-link>
    </div>
    <button v-if="totalPosts > posts.length" @click="getPosts">
      {{loading ? 'Loading...' : 'Load more posts'}}
    </button>
  </div>
</template>

<script>
  import { comfortable } from '@/comfortable.js'

  export default {
    name: 'home',
    data() {
      return {
        posts: [],
        totalPosts: 0,
        loading: false
      }
    },
    methods: {
      getPosts() {
        this.loading = true;

        const options = {
          embedAssets: true,
          offset: this.posts.length
        };

        comfortable.getCollection('blogpost', options)
        .then(result => {
          this.posts.push(...result.data);
          this.totalPosts = result.meta.total;
          this.loading = false;
        })
        .catch(err => {
          this.loading = false;
          throw err;
        })
      }
    },
    created() {
      this.getPosts();
    }
  }
</script>

<style>
  article {
    margin-bottom: 42px;
    text-align: left;
  }

  article .image {
    width: 100%;
  }

  article .image img {
    max-width: 100%;
    height: auto;
  }

  article h2 {
    color: #2d2d33;
  }

  .home article {
    border: 1px solid #ccc;
  }

  .home article h2 {
    margin-left: 21px;
  }
</style>
```

**What's happening here?**

In short: In the script part, we fetch a list of post items from the API. For each post, Vue creates an article and displays its content, wrapped into a link to the single view.

By keeping track of the total number of posts stored in Comfortable, we are able to determine wether to display a `Load more` button. When the button gets clicked, the next set of post items gets fetched from the API.

### Display a single post

Now that we have a list of posts, we want to be able to load single blogposts on a separate page. We've already used the `Slug` field from the `Blogpost` model to build links on the posts list page. As you remember in `router.js`, we have defined the second part of a single view URL to be a route parameter `:slug`. So the piece of information we need to retrieve a single post is present as `this.$route.params.slug`. All we have to do now, is use a filter query.

`BlogPost.vue`

```jsx
<template>
  <div class="post" v-if="post && author">
    <article>
      <div class="image">
        <img :alt="post.fields.title" :src="`${post.fields.image[0].fields.file.url}?w=1680&h=750&fit=crop`">
      </div>
      <div class="content-wrapper">
        <h2>{{ post.fields.title }}</h2>
        <div class="content" v-html="post.fields.content.html"></div>
        <div class="author">
          <img :src="`${author.fields.avatar[0].fields.file.url}?w=30&h=30&fit=crop`" alt="author.fields.name"> Written by {{ author.fields.name }}
        </div>
      </div>
    </article>
  </div>
</template>

<script>
  import Comfortable from 'comfortable-javascript';
  import { comfortable } from '@/comfortable.js'
  import _ from 'lodash';

  export default {
    name: 'blogPost',
    data() {
      return {
        post: null,
        author: null,
      }
    },
    methods: {
      getPost() {
        const options = {
          embedAssets: true,
          includes: 1,
          filters: new Comfortable.Filter()
            .addAnd('slug', 'equal', this.$route.params.slug)
        };

        comfortable.getDocuments(options)
        .then(result => {
          this.post = result.data[0];
          this.author = _.find(result.includes.author, { meta: { id: this.post.fields.author[0].meta.id } });
        })
        .catch(err => {
          throw err;
        })
      }
    },
    created() {
      this.getPost();
    }
  }
</script>

<style>
  .post .image{
    width: 100%;
  }

  .post .image img{
    width: 100%;
  }

  .post .author img {
    margin-top: 10px;
  }

  .post .author img {
    display: inline-block;
    margin-bottom: -8px;
    margin-right: 10px;
    border-radius: 50%;
  }
</style>
```

### The About page

At last we add the About page. We're going to create this page from the basic Content Type `Page`. This way you are able create several pages from the same Component and Content Type.

The important part here is, that we're fetching this page by a route parameter again, and this time we'll use the document alias that we've created in the Content Tree, instead of a filter.

`Page.vue`

```jsx
<template>
  <div class="page" v-if="page">
    <article>
      <div class="content-wrapper">
        <h2>{{ page.fields.title }}</h2>
        <div class="content" v-html="page.fields.content.html"></div>
      </div>
    </article>
  </div>
</template>

<script>
  import { comfortable } from '@/comfortable.js'

  export default {
    name: 'page',
    data() {
      return {
        page: null
      }
    },
    methods: {
      getPage() {
        comfortable.getAlias(this.$route.params.slug)
        .then(result => {
          this.page = result;
        })
        .catch(err => {
          throw err;
        })
      }
    },
    created() {
      this.getPage();
    }
  }
</script>
```

## Running and building the app

That's it 🙂 Run `npm run serve` to view the app in your browser, or `npm run build` to get a deployable blog from your codebase. Don't forget to handle the [HTML5 History Mode](https://router.vuejs.org/guide/essentials/history-mode.html#example-server-configurations) on your server.

Happy coding!

[Join our Slack team](https://slack-comfortable.herokuapp.com/) to get in touch with the community and ask questions!

## Bonus: Serverless deployment with Netlify

Deploying your Blog with Netlify is perfect if you don't want to handle a server or webspace yourself. You'll be provided with a really fast and reliable deployment and hosting for any static website.

Using Netlify for this example is as simple as clicking this [link](https://app.netlify.com/start/deploy?repository=https://github.com/cmftable/comfortable-vue-blog). You'll be asked to connect your GitHub or GitLab account to let Netlify create a Repository on your behalf. The Repository will include the code from this example and you can change it and play around as you like.

That's all. Your Blog will be available within seconds. 🚀 😊


