Structr

REST Interface

Overview

The REST interface is the universal application programming interface (API) in Structr. It provides access to all types in the data model, including the internal types that power Structr. The REST API allows you to create, read, update and delete any object in the database, as well as create and run business logic methods and maintenance tasks.

Basics

Endpoints

Structr automatically creates REST endpoints for all types in the data model. There are different types of endpoints: collection resources, which provide access to collections of objects of the corresponding type, and entity resources that allow you to read, update or delete individual objects.

Transactions

All requests made via the REST interface are subject to the ACID principle, meaning each request is processed as a single atomic transaction. If a request is successful, all changes made within that request are guaranteed to be saved in the database. Conversely, if any part of the request fails, all changes are rolled back and the database remains in its previous state. This ensures data consistency even when multiple related objects are created or modified in a single request.

How to access the API

To use the API, you need an HTTP client that supports GET, PUT, POST, PATCH (optional) and DELETE, and a JSON parser/formatter to process the response. Since these technologies are available in all modern browsers, it is very easy to work with the API in a web application using fetch() or libraries like jQuery. The Data Access article shows examples for different HTTP clients.

The example requests in this chapter use curl, a command-line tool available at https://curl.haxx.se/download.html. It might already be installed on your system.

Note: The output of all REST endpoints can also be viewed with a browser. Browser requests send an HTTP Accept:text/html header which causes Structr to generate the output as HTML.

Data Format

The REST API uses JSON to transfer data. The request body must be valid JSON and it can either be a single object or an array of objects. The response body of any endpoint will always be a JSON Result Object, which is a single object with at least a result entry.

Objects

A valid JSON object consists of quoted key-value mappings where the values can be strings, numbers, objects, arrays or null. Date values are represented by the ISO 8601 international date format string.

{
    "id": "a01e2889c25045bdae6b33b9fca49707",
    "name": "Project #1",
    "type": "Project",
    "description": "An example project",
    "priority": 2,
    "tasks": [
        {
            "id": "dfa33b9dcda6454a805bd7739aab32c9",
            "name": "Task #1",
            "type": "Task"
        },
        {
            "id": "68122629f8e6402a8b684f4e7681653c",
            "name": "Task #2",
            "type": "Task"
        }
    ],
    "owner": {},
    "other": null,
    "example": {
        "name": "A nested example object",
        "fields": []
    },
    "createdDate": "2020-04-21T18:31:52+0200"
}

The JSON object above is a part of an example result produced by the /Project endpoint. You can see several different nested objects in the result: the root object is a Project node, the tasks array contains two Task objects, and the owner is an empty object because the view has no fields for this type. (All these details will be explained in the following sections).

Nested Objects

One of the most important and powerful functions of the Structr REST API is the ability to transform nested JSON objects into graph structures, and vice versa. The transformation is based on contextual properties in the data model, which encapsulate the relationships between nodes in the graph.

You can think of the data model as a blueprint for the structures that are created in the database. If you specify a relationship between two types, each of them gets a contextual property that manages the association. In this example, we use Project and Task, with the following schema.

Input and Output

The general rule is that the input format for objects is identical to the output format, so you can use (almost) the same JSON to create or update objects as you get as a response.

For example, when you create a new project, you can specify an existing Task object in the tasks array to associate it with the project. You can leave out the id and type properties for the new object, because they are filled by Structr once the object is created in the database.

Create a new object and associate an existing task

{
    "name": "Project #2",
    "description": "A second example project",
    "priority": 1,
    "tasks": [
        {
        "id": "dfa33b9dcda6454a805bd7739aab32c9",
        }
    ]
}

Reference by UUID

The reference to an existing object is established with the id property. The property contains the UUID of the object, which is the primary identifier. Because the UUID represents the identity, you can use it instead of the object itself when specifying a reference to an existing object, like in the following example.

Short Form
{
    "name": "Project #2",
    "description": "A second example project",
    "priority": 1,
    "tasks": [ "dfa33b9dcda6454a805bd7739aab32c9" ]
}

If you want to reference an existing object in JSON, you can use the UUID instead of the object itself.

Reference by property value

It is also possible to use properties other than id to reference an object, for example name, or a numeric property like originId etc. The only requirement is a uniqueness constraint on the corresponding property to avoid ambiguity.

Short Form
{
    "name": "Project #2",
    "description": "A second example project",
    "priority": 1,
    "tasks": [ { "name": "Task #1" } ]
}

Errors

If a request causes an error on the server, Structr responds with a corresponding HTTP status code and an error response object. You can find a list of possible status codes in the Troubleshooting Guide. The error response object looks like this.

Error Response Object

{
    "code": 422,
    "message": "Unable to commit transaction, validation failed",
    "errors": [
        {
        "type": "Project",
        "property": "name",
        "token": "must_not_be_empty"
        }
    ]
}

It contains the following fields:

NameDescription
codeHTTP status code
messageStatus message
errorsArray of error objects

Error Objects

Error objects contain detailed information about an error. There can be multiple error objects in a single error response. An error object contains the following fields.

NameDescription
typeData type of the erroneous object
propertyName of the property that caused the error (optional)
tokenError token
detailsDetails (optional)

