API

An app's API defines the kinds of calls or requests that can be made by the app's frontend, how to make them, how they are handled by the backend, the data formats that should be used, the conventions to follow, etc.

Syntax overview

Imagine allows you to represent your app's backend API handling using the following structure and syntax:

settings
api:
# [RestAPI, GraphQL]
format: <format-choice>
end settings
API /'api-name' {
actions [<action>, <action>...]
permissions [<permission-name-1>, <permission-name>...]
model <model-name>
filter [<field-name-1>, <field-name-2>...]
data [<field-name-1>, <field-name-2>...]
}

Example

As an example of a API, let's take the music app, Musica, that has the concepts of API handling for the api-name musician-profile.

Representation in Imagine syntax

The Musica API handling would be represented in Imagine's syntax as follows:

Model Musician {
id integer [primary key, default auto-increment]
first_name string [maxlength 40]
last_name string [maxlength 40]
instrument string [default "piano", choice ["piano","guitar","drums","bass"]]
rating float [default 2.0, range 1.0 10.0]
}
API /musician-profile {
actions [Read, ReadMany]
model Musician
data [first_name, last_name]
}

Settings

Imagine's API settings allow you choose whether you want your API to be represented in RestAPI or GraphQL format.

settings
# [RestAPI, GraphQL]
apiformat: RestAPI
end settings

Domain-specific syntax

Imagine's Domain-specific syntax for the API domain currently supports the following modules: API and CustomAPI

We will describe the various elements in this module in detail below.

API

Imagine's API module allows you to create API endpoints that can manipulate data models in your app.

API /'api-name' {
actions [<action1>, <action2>]
permissions [<permission1>, <permission-2>]
model <model-name>
filter [<field-name>, <field-name>]
data [<field-name>, <field-name>]
}

Note: We do not current support making API calls in the app's frontend. We will allow this capability in future releases, when we support frontend frameworks like React.


The various elements of Imagine's API module are as follows:

api-name

In our syntax, API is followed by a unique user-selected <api-name> (or /<api-name> - the forward slash / is optional) for that API endpoint. Please see this for rules on selecting a <api-name>

In the example below, musician-profile is an <api-names>.

API /musician-profile {
actions [Read, ReadMany]
model Musician
data [first_name, last_name]
}


actions

The API's <actions> allow you to define how you want to manipulate a data model.

The Imagine compiler currently supports the following <actions>:

  • Create - Creates a new object instance in a data model. The payload consists of data for fields of the data model.
  • Read - Reads one instances of a data model from the storage. The payload consists of the unique identifier for the object
  • Update - Updates one or more fields for an instance of a data model. The payload consists of values of the fields that should be updated.
  • Delete - Deletes an instance of a data model. The payload consists of the unique identifier for the object
  • CRUD - Implements all CRUD (Create, Read, Update, Delete) actions on a data model.
  • ReadMany - Reads many instances of a data model from the storage after filtering by certain criterion. By default, the API would return all instances. (The API caller can also use filter to provide a way to specify which objects to read)

In addition to the common API <actions> listed above, a typical app has a number of custom APIs, specific to the app's use case, that implement different functionalities within the app. While you would write the code for any custom APIs yourself, you can generate an custom API code template using Imagine by specifying custom as your API <action>. You can read mode about the specific syntax required for this in the custom API code module.

In the example below, see a CRUD API config that implements all of the CRUD actions on a particular model:

API /<api-url> {
actions CRUD
Model <model-name>
}


permissions

The API's <permissions> allow you to select which of the <permissions> defined in the Authorization domain are needed to be able to access this particular API handler.

Note: If no permissions are specified in the API module, the API handler is accessible to all API callers in the app.

In the example below, see a /publish_album API that

Permission invite-new-musician
API /create-musician {
permissions [invite-new-musician]
actions [Create]
model Musician
data [first_name, last_name, age, instrument]
}

model

The API's <model> allow you to select the model you want to manipulate with this particular API handler.

In the example below, the /publish_album API manipulates the Musician model

Permission invite-new-musician
API /create-musician {
permissions [invite-new-musician]
actions [Create]
model Musician
data [first_name, last_name, age, instrument]
}



data

<data> specifies the data that the API handler should expect or return.

Note: A special keyword ALL can be used to indicate all fields of the Model to be included in the data.

In the example below, the /musician-profile API only returns the data relevant to be shown in a profile page. Other data which are not needed for the profile page is not returned.

Model Musician {
id integer [primary key, default auto-increment]
first_name string [maxlength 40]
last_name string [maxlength 40]
instrument string [default "piano", choice ["piano","guitar","drums","bass"]]
rating float [default 2.0, range 1.0 10.0]
age integer []
}
API /musician-profile {
actions [Read, ReadMany]
model Musician
data [first_name, last_name]
}


filter

<filter> lists the various data fields on which an API can be filtered, and is only applicable for Read and ReadMany <actions>.

Note: A special keyword ALL can be used to indicate all fields of the Model to be included in the filter list.

In the example below, the API would allow filtering based on first_name, last_name and instrument.

API /search-musician {
actions [ReadMany]
model Musician
filter [first_name,last_name,instrument]
data ALL
}


Custom API

If you want to create a custom API handler with <actions> outside the supported CRUD actions, we provide a simple scaffolding for custom APIs.

While the code for the custom logic for a custom API to be filled in by the app developer, we help generate the code for permissions checking for the API.

A custom API is created by specifying custom as the actions within the API module.

Note: If you are defining the <action> as custom, the API module, will not support <model> <filter> or <data> parameters. If you use any of these parameters, with a custom <action> chosen, the compiler will return an error, as you will need to write custom specifications for a custom API.

In the example below, [Config for a custom API /make_payment that requires the caller to have the permission payment.]

[The generated code will do the permissions checking but will require the app developer to fill in the code required for executing the payment.

API /make_payment {
permissions [payment]
actions custom
}