Why does EK9 complain that my application registers services but the program creates no HTTP?

← Web Services · Ref: Q1375

A registered service is only reachable while something serves it. If the program linked to an application never creates an HTTP instance, the program runs its body and EXITS — the service is constructed, consumes its dependencies, and can never receive a request. Nothing at runtime reports this: the process exits successfully, so the mistake stays invisible until someone notices the endpoint was never live. EK9 raises E12020 at compile time instead.

TWO DIRECTIONS OF ONE MISTAKE

E12020 is the mirror of E12021. E12021 fires when a program creates HTTP but its application registers no services (a server with nothing to route to). E12020 fires when the application registers services but the program creates no HTTP (services with nothing serving them). Both are decidable at compile time because the application's registrations and the program's variable declarations are already resolved.

THE TWO FIXES

Either serve them — create NetworkProperties, an HTTP instance, and call serve() — or SPLIT the application. Splitting is the right answer when the program genuinely needs only the components: give it an application registering just those, and keep the service registration in the application used by the serving program. Conflating serving wiring with component wiring is exactly what leaves a REST service registered and permanently unreachable.

Example

defines module qa.webservices.serviceswithouthttp

  defines constant

    plainText <- "text/plain"

  defines service

    GreetService :/greet open

      hello() as GET for :/hello
        <- response as HTTPResponse: () with trait of HTTPResponse
          override status() as pure
            <- rtn as Integer: 200
          override content()
            <- rtn as String: "hello"
          override contentType() as pure
            <- rtn as String: plainText
          default operator ?

  defines component

    Repository as abstract
      describe() as pure abstract
        <- rtn as String?

    InMemoryRepository extends Repository
      override describe() as pure
        <- rtn as String: "in-memory"

  defines application

    // Serving wiring: registers the service, so its program MUST create HTTP.
    GreetApp
      register InMemoryRepository() as Repository
      register GreetService()

    // Component wiring only: safe for a program that does not serve.
    RepositoryOnly
      register InMemoryRepository() as Repository

  defines program

    ServeIt() with application of GreetApp
      stdout <- Stdout()
      props <- NetworkProperties(19099)
      httpServer <- HTTP(props)
      stdout.println("serving")
      httpServer.serve()

    ReportOnly() with application of RepositoryOnly
      repository as Repository!
      stdout <- Stdout()
      stdout.println(repository.describe())

Common mistakes

E12020 — GreetApp registers GreetService, so a program wired to it must create and serve an HTTP instance. Without one the program prints and exits and the service is never reachable. See ek9 -h E12020 for details.

Incorrect:

NoServe() with application of GreetApp
      stdout <- Stdout()

Correct:

ServeIt() with application of GreetApp
      props <- NetworkProperties(19091)
      httpServer <- HTTP(props)
      httpServer.serve()

E12020 — When a program needs only the components, split the application: keep the service registration in the application the serving program uses, and give the non-serving program one that registers only what it needs. See ek9 -h E12020 for details.

Incorrect:

AccessPoint
      register InMemoryRepository() as Repository
      register Addresses()

Correct:

RepositoryOnly
      register InMemoryRepository() as Repository
Other ways to ask this
  • What is E12020 in EK9?
  • I registered a service but never see it respond — why?
  • Can I use an application that registers services from a non-serving program?
  • Why must a program with a service application create HTTP?
  • How do I share components with a program that does not serve HTTP?

Coming from another language?

Spring Boot: @RestController beans are picked up by the embedded server automatically, so 'registered but never served' cannot easily arise — the framework always starts a server. JAX-RS/Jersey: resources are registered in an Application subclass and served by the container; an unserved resource is a deployment error found at runtime. Node/Express: routes are attached to an app that must still call listen() — forgetting listen() is the same bug, but it is only visible when the process exits. EK9: the compiler rejects it, so the process can never be built in that state.

Keywords: injection, E12020, E12021, application, wiring, web, component, serve, http, rest, service, register