Note: If an error occurs in a request, the whole transaction is rolled back and no changes will be made to the database contents.

Related Topics

Authentication

REST endpoints are protected by a security layer called Resource Access Permissions that controls which endpoints each type of user can access. This article explains how to configure these permissions and how to set up CORS for cross-origin requests.

For general information about authentication methods (sessions, JWT, OAuth, two-factor authentication), see the Security chapter.

Resource Access Permissions

Non-admin users require explicit permission to fetch data from REST endpoints. Resource Access Permissions define which endpoints each user category can access. Consider the following request:

curl:

curl -s http://localhost:8082/structr/rest/User

Response:

{
    "code": 401,
    "message": "Forbidden",
    "errors": []
}

Access to the User collection was denied. If you look at the log file, you can see a warning message because access to resources without authentication is prohibited by default:

2020-04-19 11:40:15.775 [qtp1049379734-90] INFO  o.structr.web.auth.UiAuthenticator - Found no resource access permission for anonymous users with signature 'User' and method 'GET'.

Signature

Resource Access Permissions consist of a signature and a set of flags that control access to individual REST endpoints. The signature of an endpoint is based on its URL, replacing any UUID with _id, plus a special representation for the view (the view’s name, capitalized and with a leading underscore).

The signature of a schema method equals its name, but capitalized. The following table shows examples for different URLs and the resulting signatures:

Type URL Signature
Collection /structr/rest/Project Project
Collection with view /structr/rest/Project/ui Project/_Ui
Collection with view /structr/rest/Project/info Project/_Info
Object with UUID /structr/rest/Project/362cc05768044c7db886f0bec0061a0a Project/_id
Object with UUID and view /structr/rest/Project/362cc05768044c7db886f0bec0061a0a/info Project/_id/_Info
Subcollection /structr/rest/Project/362cc05768044c7db886f0bec0061a0a/tasks Project/_id/Task
Schema Method /structr/rest/Project/362cc05768044c7db886f0bec0061a0a/doUpdate Project/_id/DoUpdate

Finding the Correct Signature

If access to an endpoint is denied because of a missing Resource Access Permission, you can find the required signature in the log file:

Found no resource access permission for anonymous users with signature 'User/_id' and method 'GET'.

Flags

The flags property of a Resource Access Permission is a bitmask based on an integer value where each bit controls one permission. You can either set all flags at once with the corresponding integer value, or click the checkboxes in the Admin UI to toggle individual permissions.

Anonymous Access

With the default configuration, anonymous users cannot access any endpoints. To allow anonymous access to an endpoint, you must grant permission explicitly and separately for each HTTP method. Use the “Non-authenticated Users” flags in Resource Access Permissions for this purpose.

Without endpoint access permission:

curl -s http://localhost:8082/structr/rest/Project
{
    "code": 401,
    "message": "Forbidden",
    "errors": []
}

With endpoint access permission:

curl -s http://localhost:8082/structr/rest/Project
{
    "result": [],
    "query_time": "0.000127127",
    "result_count": 0,
    "page_count": 0,
    "result_count_time": "0.000199823",
    "serialization_time": "0.001092944"
}

Now you can access the endpoint, but you still don’t see any data because no project nodes are visible for anonymous users. Visibility is controlled separately through visibility flags on each object (see User Management in the Security chapter).

Authenticated Users

With the default configuration, non-admin users cannot access any endpoints. To allow non-admin users access to an endpoint, you must grant permission explicitly and separately for each HTTP method. Use the “Authenticated Users” flags in Resource Access Permissions for this purpose.

Cross-Origin Resource Sharing (CORS)

When your frontend runs on a different domain than your Structr backend, browsers block requests by default. This security feature is called the same-origin policy. CORS headers tell browsers which cross-origin requests to allow.

When You Need CORS

CORS configuration is required when:

CORS Settings

Each CORS entry configures response headers for a URL path:

Setting HTTP Header Purpose
Accepted Origins Access-Control-Allow-Origin Which domains can make requests (* for any)
Max Age Access-Control-Max-Age How long browsers cache preflight responses (seconds)
Allow Methods Access-Control-Allow-Methods Which HTTP methods are permitted
Allow Headers Access-Control-Allow-Headers Which request headers clients can send
Allow Credentials Access-Control-Allow-Credentials Whether to include cookies
Expose Headers Access-Control-Expose-Headers Which response headers JavaScript can access

Common Patterns

For development with a local frontend:

Setting Value
Path /structr/rest
Accepted Origins http://localhost:3000
Allow Methods GET, POST, PUT, DELETE, OPTIONS
Allow Headers Content-Type, Authorization
Allow Credentials true

For a public API:

Setting Value
Path /structr/rest
Accepted Origins *
Allow Methods GET, POST
Allow Headers Content-Type

Configure CORS settings in the Security area of the Admin UI under the CORS tab.

Related Topics

Data Access

This article explains how to read, create, update, and delete objects through the REST API. It assumes you understand the basics covered in the Overview article and have configured authentication as described in the Authentication article.

There are two different endpoint types in the REST API:

