What are the basic rules for EK9 service definitions?

← Web Services · Ref: Q944

EK9 services enforce three fundamental rules:

1. CONCRETE BODIES REQUIRED (E07740)

Every service method must have a concrete implementation body. A method with no implementation is E07740 ('implementation not provided, but services cannot be abstract'): abstract methods cannot serve HTTP requests.

2. SUPPORTED OPERATORS ONLY (E07670)

A service may declare only these operators: +, +=, -, -=, :^:, :~: and ?. The CRUD ones are commonly mapped as:
- += for POST (create)
- -= for DELETE
- :~: for PATCH (merge)
- :^: for PUT (replace)
Any other operator, such as == or <, is E07670.

3. NO PROTECTED NON-WEB METHODS (E07240)

A service may have helper methods that are not HTTP-bound, either plain or 'private'. What it may not do is mark such a method 'protected' (E07240): nothing can extend a service, so 'protected' has no meaning.

See Q657 for URI mapping. See Q659 for HTTPResponse. See Q660 for CRUD operators. See Q685 for method bodies.

Example

defines module qa.services.basics.overview

  defines class

    <?-
      Models an HTTP response with status and content.
      Demonstrates the pattern services use for endpoint handlers.
    -?>
    HealthResponse
      statusCode as Integer: 200
      responseBody as String: `{"status": "ok"}`
      responseType as String: "application/json"

      HealthResponse()
        ->
          givenStatus as Integer
          givenBody as String
        this.statusCode :=: givenStatus
        this.responseBody :=: givenBody

      statusCode()
        <- rtn as Integer: Integer(statusCode)

      responseBody()
        <- rtn as String: String(responseBody)

      responseType()
        <- rtn as String: String(responseType)

      operator $ as pure
        <- rtn as String: `HealthResponse[${statusCode}]`
      operator #^ as pure
        <- rtn as String: $this
      default operator ?

    <?-
      Simulates a service endpoint handler.
      In real EK9 services, methods must have concrete bodies (E07740).
    -?>
    HealthHandler
      endpoint as String: "/api/health"

      handleGet()
        <- rtn as HealthResponse: HealthResponse(200, `{"status": "ok"}`)

      endpoint()
        <- rtn as String: String(endpoint)

      operator $ as pure
        <- rtn as String: `Handler[${endpoint}]`
      operator #^ as pure
        <- rtn as String: $this
      default operator ?

  defines program

    ServiceBasicsDemo()
      stdout <- Stdout()
      handler <- HealthHandler()
      healthResult <- handler.handleGet()

      stdout.println("Service basics:")
      stdout.println("  Methods must have concrete bodies")
      stdout.println("  Service operators: +, +=, -, -=, :^:, :~: and ?")
      stdout.println("  Non-web helper methods cannot be protected")
      stdout.println($healthResult)
Other ways to ask this
  • What triggers E07740 SERVICE_WITH_NO_BODY_PROVIDED?
  • What triggers E07670 SERVICE_OPERATOR_NOT_SUPPORTED?
  • Why is a protected helper method in a service rejected with E07240?
  • What operators can services use for CRUD?

Coming from another language?

Java Spring: controllers can have private helper methods. Python Flask: handler functions are plain Python. Go: handlers are plain functions. EK9: services hold HTTP endpoints plus plain or private helper methods (never protected), with a fixed set of operators and every method given a body.

Keywords: E07670, CRUD, E07240, method, E07740, web, service, body, basics, operator