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 authenticate a request, how to configure these permissions, and how to set up CORS for cross-origin requests.

Sessions, JWT, OAuth and two-factor authentication are covered in detail in the Security chapter.

Authentication Methods

Structr determines the user context for each request by checking the following, in order:

  1. An OAuth login flow (external authentication), which runs before all other checks
  2. A session cookie, unless the request carries an Authorization header
  3. A JWT in the Authorization header; when this header is present, Structr skips the session check
  4. A session token in the X-StructrSessionToken header
  5. The X-User and X-Password headers

If none of these yield a user, Structr treats the request as anonymous.

The X-StructrSessionToken header carries the id of an existing session, the value that the login endpoint sets as JSESSIONID cookie, so that a client without cookie support can reuse its session. Structr refuses the token when that session has timed out or no longer exists.

Header authentication is described below because it is the method used in the REST examples throughout this guide. For the other methods, see the Security chapter.

Header Authentication

Send the credentials as X-User and X-Password headers:

curl:

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

Structr authenticates each request on its own: it verifies the credentials, resolves the user, and processes the request in that user’s context. No session is created and no token is stored, so there is nothing to expire, refresh or revoke.

That makes header authentication the simplest option for scripts, scheduled jobs and server-to-server calls. For browser applications and mobile clients, prefer sessions or JWT, which avoid transmitting the password repeatedly.

Security Considerations

Because the credentials travel with every request:

Prefer a dedicated service user that holds only the permissions its client needs, rather than an administrator account. Such a user can be revoked without affecting anyone else, and a misconfigured client then blocks only that account.

Authenticating successfully does not by itself grant access to any endpoint. Access is governed by Resource Access Permissions, described next.

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": "Access denied",
    "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 or a subcollection contains the method or property name verbatim; only view names are 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/tasks
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": "Access denied",
    "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" -XDELETE

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" -XDELETE

Related Topics

Method Parameters

All schema methods and user-defined functions in Structr can be called via the REST API. This article explains how to pass parameters to these methods and how to access them from within your code.

The parameter handling described here applies to all method types:

Method Type Endpoint this inside the method
User-defined function /structr/rest/myFunction not available
Static method /structr/rest/MyType/myMethod not available
Instance method /structr/rest/MyType/<uuid>/myMethod the object identified by <uuid>
Method on current user /structr/rest/me/myMethod the authenticated user

The way you pass and access parameters is identical across all of these. Only this differs.

Endpoints

The REST endpoint depends on the method type:

/structr/rest/<functionName>                      (user-defined function)
/structr/rest/<TypeName>/<methodName>             (static method)
/structr/rest/<TypeName>/<uuid>/<methodName>      (instance method)
/structr/rest/me/<methodName>                     (method on current user)

For example, a user-defined function called processOrder is available at /structr/rest/processOrder, while a static method findOverdue on the type Invoice is available at /structr/rest/Invoice/findOverdue.

Each method is configured to accept a specific HTTP verb (POST, GET, PUT, PATCH, or DELETE). Requests with a different verb will return a 405 Method Not Allowed error.

Passing Parameters

How you pass parameters depends on the HTTP verb.

POST, PUT, and PATCH

For these verbs, parameters are sent as a JSON object in the request body. Each top-level key in the JSON object becomes a named parameter inside the function.

Request
$ curl -s -HX-User:admin -HX-Password:admin \
    -H"Content-Type: application/json" \
    -XPOST http://localhost:8082/structr/rest/processOrder \
    -d '{ "orderId": "ORD-2026-001", "priority": 3, "express": true }'
Using fetch()

When calling from JavaScript in the browser, use the body property (not data) and set the Content-Type header explicitly:

fetch('/structr/rest/processOrder', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ orderId: 'ORD-2026-001', priority: 3, express: true })
});

Common mistake: The fetch() API uses body for the request payload. Using data (which is a jQuery convention) sends an empty body, and your function will receive no parameters.

GET and DELETE

For GET and DELETE requests, there is no request body. Instead, values can be passed as additional path segments:

/structr/rest/myTestFunction/value1/value2

Path segments require declared parameters. Structr maps each segment to the corresponding declared parameter by position. Without declared parameters (or with fewer declared parameters than path segments), the request fails with a 422 Unprocessable Entity error:

{
  "code": 422,
  "message": "Tried to call user-defined function 'myTestFunction‛ with illegal arguments. To fix this error, you can either specify method parameters, or call the method with a single argument of type object, e.g. { "name": "example" }."
}

For a function with declared parameters foo and bar, a request to /structr/rest/myTestFunction/x/y gives you:

{
    let foo = $.args.foo;           // "x"
    let bar = $.args.bar;           // "y"
    let bar2 = $.retrieve('bar');   // also "y"
}

To pass values to a GET/DELETE method without declaring parameters, use query string parameters and read them via $.request inside the function (see Accessing Query String Parameters below).

URL Compliance

By default, no URL violations are allowed. When using plain string parameters (i.e. accepting arbitrary user input) via the path for URL Routing or GET methods, you may need to explicitly allow certain URL violations via the configuration key httpservice.uricompliance.allowedviolations. Relevant options include:

For URL Routing, the first violation can be circumvented by adding a static element to each path segment. The latter two violations can be avoided using a Base64UrlString parameter, since it produces URL-safe output by design.

When calling methods via GET and passing named parameters via path segments, the first violation can not be avoided when empty values are supposed to be valid. The latter two violations can be avoided by manually using URL-safe transport encodings like base64url for parameter values.

Accessing Parameters in Code