The examples in this chapter use HTTP header-based authentication with the default admin user that Structr creates on first startup.

Collection Endpoints

Collection endpoints provide access to collections of objects and support pagination, searching, filtering and bulk editing. The example request below accesses the endpoint for the type Project and fetches all existing objects.

Request

$ curl -s -HX-User:admin -HX-Password:admin http://localhost:8082/structr/rest/Project

If the request is successful, the result object contains one or more objects and some metadata.

Response

{
    "result": [
        {
            "id": "c624511baa534ece9678f9d212711a9d",
            "type": "Project",
            "name": "Project #1"
        },
        {
            "id": "85889e5ff1cd4bb39dc32076516504ce",
            "type": "Project",
            "name": "Project #2"
        }
    ],
    "query_time": "0.000026964",
    "result_count": 2,
    "page_count": 1,
    "result_count_time": "0.000076108",
    "serialization_time": "0.000425659"
}

Result Object

KeyDescription
resultArray of result objects
query_timeTime it took to run the query (in seconds)
result_countNumber of results in the database (if fewer than the soft limit)
page_countNumber of pages in a paginated result
result_count_timeTime it took to count the result objects (in seconds)
serialization_timeTime it took to serialize the JSON response (in seconds)

The number of results returned by a GET request to a collection resource can be limited with the request parameter _pageSize. This so-called pagination depends on a combination of the parameters _pageSize and _page. The _page parameter has a default value of 1, so you can omit it to get the first page of the paginated results.

Note: Starting with Structr 4.0, the built-in request parameters must be prefixed with an underscore, i.e. pageSize becomes _pageSize. This change was introduced to prevent name clashes with user-defined attributes. If your installation runs on a version prior to 4.0, simply omit the underscore.

Fetch the First 10 Projects
$ curl -s -HX-User:admin -HX-Password:admin http://localhost:8082/structr/rest/Project?_pageSize=10
Fetch the Next 10 Projects

To fetch the next 10 results, you can use the page parameter with a value of 2:

$ curl -s -HX-User:admin -HX-Password:admin "http://localhost:8082/structr/rest/Project?_pageSize=10&_page=2"

Please note that the ampersand character & has a special meaning in some command-line environments, so you might need to put the URL string in quotes.

Note: As of Structr version 3.4.3, the total number of results returned in a single GET request to a collection resource is soft-limited to 10,000 objects. This limit can be overridden by requesting a larger _pageSize, or by increasing the global soft limit in the Configuration Tool.

Sorting

By default, the results of a GET request are unordered, and the desired sort order can be controlled with the (optional) parameters _sort and _order. You can sort the result by one or more indexed property value (including Function Property results), in ascending (_order=asc) or descending order (_order=desc), as shown in the following examples. The default sort order is ascending order. String sort order depends on the case of the value, i.e. upper case Z comes before lower case a.

To sort the result based on multiple fields, repeat the _sort and _order parameters in the request URL for each property you want to sort by. The order of appearance of the fields in the URL determines the priority.

$ curl -s -HX-User:admin -HX-Password:admin http://localhost:8082/structr/rest/Project?_sort=name
$ curl -s -HX-User:admin -HX-Password:admin http://localhost:8082/structr/rest/Project?_sort=name&_order=desc
$ curl -s -HX-User:admin -HX-Password:admin "http://localhost:8082/structr/rest/Project?_sort=status&_sort=name&_order=desc&_order=asc"

Null Values

If a sorted collection includes objects with null values, those objects are placed at the end of the collection for ascending order (“nulls last”), or at the beginning of the collection for descending order.

Empty Values

Empty string values are not treated like null, but are instead placed at the beginning of the collection for ascending order, or at the end of the collection for descending order.

Searching

To search for objects with a specific value, you can simply put the property name with the desired value in a request parameter, as shown in the following example.

$ curl -s -HX-User:admin -HX-Password:admin http://localhost:8082/structr/rest/Project?name=Project1

These simple value-based filters can also be used on contextual properties, e.g. you can select all projects with a specific owner.

$ curl -s -HX-User:admin -HX-Password:admin http://localhost:8082/structr/rest/Project?owner=5c08c5bd41164e558e9388c22752d273

You can also search for multiple values at once, resulting in an “OR” conjunction, with the ; character between the values:

$ curl -s -HX-User:admin -HX-Password:admin "http://localhost:8082/structr/rest/Project?name=Project1;Project2"

Indexing

Please note that you can only search for objects based on indexed and contextual properties, so make sure to set the indexed flag (and a type hint for Function Properties) in the data model. Setting the flag is necessary because Structr maintains indexes in the database to provide better search performance, and the creation and deletion of these indexes is coupled to indexed flag.

Advanced Search Capabilities

Besides simple value-based filters, Structr also supports other search methods:

Inexact Search

To switch from exact search (which is the default) to inexact search, you can add the request parameter _inexact=1 to the URL.

$ curl -s -HX-User:admin -HX-Password:admin "http://localhost:8082/structr/rest/Project?name=Proj&_inexact=1"

This query will return all the project nodes whose name contain the string “proj”. The search value is automatically converted to lower case. Please also note that the search applies to the whole string, so it doesn’t mean “begins with”, but “contains”.

