Endpoints
Manul automatically generate endpoints for your application, covering CRUD, method invocation and query operations.
These endpoints strive to meet the REST conventions as closely as possible, with one notable exception: the request path for method invocation uses a verb rather than a noun.
Preparation
Section titled “Preparation”Create a Manul project named “api-guide”:
manul new api-guideCreate a source file containing the User class:
class User( var name: string, var email: string)Deploy the appliction:
manul deployCreate Object
Section titled “Create Object”To create an object, send a POST request to the corresponding collection path.
The request body is a JSON object where each field corresponds to a constructor parameter. The field values are represented in a format described below.
The endpoint responds with 201 Created upon success. The response body contains the newly created object, including its ID and public fields. The field values are represented with the same format used in the request.
Example
Section titled “Example”The following request creates a user:
POST /api-guide/users
{ "name": "someone", "email": "someone@example.com"}Response:
HTTP/1.1 201 Created
{ "id": "101", "name": "someone", "email": "someone@example.com"}Collection Path
Section titled “Collection Path”A collection path represents the resource containing all objects of a specific class.
It starts with /{application}/, followed by a subpath derived from the class name using the following process:
- Replace
.with/. - Change CamelCase to kebab-case.
- Pluralize the last word.
For example, the class pkg.MyObject is converted as follows:
pkg.MyObject -> pkg/MyObject -> pkg/my-object -> pkg/my-objects
Note: Manul uses intelligent pluralization rules, rather than simply appending an “s”. For example, “box” is correctly pluralized as “boxes”.
Following this rule, the collection path for User objects is /api-guide/users.
Value Format
Section titled “Value Format”Values are represented in request and response bodies according to its type:
- string/boolean: Use the corresponding JSON type.
- char: Represented as single-character strings.
- Integer/Float: Represented as numbers.
- Array: Represented as JSON arrays.
- Object: Represented as JSON objects. See references and value object for specific formats.
Note: this value format is used throughout this guide.
Retrive Object
Section titled “Retrive Object”To retrieve a single object using its ID, send a GET request to the object path.
The response body is a JSON object containing object ID and all public fields. Field values are represented in the same format used in the creation request body.
Field values are represented using the format described in object creation.
Example
Section titled “Example”The request below retrieves the user created in the previous example:
GET /api-guide/users/101Response:
HTTP/1.1 200 OK
{ "id": "101", "name": "someone", "email": "someone@example.com"}Object Path
Section titled “Object Path”An object path designates a single object. It’s constructed by appending / and the object ID to the collection path.
Update Object
Section titled “Update Object”To update an object, send a PATCH request to the object path.
The request body is a JSON object of fields to be updated. The fields must be public and mutable.
The following request changes the name of the user:
PATCH /api-guide/users/101
{ "name": "someone1"}The endpoint responds with the updated object in the body:
HTTP/1.1 200 OK
{ "id": "101", "name": "someone1", "email": "someone@example.com"}Delete Object
Section titled “Delete Object”To delete an object, send a DELETE request to the object path.
To test without losing our original user, let’s create a temporary user:
POST /api-guide/users
{ "name": "tmp", "email": "tmp@example.com"}Send a delete request to remove the newly created user:
DELETE /api-guide/users/{tmp-id}The endpoint responds with 204 No Content.
Invoke a Method
Section titled “Invoke a Method”While the previous examples covers basic CRUD operations, Manul also supports invoking public methods through endpoints.
Let’s update the example by adding a method to the User class:
class User( var name: string, var email: string) {
var credits = 0
fn addCredit(amount: int) -> int { require(amount > 0, "Amount must be positive") credits += amount return credits }
}Deploy the changed code
manul deployTo invoke a method, send a POST request to the method path.
The method path is constructed by taking the object path of the receiver and appending / followed by the method name in hyphenated form.
The request body is a JSON object where each field corresponds to a method parameter.
If the method returns a value, the endpoint responds with 200 OK. The response body contains the return value, serialized with the same format used in the creation request. If the method has no return value, the endpoint responds with 204 No Content.
To add credts to a user, send the following request:
POST /api-guide/users/101/add-credit
{ "amount": 100}Response:
HTTP/1.1 200 OK
100Note: Even though the return value is a single number, the content type is still application/json.
Query Objects
Section titled “Query Objects”Manul objects are fully indexed and searchable. To query objects, send a GET request to the collection path.
Query Parameters
- Exact Match:
?name=someone. incondition: Provide multiple query parameters with the same name. E.g.,?name=Jack&name=Alice- Range condition: For numeric fields, use
{field}.ge(greater than or equal) and{field}.le(less than or equal) to specify a range, E.g.,?credits.ge=50&credits.le=200 - Pagination: Use
_pageand_pageSizeto control pagination.
The response of this endpoints is a JSON object with two fields:
items: The array of objects in the current pagetotal: The number of all objects matching the query
Objects in the items array use the same format as the response of the retrieval endpoint.
Example:
GET /api-guide/users?name=someone&credits.ge=50&_page=1&_pageSize=20Response:
HTTP/1.1 200 OK
{ "items": [ { "name": "someone1", "email": "someone@example.com", "credit": 100 } ], "total": 1}Multi Get
The query endpoint can also be used to achieve multi get by passing multiple id parameters.
GET /api-guide/users?id=101&id=102The response looks the same of the previous example.
References
Section titled “References”A reference points to an object. It’s represented as a JSON object in request and response bodies. It contains an id field and an optional summary field.
The id field stores the ID of the target object, while the summary field stores a short descriptive text (if the target object has one).
Inerface Definition:
interface Reference { id: string summary?: string}Let’s update the example by adding a reference field to User:
class Group( @Summary var name: string)
class User( var name: string, var email: string, var group: Group?) {
// Class body omitted
}Deploy the changes:
manul deployFirst, create a group:
POST /api-guide/groups
{ "name": "admin"}Next, update the user to link to the group:
PATCH /api-guide/users/101
{ "group": { "id": "201" }}Retrive the user again to see what it looks now:
GET /api-guide/users/101Response
HTTP/1.1 200 OK
{ "id": "101", "name": "someone1", "email": "someone@example.com", "credits": 100, "group": { "id": "201", "summary": "admin" }}** Querying by References**
Manul supports querying by references using parameter of the form {field}.id.
GET /api-guide/users?group.id=202Value Objects
Section titled “Value Objects”Value objects are represented in request and response bodies as nested JSON objects containing their full content.
Let’s add a value class to to the example.
value class Address( street: string, city: string, state: string)
class User( var name: string, var email: string, var group: Group?, var address: Address?) {
// Class body omitted
}Value objects don’t have identity; therefore you do not need create an address object separately. Instead, update the user object with a nested address object directly:
PATCH /api-guide/users/101
{ "address": { "street": "123 Java Lane", "city": "San Francisco", "state": "CA" }}Retrieve the object and you will see a nested address in the response:
{ "name": "someone1", "email": "someone@example.com", "credits": 100, "group": { "id": "201", "summary": "admin" }, "address": { "street": "123 Java Lane", "city": "San Francisco", "state": "CA" }}Error Response
Section titled “Error Response”A non-2xx HTTP status indicates an error. The body of an error response is a JSON object with the following format:
interface ErrorResponse { code: string message: string}code: a machine-readable error code (e.g.,OBJECT_NOT_FOUND).message: a human-readable text explaining the error.
Let’s trigger an error by invoking addCredit with a negative amount:
POST /api-guide/users/101/add-credit
{ "amount", -1}Response:
HTTP/1.1 400 Bad Request
{ "code": "FUNCTION_EXECUTION_ERROR", "message": "Amount must be positive"}