JavaScript

In JavaScript, use the $.args object (or its aliases $.arguments and $.methodParameters):

{
    let orderId  = $.args.orderId;
    let priority = $.args.priority;
    let express  = $.args.express;

    $.log('Processing order ' + orderId + ' with priority ' + priority);
}

All three names refer to the same object:

Accessor Description
$.args Short form (recommended)
$.arguments Alias for $.args
$.methodParameters Alias for $.args

You can also use $.retrieve():

{
    let orderId = $.retrieve('orderId');
}

StructrScript

In StructrScript, use the retrieve() function:

${retrieve('orderId')}

Accessor Reference

The following table clarifies which accessors work for method parameters and which do not:

Accessor Works for… Notes
$.args Enumeration or inspection of all parameters Returns a map-like object, e.g. {foo: "x", bar: "y"}
$.args.paramName Named parameters Direct property access
$.retrieve('paramName') Named parameters Falls back to values stored with $.store() if no such parameter exists
$.retrieve (without call) Not for inspection Returns the function reference itself, not the parameters map
Object.keys($.args) Listing parameter names Useful for inspecting what was passed
$.get('paramName') Not for parameters Resolves a property on the current entity (this)
$.requestStoreGet('paramName') Not for parameters Only reads values previously stored with $.store() or $.requestStore

To inspect all current parameters, use $.args directly – it returns a readable map of all parameter names and values. $.retrieve is a function and only produces useful output when called with a key argument.

For clean named access, declare parameters on the function. This is the recommended approach for any method that takes arguments.

Accessing Query String Parameters

If you need to read query string parameters from a GET request (e.g. /structr/rest/search?q=test&limit=10), use the $.request object:

{
    let query = $.request.q;
    let limit = $.request.limit;
}

The $.request object exposes query string parameters as properties, reading directly from the underlying HTTP servlet request. This works regardless of the HTTP verb. If a parameter appears multiple times in the query string, $.request returns the single value for one occurrence and an array for multiple occurrences.

Declared Parameters

You can declare typed parameters on a user-defined function in the Code area of the Admin UI. Declared parameters serve two purposes: they enable positional argument mapping for GET/DELETE requests, and they provide automatic type conversion.

Positional Mapping

When a function has declared parameters, positional path arguments are mapped to parameter names in order. For example, if you declare two parameters customerId (String) and year (Integer), a GET request to:

/structr/rest/getCustomerReport/C-100/2026

maps "C-100" to customerId and "2026" to year (converted to an Integer). Inside the function, you access them like any other parameter:

{
    let id   = $.args.customerId;  // "C-100"
    let year = $.args.year;        // 2026 (Integer)
}

Automatic Type Conversion

If you declare a parameter type, Structr converts incoming values automatically. Supported conversions:

Declared Type Input Conversion
Integer, int Number or numeric String Converted to Integer
Long, long Number or numeric String Converted to Long
Double, double Number or numeric String Converted to Double
Float, float Number or numeric String Converted to Float
Date ISO 8601 String Parsed to Date

If a value cannot be converted (e.g. passing "abc" for an Integer parameter), the request fails with a 422 Unprocessable Entity error.

Without declared parameters, all values pass through as-is. JSON numbers arrive as numbers, strings as strings, objects as maps.

store() vs. Method Parameters

The $.store() / $.retrieve() mechanism and method parameters are separate. When you call $.retrieve('key'), Structr first checks whether key is a method parameter. Only if no parameter with that name exists does it fall back to values stored with $.store().

This means that if you call $.store('name', ...) where name is also a method parameter, the stored value becomes inaccessible through $.retrieve() – the parameter always takes precedence. Structr logs an info message when this happens. To avoid the conflict, use $.requestStore to read and write stored values directly:

{
    // Parameter "name" is passed via REST
    let paramName = $.retrieve('name');     // returns the parameter value

    $.requestStore.name = 'other value';    // writes to the request store
    let stored = $.requestStore.name;       // reads from the request store
}

Complete Example

A user-defined function createTask configured for POST:

{
    let projectId = $.args.projectId;
    let taskName  = $.args.name;
    let assignee  = $.args.assigneeEmail;

    $.assert(!$.empty(projectId), 422, 'projectId is required');
    $.assert(!$.empty(taskName),  422, 'name is required');

    let project = $.find('Project', projectId);
    $.assert(project != null, 404, 'Project not found');

    let user = null;
    if (!$.empty(assignee)) {
        user = $.first($.find('User', { eMail: assignee }));
    }

    let task = $.create('Task', {
        name: taskName,
        project: project,
        owner: user
    });

    return task;
}

Request

$ curl -s -HX-User:admin -HX-Password:admin \
    -H"Content-Type: application/json" \
    -XPOST http://localhost:8082/structr/rest/createTask \
    -d '{ "projectId": "a01e2889c250...", "name": "Design review", "assigneeEmail": "alice@example.com" }'

Response

{
    "result": {
        "id": "f7a3b921...",
        "type": "Task",
        "name": "Design review"
    },
    "serialization_time": "0.001234"
}

System endpoints

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

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

Servlets

Name Description
ConfigServlet
CsvServlet
DocumentationServlet
JsonRestServlet
MetricsServlet
OpenAPIServlet
DeploymentServlet
EventSourceServlet
HealthCheckServlet
HistogramServlet
HtmlServlet Main entry point for HTML rendering.
LoginServlet
LogoutServlet
ProxyServlet
TokenServlet JWT token endpoint
UploadServlet File upload endpoint.
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.