Distance Search

Distance search is based on the value of the properties latitude and longitude, which can be made available by extending the internal type Location in the data model. If a node has latitude and longitude, it can be found with a distance search as shown in the following example.

$ curl -s -HX-User:admin -HX-Password:admin "http://localhost:8082/structr/rest/Hotel?_latlon=50.1167851,8.7265218&_distance=0.1"

The parameters that activate distance search are _latlon and _distance. Latlon indicates the latitude and longitude values of the search origin, separated by a comma, and distance indicates the desired circular search radius around the given location, in kilometers.

Range Filters

To filter the result based on a range of property values, you can use the following syntax: [<start> TO <end>]. Please note that you need to do URL encoding for the spaces in the range expression if you construct the URL string manually.

Omitting results in a “lower than equal” search, omitting results in a “greater than equal” search. Omitting both does not raise an error, but does nothing.

Note: The space character is still required.

$ curl -s -HX-User:admin -HX-Password:admin "http://localhost:8082/structr/rest/Project?priority=[1 TO 2]"

$ curl -s -HX-User:admin -HX-Password:admin "http://localhost:8082/structr/rest/Project?priority=[0 TO ]"

$ curl -s -HX-User:admin -HX-Password:admin "http://localhost:8082/structr/rest/Project?priority=[ TO 2]"
Date Ranges

The range filter is especially useful for date ranges as shown in the following example. The first query selects only those projects that were created in January 2020. The date format used in this example is the international date format as specified in ISO 8601. Please note that the date query string is timezone-specific, so your search results may vary. You can configure the timezone of your Structr installation in the start script.

Note: The next query needs manual URL encoding if you want to test it with a command-line tool, or any other tool that does not encode URLs automatically.

$ curl -s -HX-User:admin -HX-Password:admin http://localhost:8082/structr/rest/Project?createdDate=[2020-01-01T00:00:00Z TO 2020-01-31T23:59:59Z]"

$ curl -s -HX-User:admin -HX-Password:admin http://localhost:8082/structr/rest/Project?createdDate=[ TO 2020-01-31T23:59:59Z]"

$ curl -s -HX-User:admin -HX-Password:admin http://localhost:8082/structr/rest/Project?createdDate=[2020-01-01T00:00:00Z TO ]"
Empty Values

To search for objects without a value for a given property, you simply put the name of the property in the URL, without specifying a value, which results in the following query.

$ curl -s -HX-User:admin -HX-Password:admin "http://localhost:8082/structr/rest/Project?name="

More Search Options

If you need more search options, e.g. search on multiple types, faceted search etc., have a look at the following options:

Entity Endpoints

Entity endpoints can be used to fetch the contents of a single object (using a GET request), to update an object (using PUT), or to delete an object (DELETE). Entity endpoints can also be used to execute schema methods. Access to entity endpoints and the corresponding responses are a little bit different to those of collection endpoints. The URL usually ends with a UUID, or with the name of method to be executed, and the response object contains only a single object instead of a collection.

Fetching an object by UUID

$ curl -s -HX-User:admin -HX-Password:admin http://localhost:8082/structr/rest/Project/c431c1b5020f4430808f9b330a123159

Response

{
    "result": {
        "id": "c431c1b5020f4430808f9b330a123159",
        "type": "Project",
        "name": "New Project 2"
    },
    "query_time": "0.000851438",
    "result_count": 1,
    "page_count": 1,
    "result_count_time": "0.000054867",
    "serialization_time": "0.000185543"
}

Note: The result key contains a single object instead of an array.

Executing a schema method

You can execute schema methods by sending a POST request with optional parameters in the request body to the entity resource URL of an object, as shown in the following example.

The example request causes the schema method myUpdateMethod to be executed on the project node with the UUID c431c1b5020f4430808f9b330a123159, with the parameters parameter1=test, parameter2=123.

$ curl -s -HX-User:admin -HX-Password:admin http://localhost:8082/structr/rest/Project/c431c1b5020f4430808f9b330a123159/myUpdateMethod -XPOST -d '{
    "parameter1": "test",
    "parameter2: 123
}'
Response

A schema method can return any value (including null, which results in an empty response object). Non-empty return values will be transformed to JSON objects and returned in the response. If the method runs without errors, the response status code is 200 OK and the response body contains the JSON result of the method.

In case of an error (syntax error, data error, call to the error method or failed assert calls), the response status code is the corresponding error code (401, 403, 404, 422 etc.) and the response body contains an error object.

Executing a maintenance command

You can execute a maintenance command by sending a POST request to the maintenance endpoint of the corresponding maintenance command.

$ curl -s -HX-User:admin -HX-Password:admin http://localhost:8082/structr/rest/maintenance/rebuildIndex -XPOST
Response

The response of a maintenance command will always be 200 OK with an empty response object. Most commands will send their log output to the structr log file.

View Selection

A View is a named group of properties that specifies the contents of an object in the JSON output. We recommend to create dedicated views for the individual consumers of your API, to optimize data transfer and allow for independent development and modification of the endpoints.

