# Neurelo API Reference (REST)

## Introduction

This API documentation is intended to provide an overview of how Neurelo generates APIs based on your data definition. It will go into detail on how to perform operations using a sample schema definition. You can then apply these concepts and patterns to your own schema. Reference documentation generated specifically for your data definition is included in your specific project on Neurelo's Data Access Platform.

Neurelo provides two types of APIs that are automatically generated and available for use.&#x20;

* The REST API has predictable resource-oriented URLs, accepts JSON-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes, authentication, and verbs.&#x20;
* The GraphQL API exposes a single endpoint where clients can send GraphQL queries or mutations and a strictly-typed schema that be be shared across consumers.&#x20;

You can toggle between the GraphQL and REST API documentation when reviewing the documentation in the platform.

{% hint style="info" %}
Reference - [Neurelo Supported Databases and Versions](https://docs.neurelo.com/references/supported-databases)
{% endhint %}

## API Headers

### <mark style="color:green;">Authentication</mark>

API calls to Neurelo are authenticated using an API Key for all API requests. This api key defines which environment the API request is interacting with. You can create an api key under the settings of the specific environment that you are trying to access. API Keys cannot be used across environments.

To utilize an api key, include it as a header with your API Request, using `X-API-KEY` as the key.

### <mark style="color:green;">Query Strategy</mark>

Neurelo generated APIs can be configured to fetch data using either the `selects` or `joins` strategy. The main difference is that `selects` executes multiple queries behind the scenes to gather the desired data, whereas `joins` uses a single query when possible using database-level joins, to fetch the results. By default, `joins` strategy is enabled. This can be changed using an HTTP header.

For example, to fetch all the users and their corresponding posts using `selects` strategy:

```
curl https://$ENV_API_URL/rest/user
    --url-query select='{"$related":true}'
    --header 'X-READ-STRATEGY: selects'
```

Similarly, `'X-READ-STRATEGY: joins'` can be used.

`joins` strategy is suitable for scenarios where you want to minimize the number of queries made to the database, especially when the relation expressions are relatively simpler, and there's less risk of data duplication.

`selects` can be faster in scenarios with multiple many-to-many or one-to-many relations. This is because the database's flat record list from joins can sometimes contain a significant amount of repeated information. Despite joins' quick execution in the database, the process of moving and parsing this redundant data into JSON can incur high costs.

{% hint style="info" %}
Only `GET`calls support this feature.
{% endhint %}

### <mark style="color:green;">Query Viz</mark>

To review the database queries that Neurelo will be sending to your data source, you can use the `X-QUERY-VIZ` HTTP header. For example, to enable query viz while fetching all the users and their corresponding posts:

```
curl https://$ENV_API_URL/rest/user
    --url-query select='{"$related":true}'
    --header 'X-QUERY-VIZ: true'
```

This header will return both the query string and its corresponding query parameters.

Similarly, `'X-QUERY-VIZ: false'` can be used to disable the viz. By default, this header is disabled.

{% hint style="danger" %}
IMPORTANT: When query viz is enabled, the queries will still run against your selected environment, and they may modify data in that environment depending on your queries.
{% endhint %}

## Operations

### <mark style="color:green;">Creating Objects</mark>

You can create objects by making a `POST` request to the `/rest/object`, where object is your object’s name. The request body should be a JSON array of objects representing the objects that you are looking to insert.

In this example, we will use a sample schema with a User object and a BlogPost object. A user has many posts, and a post has one user.

#### <mark style="color:purple;">Creating one object</mark> (`CreateInput`)

To create a single user, assuming that we have the sample schema running in an environment, we can issue a POST request to `/rest/user/__one` with the body set to a User object. For example

```
curl -X POST https://$ENV_API_URL/rest/user/__one
    --url-query select='{"$scalars":true}'
    --json '{name: "Admin User", country: "United States"}'
```

The response from this insert request will look like this, which is the object that was inserted:

```
{
  "data": {
    "id": 1,
    "name": "Admin User",
    "country": "United States"
  }
}
```

#### <mark style="color:purple;">Creating multiple objects</mark> (`CreateManyInput`)

To create multiple users, assuming that we have the sample schema running in an environment, we can issue a POST request to `/rest/user` with the body set to an array of User objects. For example:

```
curl -X POST https://$ENV_API_URL/rest/user
    --json '[{name: "User 1", country: "Canada"}, {name: "User 2", country: "United States"}]'
```

The response (`AffectedRowsOutput`) from this insert request will look like this, which is the count of the inserted rows:

```
{
  "data": {
    "count": 2
  }
}
```

{% hint style="info" %}
Currently, related objects cannot be created or associated with the 'root' object using `CreateManyInput`. To achieve this, it is necessary to use `CreateInput`.
{% endhint %}

#### <mark style="color:purple;">Creating objects with relations</mark>

{% hint style="info" %}
Currently, Neurelo for MongoDB does not directly support the creation of objects with relations. However, you can create inner objects (which are equivalent to embedded documents, meaning documents nested within another document) or use references to accommodate your data modeling needs. Refer to the [Modeling Relationships for MongoDB with Inner Objects and References section](#modeling-relationships-for-mongodb-with-inner-objects-and-references) for more information.
{% endhint %}

When creating a single object, you can create or connect related object in the same transaction.

On an object with a has one relationship to another object, you can use the `create` input to create a new related object automatically associated with the 'root' object. Alternatively, you can use the `connect` input to link an existing related object to the 'root' object. When using `connect`, an error will be thrown if the linked object is not found. To address this, use `connectOrCreate`, which links an existing related object to the 'root' object or creates a new related object if it doesn't exist.

On an object with a has many relationship to another object, you can also utilize `create`, `connect`, and `connectOrCreate`. Additionally, you can use `createMany` to create many related objects.

#### `create`

To demonstrate creating a root object and its related object, we will use a sample schema with a User object and a BlogPost object. A user has many posts, and a post has one user.

```
curl -X POST https://$ENV_API_URL/rest/user/__one
    --url-query select='{ "$scalars": true, "posts": { "$scalars": true }}'
    --json '{name: "Admin User", country: "United States", posts: { create: [{ title: "Post Title" }] }}'
```

The response from this insert request will look like this, which is the inserted object(s):

```
{
  "data": {
    "id": 1,
    "name": "Admin User",
    "posts": [
      {
        "id": 1,
        "title": "Post Title"
      }
    ]
  }
}
```

#### `connect`

To create a root object and link it with an existing related object in a has many relationship, assuming that we have the sample schema running in an environment, we can use the `connect` input.

```
curl -X POST https://$ENV_API_URL/rest/user/__one
    --url-query select='{ "$scalars": true, "posts": { "$scalars": true }}'
    --json '{name: "User 1", country: "Canada", posts: { connect: [{ id: 2 }] }}'
```

The response from this insert request will look like this, which is the inserted object(s):

```
{
  "data": {
    "id": 1,
    "name": "User 1",
    "country": "Canada",
    "posts": [
      {
        "id": 2,
        "title": "My Trip to Hawaii"
      }
    ]
  }
}
```

In the above example, the Post with an ID of 2 previously existed and is now being associated with a relationship with the new root object.

#### `connectOrCreate`

To create a root object and link it with an existing related object or create a new related object if it doesn't exist (when the root and related object are in a has many relationship), assuming that we have the sample schema running in an environment, we can use the `connectOrCreate` input. For example,

```
curl -X POST https://$ENV_API_URL/rest/user/__one
    --url-query select='{ "$scalars": true, "posts": { "$scalars": true }}'
    --json '{name: "User 1", country: "Canada", posts: { connectOrCreate: [{ create: {"title": "My thoughts on Wonderwall by Oasis"}, where: {"id": 30} }] }}'
```

The response from this insert request will look like this, which is the inserted object(s):

```
{
  "data": {
    "id": 1,
    "name": "User 1",
    "country": "Canada",
    "posts": [
      {
        "id": 3,
        "title": "My thoughts on Oasis' Wonderwall"
      }
    ]
  }
}
```

In the above example, by observing the Post's ID, we can conclude that the ID 30 was not found in the Post object. As a result, a new object was created.

#### `createMany`

To create a root object and link it with many related objects in a has many relationship, assuming that we have the sample schema running in an environment, we can use the `createMany` input.

```
curl -X POST https://$ENV_API_URL/rest/user/__one
    --url-query select='{ "$scalars": true, "posts": { "$scalars": true }}'
    --json '{name: "User 1", country: "United States", posts: { createMany: [{"title": "Watching a rooftop concert"}, {"title": "How to buy concert tickets?"}] }}'
```

The response from this insert request will look like this, which is the inserted object(s):

```
{
  "data": {
    "id": 1,
    "name": "User 1",
    "country": "United States",
    "posts": [
      {
        "id": 1,
        "title": "Watching a rooftop concert"
      },
      {
        "id": 2,
        "title": "How to buy concert tickets?"
      }
    ]
  }
}
```

Moreover, to prevent the insertion of nested records with unique fields or identifiers that already exist, we can use `skipDuplicates`. For example:

```
curl -X POST https://$ENV_API_URL/rest/user/__one
    --url-query select='{ "$scalars": true, "posts": { "$scalars": true }}'
    --json '{name: "Admin User", country: "Canada", posts: {createMany: {data: [{id: 3, name: "Post 1"}, {id: 3, name: "Post 2"}], skipDuplicates: true}}}'
```

The response from this insert request will look like this, which is the inserted object(s):

```
{
  "data": {
    "id": 1,
    "name": "Admin User",
    "country": "Canada",
    "posts": [
      {
        "id": 3,
        "title": "Post 1"
      }
    ]
  }
}
```

In the above example, as Post IDs were same, the second nested record was prevented from being inserted.

{% hint style="info" %}
Currently, `skipDuplicates` is not supported for MongoDB.
{% endhint %}

### <mark style="color:green;">Retrieving Objects</mark>

You can retrieve objects by making a `GET` request to the `/rest/object`, where object is your object’s name. By default, this endpoint will return all objects, you can utilize the parameters described above to filter and limit your results.

In this example, we will use a sample schema with a User object and a BlogPost object. A user has many posts, and a BlogPost has one user. A user has a relationship property to posts called `posts` and the post has a relationship property to users called `author`.

#### <mark style="color:purple;">Retrieving unique object</mark>

To retrieve a unique user, assuming that we have the sample schema running in an environment, we can issue a GET request to `/rest/user/{{userId}}`. For example:

```
curl https://$ENV_API_URL/rest/user/1
```

The response from this request will look like this, which is the retrieved object:

```
{
  "data": {
    "id": 1,
    "name": "User 1",
    "country": "Canada"
  }
}
```

#### <mark style="color:purple;">Retrieving all objects</mark>

To retrieve all users, assuming that we have the sample schema running in an environment, we can issue a GET request to `/rest/user`. For example:

```
curl https://$ENV_API_URL/rest/user
```

The response from this request will look like this, which is an array of the retrieved objects:

```
{
  "data": [
    {
      "id": 1,
      "name": "User 1",
      "country": "Canada"
    },
    {
      "id": 2,
      "name": "User 2",
      "country": "United States"
    }
  ]
}
```

#### <mark style="color:purple;">Filtering for specific objects</mark>

To filter specific objects, we can utilize the `filter` query parameter. For complete documentation of all the options available to you with the query parameter, take a look at the `filter` parameter documentation below.

To filter all specific objects, assuming that we have the sample schema running in an environment, we can issue a GET request to `/rest/user` with the filter parameter specified. For example:

```
curl https://$ENV_API_URL/rest/user
    --url-query filter='{"country":{"contains":"United"}}'
```

The response from this request will look like this, which is an array of the retrieved objects:

```
{
  "data": [
    {
      "name": "User 2",
      "country": "United States"
    }
  ]
}
```

#### <mark style="color:purple;">Retrieving all related objects</mark>

To retrieve all related objects, we can utilize the `select` query parameter along with the `$related` parameter. Furthermore, to retrieve all objects, `$scalars` parameter can be used. When `$scalars` and `$related` is used in conjunction, all the retrieved objects and their related objects can be queried. For a complete documentation of all the options available to you with the query parameter, take a look at the `select` parameter documentation below.

To retrieve all users with their posts, assuming that we have the sample schema running in an environment, we can issue a GET request to `rest/user` with the select parameter specified. For example:

```
curl https://$ENV_API_URL/rest/user
    --url-query select='{"$scalars":true, "$related":true}'
```

The response from this request will look like this, which is an array of the retrieved objects and their related objects:

```
{
  "data": [
    {
      "name": "Admin User",
      "country": "United States",
      "posts": [
        {
          "name": "My Trip to Egypt",
          "words": 1200
        },
        {
          "name": "My Trip to Canada",
          "words": 800
        }
      ]
    }
  ]
}
```

#### <mark style="color:purple;">Retrieving specific related objects</mark>

To retrieve specific related objects, we can utilize the `select` query parameter and manually specify the objects. For a complete documentation of all the options available to you with the query parameter, take a look at the `select` parameter documentation below.

To retrieve all the users and their post names, assuming that we have the sample schema running in an environment, we can issue a GET request to `rest/user` with the select parameter specified. For example:

```
curl https://$ENV_API_URL/rest/user
    --url-query select='{"$scalars":true, "posts": {"name": true}}'
```

The response from this request will look like this, which is an array of the retrieved objects and their specific related object:

```
{
  "data": [
    {
      "name": "Admin User",
      "country": "United States",
      "posts": [
        {
          "name": "My Trip to Egypt"
        },
        {
          "name": "My Trip to Canada"
        }
      ]
    }
  ]
}
```

#### <mark style="color:purple;">Retrieving n-levels of related objects</mark>

To retrieve n-levels of related objects, we can utilize the `select` query parameter and manually specify the deeply-nested related objects. For a complete documentation of all the options available to you with the query parameter, take a look at the `select` parameter documentation below.

Assume that in our sample schema, there is a new object Activity, such that each post has many activities. A post has a relationship property to activities called `activity`.

To retrieve all the users, their post names, and activities on each post, assuming that we have the sample schema running in an environment, we can issue a GET request to `rest/user` with the select parameter specified. For example:

```
curl https://$ENV_API_URL/rest/user
    --url-query select='{"$scalars":true, "posts": {"name": true, "activity": true}}'
```

The response from this request will look like this, which is an array of the retrieved objects and their specific related object:

```
{
  "data": [
    {
      "name": "Admin User",
      "country": "United States",
      "posts": [
        {
          "name": "My Trip to Egypt",
          "activity": [
            {
              "name": "User 123",
              "action": "Like"
            },
            {
              "name": "User 234",
              "action": "Bookmark"
            }
          ]
        },
        {
          "name": "My Trip to Canada",
          "activity": [
            {
              "name": "User 345",
              "action": "Like"
            }
          ]
        }
      ]
    }
  ]
}
```

### <mark style="color:green;">Updating Objects</mark>

You can update objects by making a `PATCH` request to the `/rest/object`, where object is your object’s name. The request body should be a JSON object with the properties that you are looking to update, and you can use the `filter` parameter to apply your desired condition to the operation.

#### <mark style="color:purple;">Updating unique object</mark> (`UpdateInput`)

To update a unique user, assuming that we have the sample schema running in an environment, we can issue a PATCH request to `/rest/user/{{userId}}`. For example:

```
curl -X PATCH https://$ENV_API_URL/rest/user/1
      --json '{name: "Admin User 1"}'
```

The response from this request will look like this, which is the updated object:

```
{
  "data": {
    "id": 3,
    "name": "Admin User 1",
    "country": "Canada"
  }
}
```

#### <mark style="color:purple;">Updating multiple objects</mark> (`UpdateManyInput`)

To update multiple users, assuming that we have the sample schema running in an environment, we can issue a PATCH request to `/rest/user`. For example:

```
curl -X PATCH https://$ENV_API_URL/rest/user
    --url-query filter='{"country":{"contains":"United"}}'
    --json '{name: "Admin User 1"}'
```

The response (`AffectedRowsOutput`) from this update request will look like this, which is the count of the updated rows:

```
{
  "data": {
    "count": 1
  }
}
```

#### <mark style="color:purple;">Updating scalars using special operations</mark>

#### `increment`, `decrement`, `multiply`, `divide`

When using the `integer` type for a property, the `UpdateInput` and `UpdateManyInput` provides basic arithmetic capabilities through `increment`, `decrement`, `multiply`, and `divide`. The `increment` operation increases the specific property by a given number, while `decrement` decreases it. Similarly, `multiply` and `divide` multiply or divide the property value by a specified number. This functionality proves useful by eliminating the need for an application-level read before a write call. An example of this is as follows:

```
curl -X PATCH https://$ENV_API_URL/rest/post/1
      --json '{name: "My updated .bashrc file", words: {increment: 30}}'
```

The response from this request will look like this, which is the updated object:

```
{
  "data": {
    "id": 1,
    "name": "My updated .bashrc file",
    "words": 350
  }
}
```

#### `push`

When using the `array` type for a property, the `UpdateInput` and `UpdateManyInput` provides a `push` operation. This inserts a value or array of values at the end of the array. If the value is null, it would initialize the array with the value(s) provided. For example,

```
curl -X PATCH https://$ENV_API_URL/rest/post/1
      --json '{name: "My updated .bashrc file", tags: {push: ["alias"]}}'
```

The response from this request will look like this, which is the updated object:

```
{
  "data": {
    "id": 1,
    "name": "My updated .bashrc file",
    "tags": ["termcolor", "alias"]
  }
}
```

#### <mark style="color:purple;">Updating with relations</mark>

{% hint style="info" %}
Currently, Neurelo for MongoDB does not directly support the updation of objects with relations. However, you can update inner objects (which are equivalent to embedded documents, meaning documents nested within another document) or use references to accommodate your data modeling needs. Refer to the [Modeling Relationships for MongoDB with Inner Objects and References section](#modeling-relationships-for-mongodb-with-inner-objects-and-references) for more information.
{% endhint %}

When updating a single object, you can create, update, disconnect or delete related object in the same transaction.

On an object with a has one relationship to another object, you can use the `update` input to update a related object automatically associated with the 'root' object. Alternatively, you can use the `disconnect` input to unlink an existing related object with the 'root' object. When using `update`, an error will be thrown if the linked object is not found. To address this, use `upsert`, which updates an existing related object to the 'root' object or creates a new related object if it doesn't exist. You can also use `delete` input to delete an existing related object.

The `connect`, `connectOrCreate`, and `create` inputs (as explained in the Creating objects with relations section) are also supported here.

On an object with a has many relationship to another object, you can also utilize `create`, `connect`, `connectOrCreate`, `update`, `upsert`, `delete`, and `disconnect` inputs. Additionally, you can use `createMany`, `updateMany`, and `deleteMany` to create, update, and delete many related objects, respectively.

`createMany` is explained in the Creating objects with relations section.

#### `update`

To demonstrate updating a root object and its related object, we will use a sample schema with a User object and a BlogPost object. A user has many posts, and a post has one user.

```
curl -X PATCH https://$ENV_API_URL/rest/user/1
    --url-query select='{ "$scalars": true, "posts": { "$scalars": true }}'
    --json '{name: "Admin User 1", posts: { update: [{ where: {id: 2}, data: {name: "My system configuration" } }] } }'
```

The response from this update request will look like this, which shows the updated object(s):

```
{
  "data": {
    "id": 1,
    "name": "Admin User 1",
    "posts": [
      {
        "id": 1,
        "name": "Test"
      },
      {
        "id": 2,
        "name": "My system configuration"
      }
    ]
  }
}
```

In the above example, the Post with an ID of 2 previously existed and is now being updated along with the name of the user.

#### `upsert`

To update a root object and update its corresponding related object or create a new related object if it doesn't exist (when the root and related object are in a has many relationship), assuming that we have the sample schema running in an environment, we can use the `upsert` input. For example,

```
curl -X PATCH https://$ENV_API_URL/rest/user/1
    --url-query select='{ "$scalars": true, "posts": { "$scalars": true }}'
    --json '{name: "Admin User 1", posts: { upsert: { where: {"id": 2}, update: {name: "Hardware Info" }, create: {name: "Hardware Info"} } } }'
```

The response from this upsert request will look like this, which is the upsert object(s):

```
{
  "data": {
    "id": 1,
    "name": "Admin User 1",
    "posts": [
      {
        "id": 1,
        "name": "Test"
      },
      {
        "id": 2,
        "name": "Hardware Info"
      }
    ]
  }
}
```

#### `disconnect`

To unlink an existing related object with the 'root' object (when the root and related object are in a has many relationship), assuming that we have the sample schema running in an environment, we can use the `disconnect` input. For example,

```
curl -X PATCH https://$ENV_API_URL/rest/user/
    --url-query select='{ "$scalars": true, "posts": { "$scalars": true }}'
    --json '{name: "Admin User 1", posts: { disconnect: { id: 2 } } }'
```

The response from this request will look like this, which is the updated object(s):

```
{
  "data": {
    "id": 1,
    "name": "Admin User 1",
    "posts": [
      {
        "id": 1,
        "name": "Test"
      }
    ]
  }
}
```

In the above example, the Post with an ID of 2 previously existed and still exists, but it is now being unlinked with respect to the 'root' object. Furthermore, the name of the user also gets updated.

#### `delete`

To delete an existing related object asccociated with a 'root' object (when the root and related object are in a has many relationship), assuming that we have the sample schema running in an environment, we can use the `delete` input. For example,

```
curl -X PATCH https://$ENV_API_URL/rest/user/
    --url-query select='{ "$scalars": true, "posts": { "$scalars": true }}'
    --json '{name: "Admin User 1", posts: { delete: { id: 2 } } }'
```

The response from this request will look like this, which is the updated object(s):

```
{
  "data": {
    "id": 1,
    "name": "Admin User 1",
    "posts": [
      {
        "id": 1,
        "name": "Test"
      }
    ]
  }
}
```

In the above example, the Post with an ID of 2 is now deleted. Furthermore, the name of the user also gets updated.

#### `updateMany`

To update a root object and its many related objects in a has many relationship, assuming that we have the sample schema running in an environment, we can use the `updateMany` input.

```
curl -X PATCH https://$ENV_API_URL/rest/user/
    --url-query select='{ "$scalars": true, "posts": { "$scalars": true }}'
    --json '{name: "Admin User 1", posts: {updateMany: [{where: {id: 1}, data: {name: "Test Blog"}}, {where: {id: 2}, data: {name: "Hardware Info Blog"}}]}}'
```

The response from this updateMany request will look like this, which is the updated object(s):

```
{
  "data": {
    "id": 1,
    "name": "Admin User 1",
    "posts": [
      {
        "id": 1,
        "name": "Test Blog"
      },
      {
        "id": 2,
        "name": "Hardware Info Blog"
      }
    ]
  }
}
```

#### `deleteMany`

To update a root object and delete its many related objects in a has many relationship, assuming that we have the sample schema running in an environment, we can use the `deleteMany` input.

```
curl -X PATCH https://$ENV_API_URL/rest/user/
    --url-query select='{ "$scalars": true, "posts": { "$scalars": true }}'
    --json '{name: "Admin User Empty", posts: {deleteMany: [{id: 1}, {id: 2}]}}'
```

The response from this deleteMany request will look like this, which is the deleted object(s):

```
{
  "data": {
    "id": 1,
    "name": "Admin User Empty",
    "posts": []
  }
}
```

### <mark style="color:green;">Deleting Objects</mark>

You can delete objects by making a `DELETE` request to the `/rest/object`, where object is your object’s name. You can use the `filter` parameter to apply your desired condition to the operation.

In this example, we will use a sample schema with a User object.

#### <mark style="color:purple;">Delete unique object</mark>

To delete a unique user, assuming that we have the sample schema running in an environment, we can issue a DELETE request to `/rest/user/{{userId}}`. For example:

```
curl -X DELETE https://$ENV_API_URL/rest/user/1
```

The response from this request will look like this, which is the deleted object:

```
{
  "data": {
    "id": 1,
    "name": "User 1",
    "country": "Canada"
  }
}
```

#### <mark style="color:purple;">Delete all objects</mark>

To delete all users, assuming that we have the sample schema running in an environment, we can issue a DELETE request to `/rest/user`. For example:

```
curl -X DELETE https://$ENV_API_URL/rest/user
```

The response from this request will look like this, which is the count of the deleted object(s):

```
{
  "data": {
    "count": 1
  }
}
```

#### <mark style="color:purple;">Delete specific objects</mark>

To delete specific objects, we can utilize the `filter` query parameter. For complete documentation of all the options available to you with the query parameter, take a look at the `filter` parameter documentation below.

To delete all specific Users, assuming that we have the sample schema running in an environment, we can issue a DELETE request to `/rest/user` with the filter parameter specified. For example:

```
curl -X DELETE https://$ENV_API_URL/rest/user
      --url-query filter="{\"name\":{\"contains\":\"Admin\"}}"
```

The response from this request will look like this, which is the count of the deleted object(s):

```
{
  "data": {
    "count": 1
  }
}
```

### <mark style="color:green;">Aggregating Objects</mark>

You can aggregate objects by making a `GET` request to the `/rest/object/__aggregate`, where object is your object’s name. The `select` query parameter is required to specify the fields to be aggregated. The following aggregate functions are supported: `_count`, `_sum`, `_avg`, `_min`, and `_max`.

In this example, we will use a sample schema with a Post object.

#### <mark style="color:purple;">Aggregating specific objects</mark>

To aggregate specific objects, we can utilize the `select` query parameter. For a complete documentation of all the options available to you with the query parameter, take a look at the `select` parameter documentation below.

To average specific objects, assuming that we have the sample schema running in an environment, we can issue a GET request to `/rest/post/__aggregate` with the `_avg` query option specified for the `select` query parameter. This query option takes an array of fields to be aggregated. For example:

```
curl https://$ENV_API_URL/rest/post/__aggregate
    --url-query select='{"_avg": ["words"]}'
```

The response (`AggregateOutput`) from this request will look like this, which contains the aggregated value:

```
{
  "data": {
    "_avg": {
      "words": 1000.0
    }
  }
}
```

#### <mark style="color:purple;">Aggregating all objects</mark>

Presently, only `_count` option supports aggregating all objects.

To count all objects, assuming that we have the sample schema running in an environment, we can issue a GET request to `/rest/post/__aggregate` with the `_count` query option and `_all` value specified for the `select` query parameter. For example:

```
curl https://$ENV_API_URL/rest/post/__aggregate
    --url-query select='{"_count": ["_all"]}'
```

The response (`AggregateOutput`) from this request will look like this, which contains the aggregated value:

```
{
  "data": {
    "_count": {
      "_all": 2
    }
  }
}
```

### <mark style="color:green;">Grouping Objects</mark>

In this example, we will use a sample schema with a User object.

To group by specific fields, we can utilize the `select` and `group_by` query parameters. For a complete documentation of all the options available to you with the query parameter, take a look at the `select` and `group_by` parameter documentation below.

For an object, you can group records by one or more fields, by making a `GET` request to the `/rest/object/__groupBy` with the `select` and `group_by` parameters specified. For example, to count the number of users by country:

```
curl https://$ENV_API_URL/rest/post/__groupBy
    --url-query group_by='["country"]'
    --url-query select='{"country": true, "_count": ["id"]}'
```

The response from this request will look like this, which is the grouped and aggregated object(s):

```
{
  "data": [
    {
      "country": "United States",
      "_count": {
        "id": 1
      }
    },
    {
      "country": "Canada",
      "_count": {
        "id": 1
      }
    }
  ]
}
```

#### <mark style="color:purple;">Group By with Filtering</mark>

To filter the fields before grouping, we can utilize the `filter` query parameter. For a complete documentation of all the options available to you with the query parameter, take a look at the `filter` parameter documentation below.

For example,

```
curl https://$ENV_API_URL/rest/post/__groupBy
    --url-query group_by='["country"]'
    --url-query select='{"country": true, "_count": ["id"]}'
    --url-query filter='{"country": {"startsWith": "C"}}'
```

The response from this request will look like this, which is the grouped and aggregated object:

```
{
  "data": [
    {
      "country": "Canada",
      "_count": {
        "id": 1
      }
    }
  ]
}
```

To filter the groups by aggregate functions, we can utilize the `having` query parameter. For a complete documentation of all the options available to you with the query parameter, take a look at the `having` parameter documentation below.

For example,

```
curl https://$ENV_API_URL/rest/post/__groupBy
    --url-query group_by='["country"]'
    --url-query select='{"country": true, "_count": ["id"]}'
    --url-query having='{"id": {"_count": {"lte": 2}}}'
```

The response from this request will look like this, which is the grouped and aggregated object(s):

```
{
  "data": [
    {
      "country": "United States",
      "_count": {
        "id": 1
      }
    },
    {
      "country": "Canada",
      "_count": {
        "id": 1
      }
    }
  ]
}
```

## Parameters

In this section, we’ll take a look at some parameters that apply to many different operations. Availability of each parameter is dependent on the operation being performed, for example, the `filter` parameter is not available on Insert operations.

Once you are familiar with these parameters, we hope that utilizing individual operations on your objects comes naturally and will not require continous referral to the generated reference documentation.

### <mark style="color:green;">`filter`</mark> <mark style="color:green;"></mark><mark style="color:green;">(</mark><mark style="color:green;">`WhereInput`</mark><mark style="color:green;">)</mark>

The filter parameter allows you to add conditions to your query. This parameter can be used when retrieving objects, as well as when updating objects where it will act as the condition on which objects are updated.

#### <mark style="color:purple;">Filtering with Scalars</mark>

Each scalar has it’s own set of operations that can be performed to assert a condition, for example, an integer has the following operators available to it: `equals`, `not`, `in`, `notIn`, `lt`, `lte`, `gt`, `gte`. With the exception of `in` and `notIn`, all of these operators will accept the matching scalar, `in` and `notIn` will take an array of the matching scalar.

Here is a list of all available operators: `equals`, `not`, `in`, `notIn`, `lt`, `lte`, `gt`, `gte`, `contains`, `startsWith`, `endsWith`, and `search`. The availiblity of these is dependant on the scalar type, for example, you cannot utilize the `gt` operator on a string. However, `search` is specific to strings.

A filter condition for a given object includes all of the scalars present on the object with their respective scalar filter type. To construct a filter condition utilizing only an object’s scalar properties, you can use the name of the property as the key, and then a filter object for that specific scalar type.

Here is a more comprehensive overview of these operators:

| Operator            | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | Example                                                                                                                                                                                                                                                                                         |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| equals              | Filter for field values equal to a specific value                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | To filter when `age` equals 21, `{ "age": { "equals": 21 } }`                                                                                                                                                                                                                                   |
| lt/lte              | Filter for field values "less than / less than or equal to" a specific value                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | To filter when `age` is less than 21, `{ "age": { "lt": 21 } }`                                                                                                                                                                                                                                 |
| gt/gte              | Filter for field values "greater than / greater than or equal to" a specific value                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | To filter when `age` is greater than or equal to 30, `{ "age": { "gte": 30 } }`                                                                                                                                                                                                                 |
| not                 | Filters when field value does not equal a specific value                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | To filter records that have all ids except `100`, `{"id": {"not": 100}}`                                                                                                                                                                                                                        |
| in/notIn            | Checks if a field value exists / does not exist in a list, and filters accordingly.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | To filter records having ids as either `1`, `3` or `6`, `{ "id": { "in": [1, 3, 6] } }`                                                                                                                                                                                                         |
| startsWith/endsWith | Filter when a specific value is at the start or end of a field value. Case-sensitive by default. `_` wildcard can be used to match one or more characters. `$` wildcard can be used to match zero or more characters.                                                                                                                                                                                                                                                                                                                                                                                                            | To filter names that start with "Ron" (case-insensitive), `{"name": {"startsWith": "Ron", "mode": "insensitive"} }`. To filter names that start with "R", followed by one or more characters, and then "d", `{"name": {"startsWith": "R_d"} }`. This matches names such as "Roland" and "Rudd". |
| contains            | Filters when a specific value is contained in a field value. Case-sensitive by default. `_` wildcard can be used to match one or more characters. `$` wildcard can be used to match zero or more characters.                                                                                                                                                                                                                                                                                                                                                                                                                     | To filter country names that contain the word "United", `{"country": {"contains": "United" } }`                                                                                                                                                                                                 |
| search              | Filters using full-text search capabilities. This operator is specific to strings and currently, it is not supported with MongoDB. For querying, PostgreSQL and MySQL's native full-text search capabilities are leveraged. For example, in Postgres, `&` can be used to perform a boolean AND on two strings. Similarly, `\|` and `!` can be used to perform boolean OR and boolean NOT, respectively. For MySQL, `+` and `-` are used as boolean AND and boolean NOT, respectively. Boolean OR in MySQL is represented using no operators; for example, `Tomato Orange` would check if the text contains `Tomato` or `Orange`. | In PostgreSQL, to filter records with the engine as Firefox or Chrome, use `{"engine": {"search": "Firefox \| Chrome"}}`. In MySQL, to filter records with Firefox but not Chrome in the report, use `{"report": {"search": "+Firefox -Chrome"}}`.                                              |

#### <mark style="color:purple;">Combining conditions</mark>

When you add conditions to a filter object, you are asserting that all of the listed conditions must evaluate to true for an object to be returned, but you are also able to further customize your query’s condition by utilizing the `AND`, `OR`, and `NOT` properties. All of these properties accept an array of the object’s filter type, allowing you to nest and compose different filtering conditions to construct your query.

Here is a more comprehensive overview of these operators:

| Operator | Description                                                  | Example                                                                                                                                                                                                                                                                                                                                                                                 |
| -------- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| OR       | Filters for field values when one or more conditions is true | To filter names starting with either "A" or "B", `{"OR": [{"name": {"startsWith": "A"}}, {"name": {"startsWith": "B"}}]}`. Furthermore, to filter names starting with "A" or ending with "n", `{OR: [{"name": {"startsWith": "A"}}, {"name": "endsWith": "n"}]}`.                                                                                                                       |
| AND      | Filters for field values when all conditions are true        | To filter for first names starting with "A" and last names ending with "n", `{"AND": [{"firstName": {"startsWith": "A"}}, {"lastName": {"endsWith": "n"}}]}`. Furthermore, to filter names starting with "B" and ending with "n", we can use operators such as `startsWith` and `endsWith` in a single object itself, like `{"AND": [{"name": {"startsWith": "B", "endsWith": "n"}}]}`. |
| NOT      | Filters for field values when all the conditions are false   | To filter for names that are not "Alan" and not "George", `{"NOT": [{"name": {"equals": "Alan"}}, {"name": {"equals": "George"}}]}`. Furthermore, to filter names not starting with "C" and not ending with "n", we can use operators such as `startsWith` and `endsWith` in a single object itself, like `{"NOT": [{"name": {"startsWith": "C", "endsWith": "n"}}]}`.                  |

Note on Scope of Logical operators:

* Implicit AND: Combining conditions within one object, `{"name": {"startsWith": "A", "endsWith": "n"}}`, uses an implicit `AND` logic, meaning all conditions must be met.
* Explicit Logical Operators: Using `OR`, `AND`, or `NOT` to combine multiple conditions, such as `{"OR": [ {...}, {...} ]}`, processes each condition in its own scope before applying the logical operator. This structure allows for complex filtering, evaluating from the inner-most conditions outwards.

Furthermore, you can combine these operators to do more powerful filtering. For example,

* `NOT` and `AND`: To filter for names that do no start with "A" and end with "n", `{"NOT": {"AND": [{"name": {"startsWith": "A"}}, {"name": {"endsWith": "n"}}]}}`.
* `OR` and `AND`: To filter for ids that are between 0 to 10 or 30 to 40, `{"OR": [{"AND": [{"id": {"gte": 0}}, {"id": {"lte": 10}}]}, {"AND": [{"id": {"gte": 30}}, {"id": {"lte": 40}}]}]}`.

#### <mark style="color:purple;">Filtering with List</mark>

You can use the following operators to perform filtering with lists: `has`, `hasSome`, `hasEvery`, and `isEmpty`.

Additionally, you can employ the `equals` operator introduced earlier.

Here is a more comprehensive overview of these operators:

| Operator | Description                                                             | Example                                                                                                                                              |
| -------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| has      | Filters for lists where a given value exists.                           | To filter when `Canada` is in a `locations` list, `{"locations": {"has": "Canada"}}`                                                                 |
| hasSome  | Filters for lists where at least one value from the search list exists. | To filter when `Canada` or `United States` are in a `locations` list, `{"locations": {"hasSome": ["Canada", "United States"]}}`                      |
| hasEvery | Filters for lists where all values from the search list exists.         | To filter when `Canada`, `United States`, and `Spain` are in a `locations` list, `{"locations": {"hasEvery": ["Canada", "United States", "Spain"]}}` |
| isEmpty  | Filters for empty lists.                                                | To filter for empty `locations` list, `{"locations": {"isEmpty": true}}`. Similarly, to filter for non-empty lists, use `{"isEmpty": false}`.        |

#### <mark style="color:purple;">Filtering with JSON</mark>

You can use the following operators to perform JSON filtering: `array_starts_with`, `array_ends_with`, `array_contains`, `string_starts_with`, `string_ends_with`, and `string_contains`.

Additionally, you can employ operators introduced earlier, such as `equals`, `gt`, `gte`, `lt`, `lte`, and `not`.

To filter a specific part of the JSON object, each of these operators requires a specified `path`. For example, consider a JSON object like `{"Crops": {"Fruits": {"Organic": ["Orange", "Mango", "Banana"], "Non-organic": ["Apple", "Melon"]}}}`. In Postgres, the path `Crops -> Fruits -> Organic` can be represented as `["Crops", "Fruits", "Organic"]`. Similarly, for MySQL, it can be represented as `'$.Crops.Fruits.Organic'`.

Here is a more comprehensive overview of these operators (all the paths are specific to Postgres):

| Operator             | Description                                                                                     | Example                                                                                                                                                                                               |
| -------------------- | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| array\_starts\_with  | Filters for JSON objects where a specified path has a list that begins with a specific value.   | To filter when the list at `["Fruits", "Organic"]` path starts with `Orange`, `{"Crops": {"path": ["Fruits", "Organic"], "array_starts_with": "Orange"}}`                                             |
| array\_ends\_with    | Filters for JSON objects where a specified path has a list that ends with a specific value.     | To filter when the list at `["Fruits", "Organic"]` path ends with `Apple` , `{"Crops": {"path": ["Fruits", "Organic"], "array_starts_with": "Apple"}}`                                                |
| array\_contains      | Filters for JSON objects where a specified path has a list that contains a specific value.      | To filter when the list at `["Schedule", "Heathrow"]` path contains 9 , `{"Misc": {"path": ["Schedule", "Heathrow"], "array_contains": 9}}`                                                           |
| string\_starts\_with | Filters for JSON objects where a specified path has a string that begins with a specific value. | To filter when the string at `["Premium Customers", "NYC", "George"]` path starts with `Q2F0cw==` , `{"IDs": {"path": ["Premium Customers", "NYC", "George"], "string_starts_with": "Q2F0cw=="}}`     |
| string\_ends\_with   | Filters for JSON objects where a specified path has a string that ends with a specific value.   | To filter when the string at `["Premium Customers", "NYC", "George"]` path ends with `TmV1cmVsbw==` , `{"IDs": {"path": ["Premium Customers", "NYC", "George"], "string_ends_with": "TmV1cmVsbw=="}}` |
| string\_contains     | Filters for JSON objects where a specified path has a string that contains a specific value.    | To filter when the string at `["FullReport", "Browser"]` path contains `Firefox` , `{"Details": {"path": ["FullReport", "Browser"], "string_contains": "Firefox"}}`                                   |

#### <mark style="color:purple;">Filtering with Relations</mark>

You are able to utilize related objects in a condition too, though the manner in which you do so will depend on the type of relationship that this object has with it’s related object.

When a relationship has one of another object, you can utilize the `is` and `isNot` filters to construct conditions on a related object. For example, if we have a Post object, which has one User object, we can create a condition utilizing the User’s objects properties like so: `{ "user": { "is": { "name": { "equals": "George" } } } }`. This filter would return all Post objects where the related User’s name is equal to George.

When a relationship has many of another object, you can utilize the `some`, `every`, and `none` conditions to construct conditions on a set of related objects. For example, if we have a User object that has many Posts, we can create a condition utilizing the Post’s objects properties like so:`{ "posts": "some": [ { "title": { "equal": "Cats" } } ] }`. This will return a list of users where at least one of their posts has the title of “Cats”.

Here is a more comprehensive overview of these operators:

| Operator | Description                                                                                                                                        | Example                                                                                                                                                                 |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| is       | Filters a record when related record "is" equal to the filter condition. Can only be used when a relationship has one of another object.           | If every user has one post, to filter when name equals Alan, `{ "user": { "is": { "name": { "equal": "Alan" } } } }`                                                    |
| isNot    | Filters a record when related record "is not" equal to the filter condition. Can only be used when a relationship has one of another object.       | If every user has one post, to filter when name does not start with "A", `{ "user": { "isNot": { "name": { "startsWith": "A" } } } }`                                   |
| some     | Filters a record when related record has "some" filter conditions as true. Can only be used when a relationship has many of another object.        | If a brand has many sauces, to find out brands that have "Paprika" in some of their sauces, `{ "sauces": { "some": [ { "ingr": { "contains": "paprika" } } ] } }`       |
| every    | Filters a record when related record has "every" filter conditions as true. Can only be used when a relationship has many of another object.       | If a brand has many sauces, to find out brands that have "Paprika" in all of their sauces, `{ "sauces": { "every": [ { "ingr": { "contains": "paprika" } } ] } }`       |
| none     | Filters a record when related record has "none" of its filter conditions as true. Can only be used when a relationship has many of another object. | If a brand has many sauces, to find out brands that do not have "Paprika" in any of their sauces, `{ "sauces": { "none": [ { "ingr": { "contains": "paprika" } } ] } }` |

{% hint style="info" %}
You can combine the sections above to construct complex queries.
{% endhint %}

### <mark style="color:green;">`select`</mark> <mark style="color:green;"></mark><mark style="color:green;">(</mark><mark style="color:green;">`SelectInput`</mark><mark style="color:green;">)</mark>

You can use the select parameter to limit the scalars that you request for a specific object, as well as to eagerly retrieve related objects for that object.

If no select parameter is specified in MongoDB, by default all scalars and all inner objects are selected automatically. In Postgres and MySQL, only scalars are selected by default.

To select specific scalars on an object, pass an object with the property key name as the key, and `true` as the value.

To select all scalars on an object, you can utilize the `$scalars` parameter in the select object.

To select all related objects, you can utilize the `$related` parameter in the select object. This will retrieve all direct relationships from the parent object.

To select specific related objects, you can pass in the related objects property name as a key, and `true` as the value.

To further specify which properties of a related object that you want to select, you can pass in an object, with the key being the related property name on the root object, and the value being another `select` type object, allowing you to select the relations scalars, as well as it’s own relations. There is no limit to how far you can nest Select objects to explore relationships.

You can also use `select` to specify the fields to be aggregated (`AggregateInput`). The following aggregate functions are supported: `_count`, `_sum`, `_avg`, `_min`, and `_max`. For example, if we have a User object that has many posts, to find the maximum number of words amongst those posts, we can use `{"_max": ["words"]}`.

Similarly, you can use `select` (`GroupByInput`) in conjunction with `group_by` option to group records by one or more fields. For a more comprehensive overview, refer to the `group_by` parameter documentation.

### <mark style="color:green;">`order_by`</mark> <mark style="color:green;"></mark><mark style="color:green;">(</mark><mark style="color:green;">`OrderByWithRelationInput`</mark><mark style="color:green;">)</mark>

The `order_by` parameter takes an array of objects, with the following definition:

`{ [key: ScalarName]: "asc" | "desc" }`

You can use any scalar on the object being queried as a key, along with the direction of the sort as the value in the object. We do not support ordering nested fields at this time.

For example, given a sample schema with a User object:

```
curl https://$ENV_API_URL/rest/user?order_by=[{"name": "desc"}]
```

The response from this request will look like this:

```
{
  "data": [
    {
      "id": 2,
      "name": "User 2",
      "country": "United States"
    },
    {
      "id": 1,
      "name": "User 1",
      "country": "Canada"
    }
  ]
}
```

### <mark style="color:green;">`skip`</mark> <mark style="color:green;"></mark><mark style="color:green;">&</mark> <mark style="color:green;"></mark><mark style="color:green;">`take`</mark>

Pagination with Neurelo’s APIs works using the `skip` and `take` options as an implementation of offset based paginaition. You can pass an integer to each option to limit or skip the resulting objects.

For example, given a sample schema with a User object, to skip one record and take the next record:

```
curl https://$ENV_API_URL/rest/user?skip=1&take=1
```

The response from this request will look like this:

```
{
  "data": [
    {
      "id": 1,
      "name": "User 1",
      "country": "Canada"
    }
  ]
}
```

### <mark style="color:green;">`group_by`</mark>

The `group_by` parameter takes an array of field names (`Array`), to specify the fields to group by with.

Note: A `select` parameter needs to be specified alongside `group_by`, such that, every selected scalar field that is not part of an aggregation (`_count`, `_sum`, `_avg`, `_min`, and `_max`) must be included in `group_by` parameter's array of field names.

For example, let us use a sample schema with a Post object,

```
# If the selected scalar fields in select are country and name, then group_by needs to equal ["country", "name"].
curl https://$ENV_API_URL/rest/post/__groupBy
    --url-query select='{"country": true, "name": true, "_count": ["id"]}'
    --url-query group_by='["country", "name"]'
```

The response from this request will look like this:

```
{
  "data": [
    {
      "name": "User 2",
      "country": "United States",
      "_count": {
        "id": 1
      }
    },
    {
      "name": "User 1",
      "country": "Canada",
      "_count": {
        "id": 1
      }
    }
  ]
}
```

#### `having` (`HavingInput`)

`having` parameter is used in conjuction with `group_by` parameter to filter the groups by aggregate functions. It is a special kind of `filter` which accepts all the `filter` options, together with the ability to filter aggregate functions.

To filter by aggregate function, the following definition can be used:

`{ [field: ScalarName]: { [key: aggregateFunction]: filterCondition } }`

Supported aggregate functions are: `_count`, `_sum`, `_avg`, `_min`, and `_max`. filterCondition is the same as a `filter` body and accepts all the `filter` options.

For example, let us use a sample schema with a Post object,

```
curl https://$ENV_API_URL/rest/post/__groupBy
    --url-query group_by='["location"]'
    --url-query select='{"location": true, "_sum": ["num_words"]}'
    --url-query having='{"num_words": {"_sum": {"gte": 1000000}}}'
```

The response from this request will look like this, which is the grouped and aggregated object(s):

```
{
  "data": [
    {
      "location": "United States",
      "_sum": {
        "id": 5179839
      }
    },
    {
      "location": "India",
      "_sum": {
        "id": 3044357
      }
    }
  ]
}
```

Moreover, similar to `filter`, you can use `AND`, `OR`, and `NOT` operators to do more powerful filtering. For example, to filter when the number of words is in the range of 1000 to 10000, you can use `{"OR": [{"id": {"_sum": {"gte": 1000}}}, {"id": {"_sum": {"lte": 10000}}}]}`.

### Modeling Relationships for MongoDB with Inner Objects and References

Currently, Neurelo for MongoDB doesn't support direct manipulation of objects with relations. However, you can work with inner objects (equivalent to embedded documents, i.e., documents nested within another document) or use references to model such relationships.

To decide which data modeling approach suits your needs best, refer to our guide on [how to work with embedded documents and references in MongoDB](https://docs.neurelo.com/guides/how-to-work-with-embedded-documents-and-references-in-mongodb).

In summary, inner objects enhance read performance by reducing database queries and ensuring atomic updates within a single document. They are optimal for closely related data that is frequently accessed together, remains relatively static, and adheres to the 16MB document size limit. However, they might lead to data redundancy and scalability challenges as your database grows.

On the other hand, references are more suitable for large or frequently changing data. They excel in many-to-many relationships by offering the flexibility to link documents across different collections, preventing data duplication and maintaining database normalization. The trade-off, however, is the need for additional queries to get related data, which could affect your performance.

**Inner Objects**

Neurelo supports the management of inner objects at the schema level. To learn how to create inner objects for your schema, please refer to [the Neurelo Schema Language reference](https://docs.neurelo.com/neurelo-schema-language-nsl#inner-objects).

Now, assume that will have a sample schema with a User object and an Address inner object. A user **has one** address. For example,

```json
{
  "objects": {
    "user": {
      "properties": {
        "id": {
          "type": "string",
          "sourceName": "_id",
          "sourceType": "ObjectId",
          "default": {
            "function": "auto"
          },
          "identifier": true
        },
        "name": {
          "type": "string"
        },
        "address": {
          "$ref": "#/innerObjects/Address",
          "nullable": true
        }
      }
    }
  },
  "innerObjects": {
    "Address": {
      "properties": {
        "id": {
          "type": "string",
          "sourceType": "ObjectId"
        },
        "street": {
          "type": "string"
        },
        "city": {
          "type": "string"
        },
        "zipcode": {
          "type": "string"
        }
      }
    }
  }
}
```

**Create**

To create a single user with an address, assuming that we have this sample schema running in an environment, we can issue a `POST` request to `/rest/user/__one` with the body set to a User object. For example,

```bash
curl -X POST https://$ENV_API_URL/rest/user/__one
    --url-query select='{ "$scalars": true, "$innerObjects": true}'
    --data '{"name": "Admin User", "address": { "set": { "id": "5ca4bbc7a2dd94ee5816238d", "street": "Diagon Alley", "city": "Nowhere Land", "zipcode": "27000" } }}'
```

Note that, at present, our APIs **do not allow** the user to autogenerate inner object ids, hence we had to manually specify it in the above example. Also notice that we can use `$innerObjects` to `select` all the inner objects without the need to manually specify them.

The response from this insert request will look like this, which is the object that was inserted:

```json
{
  "data": {
    "id": "65eb623e591811da1638040a",
    "name": "Admin User",
    "address": {
      "id": "5ca4bbc7a2dd94ee5816238d",
      "street": "Diagon Alley",
      "city": "Nowhere Land",
      "zipcode": "27000"
    }
  }
}
```

To create multiple users, we can issue a `POST` request to `/rest/user` with the body set to an array of User objects. For example:

```bash
curl -X POST https://$ENV_API_URL/rest/user
    --url-query select='{ "$scalars": true, "$innerObjects": true}'
    --data '[{"name": "Roger Smith", "address": { "set": { "id": "5ca4bbc7a2dd94ee5816238e", "street": "King's Court", "city": "Nashville", "zipcode": "27100" } }}, {"name": "Alan Walkman", "address": { "set": { "id": "5ca4bbc7a2dd94ee5816238f", "street": "King's Court", "city": "Nashville", "zipcode": "27100" } }}]'
```

The response (`AffectedRowsOutput`) from this insert request will look like this, which is the count of the inserted rows:

```json
{
  "data": {
    "count": 2
  }
}
```

**Retrieve**

To retrieve a unique user with its address, assuming that we have the sample schema running in an environment, we can issue a `GET` request to `/rest/user/{{userId}}`. For example:

```bash
curl https://$ENV_API_URL/rest/user/65eb623e591811da1638040a
```

The response from this request will look like this, which is the retrieved object:

```json
{
  "data": {
    "id": "65eb623e591811da1638040a",
    "name": "Admin User",
    "address": {
      "id": "5ca4bbc7a2dd94ee5816238d",
      "street": "Diagon Alley",
      "city": "Nowhere Land",
      "zipcode": "27000"
    }
  }
}
```

Similarly, to retrieve all users along with their addresses, assuming that we have the sample schema running in an environment, we can issue a `GET` request to `/rest/user`. For example:

```bash
curl https://$ENV_API_URL/rest/user
```

The response from this request will look like this, which is an array of the retrieved objects (with their inner objects):

```json
{
  "data": [
    {
      "data": {
        "id": "65eb623e591811da1638040a",
        "name": "Admin User",
        "address": {
          "id": "5ca4bbc7a2dd94ee5816238d",
          "street": "Diagon Alley",
          "city": "Nowhere Land",
          "zipcode": "27000"
        }
      }
    }
  ]
}
```

Note that a `select` with `"$innerObjects": true` is not necessary for retrieving all of the corresponding inner objects.

**Update**

To update users with its address, assuming that we have the sample schema running in an environment, we can issue an `UPDATE` request to `/rest/user/`. For example:

```bash
curl -X PATCH 'https://$ENV_API_URL/rest/user'
--url-query filter='{ "id": { "equals": "65eb623e591811da1638040a" }}'
--data '{
  "address": {
    "update": {
      "city": "Valencia",
      "id": "5ca4bbc7a2dd94ee5816238d",
      "street": "Eixample",
      "zipcode": "960"
    }
  }
}'
```

The response (`AffectedRowsOutput`) from this update request will look like this, which is the count of the updated rows:

```json
{
  "data": {
    "count": 1
  }
}
```

**Delete**

To delete users with its address, assuming that we have the sample schema running in an environment, we can issue an `DELETE` request to `/rest/user/`. For example:

```bash
curl -X DELETE 'https://$ENV_API_URL/rest/user'
--url-query filter='{ "address": { "is": {"id": "5ca4bbc7a2dd94ee5816238d"} }}'
```

The response (`AffectedRowsOutput`) from this delete request will look like this, which is the count of the deleted rows:

```json
{
  "data": {
    "count": 1
  }
}
```

Note that **this will delete both the object and its inner object**. To remove the contents of just the inner object, you can use the `UPDATE` request with an `unset`:

```bash
curl -X UPDATE 'https://$ENV_API_URL/rest/user'
--url-query filter='{ "id": { "equals": "65eb623e591811da1638040a" }}'
--data '{
  "address": {
    "unset": true
  }
}'
```

This approach requires setting the inner object of your schema, specifically the unique ID, to have `nullable` set to `true`.

## API Errors

Errors for the API calls will be presented in a list format so that multiple issues can be communicated at once. Each error object will contain an `error` property that contains the error message and, optionally, a `details` object with more information about the error.

Two types of errors can be returned as a response:

* Validation errors - request is invalid or contains invalid data
* Execution errors - request execution failed

For example, a sample schema and a sample request like:

```json
Schema: 

{
  "objects": {
    "actor": {
      "properties": {
        "actor_id": {
          "type": "integer",
          "identifier": true
        },
        "first_name": {
          "type": "string"
        },
        "last_name": {
          "type": "string"
        },
        "movie_count": {
          "type": "integer"
        }
      }
    }
  }
}
```

```bash
Request: 

curl https://$ENV_API_URL/rest/actor
    --url-query select='{"first_name": true}'
    --url-query filter='{"movie_count": {"gt": "3"}}'
    --url-query aggregate='{"_count": ["first_name", "alias"]}'
    --url-query group_by='["first_name"]'
```

Will generate the following response:

```json
Response: 

{
  "errors": [
    {
      "error": "\"3\" is not of type \"integer\"",
      "details": {
        "in": "params",
        "location": "filter/movie_count/gt"
      }
    },
    {
      "error": "\"alias\" is not one of [\"actor_id\", \"first_name\", \"last_name\", \"movie_count\"]",
      "details": {
        "in": "params",
        "location": "aggregate/_count/1"
      }
    }
  ]
}
```

The above example contains two errors, each with an error message and the specific location of the invalid attribute in the details section. The location parameter contains a JSON path to the attribute that failed the validation and uses “/” as a separator for each part of the path. In the case of the second error, the location contains the number “1”, indicating that the invalid attribute is contained within an array and that the element with index 1 is invalid.
