Skip to content

Resources & method types

Resources describe externally visible APIs. Resource methods reference reusable method types; HTTP verbs occur when a method type is declared, not as standalone methods inside a resource.

Complete Base example

cmn
package com.example.customer.api

import org.joinedworkz.facilities.common.base
import org.joinedworkz.facilities.common.base.api

platform Base

type Customer {
    id**: Id
    name*: Name
}

type Order {
    orderId**: Id
}

type ErrorView {
    message*: String
}

methodtype removeCustomer DELETE
    instance=true
    produces=Id
    success=200

abstract resource /orders as Order[] by orderId {
    query()
    read()
}

resource /customers as Customer[] by id {
    query(name: String)
    create() consumes=Customer
    read()
        404: ErrorView
    update()
    removeCustomer()

    ./orders
}

The by id clause identifies collection items by the id field. ./orders adds the abstract /orders resource below each customer item. removeCustomer deliberately has a unique name because of the Base 1.3.80 delete limitation described below.

Resource shapes and identifiers

as Type declares a singleton representation. A cardinality after the type declares a collection representation; [] is the common unbounded form:

cmn
type SystemSettings {
    maintenanceMode*: Boolean
}

type Customer {
    id**: Id
    name*: Name
}

type AuditRecord {
    message*: String
}

resource /settings as SystemSettings {
}

resource /customers as Customer[] by id {
}

resource /audit-records as AuditRecord[] by recordId: Id {
}

The two by forms carry different information:

  • by id references the id field of the complex representation. The transformed Core Model retains that field reference.
  • by recordId: Id declares an identifier parameter by name and type. It does not reference a same-named representation field.

Several identifiers can be separated with commas. For an unbounded Type[] resource with a complex representation and no explicit by clause, the release 1.3.80 transformer derives an identifier from the representation's key field when one exists. An explicit clause is preferable when the public path should not depend on that inference.

Nested and reusable resources

A nested resource beginning with / belongs to the collection-level path. A resource beginning with ./ belongs to the item-level path and therefore follows the parent identifiers. Reusable subresources can be declared abstract and referenced from another resource:

cmn
type Customer {
    id**: Id
}

type CustomerSummary {
    count*: Integer
}

type Order {
    orderId**: Id
}

abstract resource /orders as Order[] by orderId {
}

resource /customers as Customer[] by id {
    /summary as CustomerSummary {
    }

    ./orders
}

This models /customers/summary at collection level and /customers/{id}/orders at item level. Nested resources can also be declared inline, as /summary is here.

Method types and HTTP verbs

The grammar defines reusable method types with this form:

cmn
methodtype exportCustomers GET
    produces='text/csv'
    success=200
    404: ErrorView

The available verbs are GET, POST, PUT, PATCH, and DELETE. The Base facility supplies two groups of method-type names:

  • raw: get, post, put, patch, delete;
  • opinionated: create, start, execute, read, downloadText, update, query, and list.

Known release 1.3.80 limitation: BaseApi.cmn declares the name delete twice, once as a raw method type and once with opinionated item-level defaults. Do not rely on which set of defaults an unqualified Base delete() reference inherits. Define a uniquely named DELETE method type for a Base-only model, or use SpringBoot's unambiguous deleteEntity helper.

The SpringBoot facility additionally supplies createEntity, readEntity, updateEntity, queryEntities, and deleteEntity. These names are facility-defined model elements, not CMN keywords.

Method scope and representations

instance=false is the transformer default and places a method on the resource itself. instance=true makes it an item-level operation and uses the identifiers of the resource path.

Properties on a concrete resource method take precedence over defaults from its method type. consumes and produces can name explicit CMN types, including collection and dictionary cardinalities:

cmn
methodtype getAttributes GET
    produces=String[String]
    success=200

String[String] denotes a dictionary with String values and String keys. The Base-oriented release 1.3.80 generator stack also interprets these quoted method-property conventions:

  • '*' lets consumes or produces derive the resource representation;
  • '*[]' lets consumes or produces derive a collection of that representation;
  • produces='**' uses the key type of a complex resource representation.

These values are generator conventions, not separate CMN grammar keywords.

Resource methods

A resource method starts with a referenced method type and may have a local name, parameters, properties, and additional results:

cmn
resource /customers as Customer[] by id {
    query(active: Boolean)
    read readCustomer()
        404: ErrorView
    create() consumes=Customer
}

Parameters without * are optional; parameter*: Type is mandatory. Properties such as consumes, produces, success, and instance are defined by the active profile or method type.

Additional results use status-code: representation. A dash means that the response has no body. There is no response(...) construct and responses are not enclosed in braces.