You can select any of the Views defined in the data model by appending its name to the request URL. If the URL does not contain a view, the public view is selected automatically.

Default View (public)

$ curl -s -HX-User:admin -HX-Password:admin http://localhost:8082/structr/rest/Project?_sort=name
Result
{
    "result": [
        {
            "id": "d46a2d3b90c94e368a70bc30acd30572",
            "type": "Project",
            "name": "Project #1"
        },
        {
            "id": "6941a2af4c024b429ffc4851b404af72",
            "type": "Project",
            "name": "Project #2"
        },
        {
            "id": "8db88530ea5949ba89cef1234e04d8e4",
            "type": "Project",
            "name": "Project #3"
        }
   ],
    "query_time": "0.000122986",
    "result_count": 3,
    "page_count": 1,
    "result_count_time": "0.000062350",
    "serialization_time": "0.017995394"
}

Manual View Selection (info)

The next request selects the info view, so the result object is different from the previous one. Note that view selection is important in fetch() and jQuery $.ajax() as well, because the view controls which properties are available in the resulting JSON object.

fetch()
fetch('/structr/rest/Project/info', {
    method: 'GET',
    credentials: 'include'
}).then(function(response) {
    return response.json();
}).then(function(json) {
    json.result.forEach(project => {
        // process result
    });
});
jQuery
$.ajax({
    url: '/structr/rest/Project/info',
    method: 'GET',
    statusCode: {
        200: function(result) {
            result.forEach(function(project) {
                // process result
            });
        }
    }
});
curl
$ curl -s -HX-User:admin -HX-Password:admin http://localhost:8082/structr/rest/Project/info?_sort=name
Result
{
    "result": [
        {
            "id": "d46a2d3b90c94e368a70bc30acd30572",
            "type": "Project",
            "name": "Project #1",
            "tasks": [
                {
                "id": "4a6894302db74c94b989fcac7e68a38e",
                "name": "Task #1",
                "type": "Task"
                }
            ],
            "description": "This is the description of the first project.",
            "owner": null,
            "priority": 2
        },
        {
            "id": "6941a2af4c024b429ffc4851b404af72",
            "type": "Project",
            "name": "Project #2",
            "tasks": [],
            "description": "My second project.",
            "owner": null,
            "priority": 3
        },
        {
            "id": "8db88530ea5949ba89cef1234e04d8e4",
            "type": "Project",
            "name": "Project #3",
            "tasks": [],
            "description": "Third project description.",
            "owner": null,
            "priority": 1
        }
    ],
    "query_time": "0.000159363",
    "result_count": 3,
    "page_count": 1,
    "result_count_time": "0.000065976",
    "serialization_time": "0.004041883"
}

The selected view is applied to all objects in the result. This means that if your result contains objects with different types, the selected view must be defined on every type, otherwise you will get an empty result object for that type.

Note: View selection takes precedence over all other options when resolving a URL. So if the name of a view matches the name of, for example, a property or nested type, the view is selected instead of the property.

Output Depth of result

If a view is created on multiple node types and contains remote node objects, then the output depth of the result JSON is restricted to the level of 3 by default. This is to prevent the whole graph from being rendered, which could happen in some scenarios like trees for examples.

With the query parameter _outputNestingDepth the output depth of the result JSON can be adjusted to the desired level.

Creating Objects

To create new objects in the database, you send a POST request to the collection resource endpoint of the target type. If the request body contains JSON data, the data will be stored in the new object. If the request does not contain a JSON body, it creates an “empty” object which only contains the properties that are assigned by Structr automatically.

fetch()

fetch('/structr/rest/Project/info', {
    method: 'POST',
    credentials: 'include',
    body: JSON.stringify({
        name: "New Project"
    }),
}).then(function(response) {
    return response.json();
}).then(function(json) {
    json.result.forEach(project => {
        // process result
    });
});

jQuery

$.ajax({
    url: '/structr/rest/Project/info',
    method: 'POST',
    data: JSON.stringify({
        name: "New Project"
    }),
    statusCode: {
        201: function(result) {
            result.forEach(function(project) {
                // process result
            });
        }
    }
});

curl

$ curl -s -HX-User:admin -HX-Password:admin http://localhost:8082/structr/rest/Project -XPOST -d '{ "name": "New Project" }'

Response

If the request results in the creation of a new object, Structr responds with an HTTP status of 201 Created and the UUID of the new object in the result.

{
    "result": [
        "b0f7e79a17c649a9934687554990acd5"
    ],
    "result_count": 1,
    "page_count": 1,
    "result_count_time": "0.000048183",
    "serialization_time": "0.000224176"
}

Creating Multiple Objects

You can create more than one object of the same type in a single request by sending an array of JSON objects in the request body, as shown in the next example. Doing so has the advantage that all objects are created in a single transaction, so either all objects are created, or none, if an error occurs. It is also much more efficient because the transaction overhead is incurred only once.

fetch()

fetch('/structr/rest/Project/info', {
    method: 'POST',
    credentials: 'include',
    body: JSON.stringify([
        { "name": "New Project 2" },
        { "name": "Another project" },
        { "name": "Project X", "description": "A secret Project" }
    ]),
}).then(function(response) {
    return response.json();
}).then(function(json) {
    json.result.forEach(newProjectId => {
        // process result
    });
});

