---
title: hello-world example
description: A small HTTP service with a health check and a hello-world route.
slug: /examples/hello-world
order: 1
visibility: public
redirects_from: [/examples/hello-world-example]
---

<!-- Generated by scripts/build_scaffold_examples.mjs from the scaffold templates. -->

# hello-world example

A small HTTP service with a health check and a hello-world route.

These files come from the same source as the scaffold command. They are the starter files before the command adds library packages, application settings, and installed dependencies.

## With a shell

Run this in a new project's root:

```text
turnzero scaffold --template hello-world
```

Follow the command's next steps. It writes the application in `app/`, takes the required library packages, installs dependencies, and runs the tests. [The turnzero command](/concepts/turnzero-command#starting-a-new-service) explains setup and options.

## From a chat

Follow [Deploying from a chat](/getting-started/deploy-an-application#deploying-from-a-chat-whose-shell-cannot-reach-the-platform). Read the files below, supply the library versions in the manifest, and send the application files through `deploy_files`.

The scaffold can bind its manifest to an application with `--application <application id>`. From a chat, name the application explicitly in `submit_manifest` and `deploy_files`. An uploaded manifest may omit `application`; if present, it must match that application.

Keep the application paths shown in the table. The download URLs end in `.txt` so a browser reads the code instead of running it; that suffix is not part of the application filename. Each file is also reproduced below for tools reading this page through MCP.

The template manifest starts with an empty `packages` list. For each `@turnzero/` dependency, read its library entry and add its current `name` and `version` to that list. Keep the `file:lib/…` dependencies, and omit those library folders from `deploy_files` so Cloud fills them. Do not send placeholder versions or hashes.

## Files

Paths are relative to the application root—the folder containing `package.json`. `deploy_files` uses these paths without an `app/` prefix.

| Application path | Read source |
| --- | --- |
| `.gitignore` | [Text](files/gitignore.txt) |
| `AGENTS.md` | [Text](files/AGENTS.md.txt) |
| `manifest.json` | [Text](files/manifest.json.txt) |
| `package.json` | [Text](files/package.json.txt) |
| `src/main.mjs` | [Text](files/src/main.mjs.txt) |
| `src/server.mjs` | [Text](files/src/server.mjs.txt) |
| `test/hello.test.mjs` | [Text](files/test/hello.test.mjs.txt) |

## File contents

### .gitignore

```text
node_modules/
.env
.env.*
```

### AGENTS.md

```text
# This service runs on Turn Zero Cloud

The service in this folder is hosted on Turn Zero Cloud. It started from the platform's `hello-world` template: the smallest Node.js service that deploys, with a health route and one API route. This file describes the template the service started from: keep it current as the service changes.

It started with one API route, `GET /api/hello`, which answers hello world.

## Where the rules are

Turn Zero Cloud serves its rules to a coding tool through its MCP server, `turnzero-cloud`, and they change with the platform, so none is repeated here.

- Where the server is not connected, connect it first. The platform's page "Connect your AI tool" says how.
- Before you change or deploy this service, read the server's `deploy` skill.

## What you run here

- The tests: `npm test`, in this folder.
- A local run and a deploy: the `deploy` skill gives each step and the line to run, in this order: `submit_manifest`, the local run, `deploy`.
```

### manifest.json

```json
{
  "manifest_version": 1,
  "services": [],
  "health": "/health",
  "region": "usa",
  "egress": [],
  "audience": { "kind": "public" },
  "packages": []
}
```

### package.json

```json
{
  "name": "hello-world",
  "private": true,
  "type": "module",
  "scripts": {
    "start": "node src/main.mjs",
    "test": "node --test"
  }
}
```

### src/main.mjs

```javascript
// src/main.mjs, the process entry the start script runs
import { createAppServer } from './server.mjs';

const port = Number(process.env.PORT ?? 8080);
createAppServer().listen(port, () => console.log(`listening on ${port}`));
```

### src/server.mjs

```javascript
// src/server.mjs
import { createServer } from 'node:http';

export function createAppServer() {
  return createServer((request, response) => {
    // The deployed runtime's abort signal: end the response when it fires.
    request.signal?.addEventListener('abort', () => response.destroy(), { once: true });
    const send = (status, type, body) => {
      if (response.destroyed) return;
      response.writeHead(status, { 'content-type': type });
      response.end(body);
    };
    // A target that does not parse as a URL names no route, so it is answered 404 below.
    let pathname = null;
    try {
      pathname = new URL(request.url, 'http://localhost').pathname;
    } catch {}
    if (request.method === 'GET' && pathname === '/health') return send(200, 'application/json', JSON.stringify({ ok: true }));
    if (request.method === 'GET' && pathname === '/api/hello') return send(200, 'text/plain; charset=utf-8', 'hello world');
    send(404, 'application/json', JSON.stringify({ error: 'not_found' }));
  });
}
```

### test/hello.test.mjs

```javascript
// test/hello.test.mjs
import assert from 'node:assert/strict';
import { after, before, test } from 'node:test';
import { createAppServer } from '../src/server.mjs';

// The server on a port of this machine, for the file.
let server;
let base;

before(async () => {
  server = createAppServer();
  await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
  base = `http://127.0.0.1:${server.address().port}`;
});

after(async () => {
  await new Promise((resolve) => server.close(resolve));
});

test('GET /health returns 200', async () => {
  const response = await fetch(`${base}/health`);
  assert.equal(response.status, 200);
  assert.deepEqual(await response.json(), { ok: true });
});

test('GET /api/hello returns the text hello world', async () => {
  const response = await fetch(`${base}/api/hello`);
  assert.equal(response.status, 200);
  assert.equal(response.headers.get('content-type'), 'text/plain; charset=utf-8');
  assert.equal(await response.text(), 'hello world');
});
```