jQuery

$.ajax({
    url: '/structr/rest/Project/info',
    method: 'POST',
    data: JSON.stringify([
        { "name": "New Project 2" },
        { "name": "Another project" },
        { "name": "Project X", "description": "A secret Project" }
    ]},
    statusCode: {
        201: function(result) {
            result.forEach(function(project) {
                // process result
            });
        }
    }
});

curl

$ curl -s -HX-User:admin -HX-Password:admin http://localhost:8082/structr/rest/Project -XPOST -d '
    [
    { "name": "New Project 2" },
    { "name": "Another project" },
    { "name": "Project X", "description": "A secret Project" }
    ]'

Response

{
    "result": [
        "c431c1b5020f4430808f9b330a123159",
        "4384f4685a4a41d09d4cfa1cb34c3024",
        "011c26a452e24af0a3973862a305907c"
    ],
    "result_count": 3,
    "page_count": 1,
    "result_count_time": "0.000038485",
    "serialization_time": "0.000152850"
}

Updating Objects

To update an existing object, you must know its UUID. You can then send a PUT request to the entity endpoint of the object which is either /structr/rest// or /structr/rest/. Both URLs are valid, but we recommend to use the typed endpoint whenever possible. The generic UUID resource /structr/rest/ can be used if you don’t know the type of the object you want to update.

fetch()

fetch('/structr/rest/Project/' + id, {
    method: 'PUT',
    credentials: 'include',
    body: JSON.stringify({
        "name": "Updated name"
    }),
}).then(function(response) {
    return response.json();
}).then(function(json) {
    // process result
});

jQuery

$.ajax({
    url: '/structr/rest/Project/' + id,
        method: 'PUT',
        data: JSON.stringify({
        "name": "Updated name"
    }),
    statusCode: {
        200: function(result) {
            // process result
        }
    }
});

curl

$ curl -s -HX-User:admin -HX-Password:admin http://localhost:8082/structr/rest/Project/c431c1b5020f4430808f9b330a123159 -XPUT -d '{ "name": "Updated name" }'

The response of a successful PUT request contains the status code 200 OK with an empty result object.

Updating Multiple Objects

To update multiple objects at once, you can use the HTTP PATCH method on the collection resource of the target type, as shown in the following example.

fetch()
fetch('/structr/rest/Project', {
    method: 'PATCH',
    credentials: 'include',
    body: JSON.stringify([
        { "id": "6b0381c05a864bb0ab8dd5dcb937e391", "name": "New name" },
        { "id": "9acef23248f943f687e5d787202b9cda", "name": "Bulk Update" }
    ])
}).then(function(response) {
    return response.json();
}).then(function(json) {
    // process result
});
jQuery
$.ajax({
    url: '/structr/rest/Project',
    method: 'PATCH',
    data: JSON.stringify([
        { "id": "6b0381c05a864bb0ab8dd5dcb937e391", "name": "New name" },
        { "id": "9acef23248f943f687e5d787202b9cda", "name": "Bulk Update" }
    ]),
    statusCode: {
        200: function(result) {
            // process result
        }
    }
});
curl
$ curl -s -HX-User:admin -HX-Password:admin http://localhost:8082/structr/rest/Project -XPATCH -d '
    [
    { "id": "6b0381c05a864bb0ab8dd5dcb937e391", "name": "New name" },
    { "id": "9acef23248f943f687e5d787202b9cda", "name": "Bulk Update" }
    ]'

Deleting Objects

To delete objects, you can send a DELETE request, either to the entity resource of an existing object, or to the collection resource of a type. If you want to delete more than one object, you can use DELETE analogous to GET to delete all objects that would be returned by a GET request, including filters but without pagination.

Note: If you send an unintended DELETE request to the collection resource, for example, because you have not checked that the id parameter is empty, you delete all objects in that collection.

fetch()

fetch('/structr/rest/Project/' + id, {
    method: 'DELETE',
    credentials: 'include',
}).then(function(response) {
    return response.json();
}).then(function(json) {
    // process result
});

jQuery

$.ajax({
    url: '/structr/rest/Project/' + id,
    method: 'DELETE',
    statusCode: {
        200: function(result) {
            // process result
        }
    }
});

curl

$ curl -s -HX-User:admin -HX-Password:admin http://localhost:8082/structr/rest/Project/c431c1b5020f4430808f9b330a123159 -XDELETE

The response of a successful DELETE request contains the status code 200 OK with an empty result object.

Deleting Multiple Objects

fetch()
fetch('/structr/rest/Project?name=' + encodeURIComponent(name) + '&_inexact=1', {
    method: 'DELETE',
    credentials: 'include',
}).then(function(response) {
    return response.json();
}).then(function(json) {
    // process result
});
jQuery
$.ajax({
    url: '/structr/rest/Project?name=' + encodeURIComponent(name) + '&_inexact=1',
    method: 'DELETE',
    statusCode: {
        200: function(result) {
            // process result
        }
    }
});
curl
$ curl -s -HX-User:admin -HX-Password:admin "http://localhost:8082/structr/rest/Project?name=New&_inexact=1" -XDELETE

Deleting All Objects Of The Same Type

$ curl -s -HX-User:admin -HX-Password:admin "http://localhost:8082/structr/rest/Project"

This will delete all objects in the target resource.

Note: Beware that this will also delete all objects of inheriting types!

To only delete objects of a certain type without deleting inheriting types, we can use the internal type attribute as an additional filter as shown in the next query.

$ curl -s -HX-User:admin -HX-Password:admin "http://localhost:8082/structr/rest/Project?type=Project"

Related Topics

Virtual Types

Virtual Types create custom REST endpoints that transform and filter data from existing types. They allow you to expose different views of your data without modifying your schema or duplicating data in the database.

How Virtual Types Work

Each Virtual Type is based on a Source Type. When you request the Virtual Type’s REST endpoint, Structr retrieves instances of the Source Type, applies an optional filter expression, and transforms each object according to your Virtual Property definitions. The transformed data is returned directly - nothing is stored in the database.

The REST endpoint is automatically created at:

/structr/rest/{VirtualTypeName}

For example, a Virtual Type named PublicProject creates an endpoint at /structr/rest/PublicProject.

Accessing Virtual Types

You access Virtual Types through the REST API just like regular types:

curl:

curl http://localhost:8082/structr/rest/PublicProject \
  -H "X-User: admin" \
  -H "X-Password: admin"

JavaScript:

const response = await fetch('/structr/rest/PublicProject', {
    headers: {
        'Content-Type': 'application/json'
    }
});

const data = await response.json();

The response has the same structure as any other REST endpoint:

{
    "result": [
        {
            "id": "a3f8b2c1-d4e5-f6a7-b8c9-d0e1f2a3b4c5",
            "type": "PublicProject",
            "name": "Project Alpha",
            "status": "active",
            "managerName": "Jane Smith"
        }
    ],
    "result_count": 1,
    "page_count": 1,
    "query_time": "0.000127127",
    "serialization_time": "0.001092944"
}

Notice that the type field shows the Virtual Type name, and properties like managerName can be computed values that don’t exist on the source type.

Configuration

Virtual Types are configured in the Admin UI under the Virtual Types area. Each Virtual Type has:

Source Type

The Source Type provides the data that the Virtual Type transforms. All instances of this type are candidates for inclusion in the Virtual Type’s output.

Filter Expression

The Filter Expression is a script that determines which source objects appear in the output. Only objects for which the expression returns true are included.

For example, to expose only active projects:

$.this.status === 'active'

Or to expose only projects visible to the current user:

$.this.visibleToAuthenticatedUsers === true

Virtual Properties

Virtual Properties define which properties appear in the output and how they are transformed. Each Virtual Property has:

Setting Description
Source Name The property name on the Source Type
Target Name The property name in the Virtual Type output
Input Function Transforms data during input operations (e.g., CSV import)
Output Function Transforms data during output operations (REST responses)

If you only specify Source Name and Target Name, the property value is passed through unchanged. Use Output Functions when you need to transform or compute values.

Output Functions

Output Functions transform property values when data is read through the REST endpoint. The function receives the source object as $.this and returns the transformed value.

Renaming Properties

To expose a property under a different name, create a Virtual Property with the desired Target Name and a simple Output Function:

$.this.internalProjectCode

This exposes the internal internalProjectCode property as whatever Target Name you specify.

Computed Properties

Output Functions can compute values that don’t exist on the source type:

Full name from parts:

$.this.firstName + ' ' + $.this.lastName

Age from birth date:

Math.floor((Date.now() - new Date($.this.birthDate).getTime()) / (365.25 * 24 * 60 * 60 * 1000))

Related data:

$.this.manager ? $.this.manager.name : 'Unassigned'

Formatting Values

Output Functions can format values for display:

Date formatting:

$.date_format($.this.createdDate, 'yyyy-MM-dd')

Currency formatting:

'$' + $.this.amount.toFixed(2)

Input Functions

Input Functions transform data during write operations, primarily used for CSV import. When you import data through a Virtual Type, Input Functions convert the incoming values before they are stored.

For example, to parse a date string during import:

$.parse_date($.input, 'MM/dd/yyyy')

The incoming value is available as $.input.

Use Cases

API Facades

Create simplified views for external consumers without exposing your internal data model. You can flatten nested structures, rename properties to match external specifications, or hide internal fields:

A Virtual Type PublicProject might expose only name, status, and description from a Project type that has many more internal properties.

Filtered Endpoints

Create endpoints that return subsets of data without requiring query parameters:

Computed Views

Expose calculated values without storing them:

CSV Import Mapping

Virtual Types serve as the mapping layer for CSV imports. Each column maps to a Virtual Property, and Input Functions handle data conversion. For details, see the Data Creation & Import chapter.

Limitations

Virtual Types transform data at read time. They do not support:

For write operations, use the source type’s REST endpoint directly.

Related Topics

System endpoints

Name Description
Reset password endpoint
User self-registration endpoint
Login endpoint
JWT token endpoint
Logout endpoint
Schema information endpoint
HTTP access statistics endpoint
Runtime event log endpoint
Direct Cypher query endpoint
Structr environment information endpoint
User information endpoint HTTP endpoint that always returns the current user as a JSON object. URL path is /me.
Statistics Log endpoint
Maintenance command execution endpoint

Markdown Rendering Hint: Children of Topic(System endpoints) not rendered because MarkdownTableFormatter prevents rendering of children.

Servlets

Name Description
LoginServlet
HistogramServlet
ProxyServlet
HealthCheckServlet
LogoutServlet
UploadServlet File upload endpoint.
HtmlServlet Main entry point for HTML rendering.
EventSourceServlet
TokenServlet JWT token endpoint
DeploymentServlet
MetricsServlet
JsonRestServlet
OpenAPIServlet
DocumentationServlet
ConfigServlet
CsvServlet
FlowServlet
PdfServlet

Markdown Rendering Hint: Children of Topic(Servlets) not rendered because MarkdownTableFormatter prevents rendering of children.

Request parameters

Structr’s HTTP API supports a number of custom request parameters to influence the behaviour of the endpoints.

Key Name Description
_page Page number Request parameter used for pagination, sets the page number, value can be any positive integer > 0.
_pageSize Page size Request parameter used for pagination, sets the page size.
_sort Sort key Request parameter used for sorting, sets the sort key.
_order Sort order Request parameter used for sorting, sets the sort order, value can be ‘asc’ or ‘desc’ for ascending or descending order.
_inexact Search type Request parameter that activates inexact search.
_latlon Latitude/Longitude Request parameter used for distance search, specifies the center point of the distance search in the form latitude,longitude.
_location Location (country, city, street) Request parameter used for distance search, specifies the center point of the distance search in the form country,city,street.
_country Country Request parameter used for distance search, specifies the center point of the search circle together with _postalCode, _city, _street.
_postalCode Postal code Request parameter used for distance search, specifies the center point of the search circle together with _country, _city, _street.
_city City Request parameter used for distance search, specifies the center point of the search circle together with _country, _postalCode, _street.
_street Street Request parameter used for distance search, specifies the center point of the search circle together with _country, _postalCode, _city.
_distance Distance in kilometers Request parameter used for distance search, specifies the radius of the search circle.
_outputNestingDepth JSON Nesting Depth You can control how deeply nested objects are serialized in REST responses using the _outputNestingDepth request parameter. By default, Structr serializes nested objects to a depth of three levels, but increasing the nesting depth includes more levels of related objects in the response. This is useful when you need to access deeply nested relationships without making multiple requests.
_filename Download filename Request parameter that sets the ‘filename’ part of the Content-Disposition: attachment response header.
_as-data-url Download as data URL Request parameter that controls the response format of a file download. If set (with any value), it causes the file data to be returned in Base64 format ready to be used in a data URL.
_locale Locale Request parameter that overrides the locale for the current request.

Markdown Rendering Hint: Children of Topic(Request parameters) not rendered because MarkdownTableFormatter prevents rendering of children.

Request headers

Structr’s HTTP API supports a number of custom request headers to influence the behaviour of the endpoints.

Name Description
Accept
Access-Control-Request-Headers
Access-Control-Request-Method
Authorization
Cache-Control
Content-Type
Expires
If-Modified-Since
Last-Modified
Origin
Pragma
Refresh-Token
Range
Structr-Return-Details-For-Created-Objects
Structr-Websocket-Broadcast
Structr-Cascading-Delete
Structr-Force-Merge-Of-Nested-Properties
Vary
X-Forwarded-For
X-User Custom request header to authenticate single requests with username / password, used in header-based authentication.
X-Password Custom request header to authenticate single requests with username / password, used in header-based authentication.
X-StructrSessionToken Custom request header to authenticate single requests with sessionToken, used in header-based authentication.
X-Structr-Edition Custom response header sent by some Structr versions to indicate the Structr Edition that is running.
X-Structr-Cluster-Node

Markdown Rendering Hint: Children of Topic(Request headers) not rendered because MarkdownTableFormatter prevents rendering of children.
Integrate With Other Systems

REST Interface

The REST interface allows you to exchange data with external systems and expose business logic methods as REST endpoints. Methods accept arbitrary JSON input and return structured JSON output, making it easy to build custom APIs and integrate Structr into existing workflows or architectures.

Views

Views control the JSON representation of types in the REST API. By default, the REST API uses the public view, which you can customize by adding or removing attributes to match your requirements. For advanced use cases, you can create additional custom views and access them via separate URLs.

Example

For example, if you create a summary view for your Article type, you can access it with:

GET /Article/a3f8b2c1d4e5f6a7b8c9d0e1f2a3b4c5/summary

This returns a JSON document with only the attributes defined in that view, e.g. title, author and publishDate.

{
    "result": {
        "title": "Introduction to Structr",
        "author": "Christian Morgner",
        "publishDate": "2025-01-15"
    },
    "query_time": "0.008205901",
    "result_count": 1,
    "page_count": 1,
    "result_count_time": "0.000136110",
    "serialization_time": "0.000554177"
}

Read more about the REST Interface.