npm package: @jmnuf/ao
Description: A simple typescript based full-stack api endpoint creator that is meant to somewhat follow web standards in its implementation.
Author: JM
Latest version: 0.5.3

# AO (Ancient Ones)

## Intro

WAH!

A simple kinda web-standard compliant http server that operates in a very minimal way. It comes with both a back-end file and a front-end file so you can go on and build a simple full-stack app in typescript just cause sharing types is nice despite this not being a great choice.

This isn't a very serious project so don't expect it to come with many things or for it to be properly updated.

## Usage

Imagine the following folder structure:

```
- src/
|- client/
|  |- apostle.ts
|- server/
|  |- index.ts
```

You can write some simple typescript backend code where you have to validate stuff manually cause of course this is minimal.

```ts
// src/server/index.ts
import { summonAncientOne } from "@jmnuf/ao/ancients";

const server = summonAncientOne()
  .get("/hello", async ({ request, query }) => {
    const name = query.name ?? "world";
    return `Hello, ${name}!`;
  })
  .get("/count", async function* ({ cookies }) {
    const targetCookie = cookies['x-count-target'];
    let target = 100;
    if (targetCookie.value) {
      try {
        const tc = parseInt(targetCookie.value);
        if (!Number.isNaN(tc)) target = tc;
      } catch (err) {
        console.error(err);
      }
    }
    for (let i = 0; i < 100; ++i) {
      yield i;
      // Wait for 1 second before sending next number
      await new Promise(resolve => setTimeout(resolve, 1000));
    }
  })
  .post("/data", async ({ request, error, cookies }) => {
    const userCookie = cookies['x-myapp-user-data'].value;
    // error function crashes handler with specific error code and message
    if (!userCookie) error(401, "Unauthorized: No user set");

    const userData = JSON.parse(userCookie);
    if (!userData || !userData.id) error(400, "Bad Request: Invalid user data set in cookie");

    const body = await request.json();
    if (!body || typeof body !== "object") error(400, "Bad Request: Invalid json body provided");

    console.log(body);
    return { ok: true, body };
  });

export default server.fetch;

export type KawaiiCthulu = typeof server;
```

Then import the exported type onto the client file for the sake of typesafe querying of the backend using native browser fetch.

> [!NOTE]
> if you have a route that is called "post", "get", "delete" or "put" it will not be registered in the apostle as those are considered reserved words at least for now. Sorry not sorry, pick a new name dawg

```ts
// src/client/apostle.ts
import { createApostle } from "@jmnuf/ao/earth";
import type { KawaiiCthulu } from "../server/index";

const apostle = createApostle<KawaiiCthulu>("api.my-awesome-thing.app");

// With the apostle setup above you can do api calls in the following manner...

const helloResponse = await apostle.hello.get({ query: { name: 'Mom' } });
if (helloResponse.ok) {
  console.log(helloResponse.data);
}

const countResponse = await apostle.count.get();
if (countResponse.ok) {
  for await (const value of countResponse.data) {
    console.log(`Streamed value: ${value}`);
  }
}

const postResponse = await apostle.data.post({ body: JSON.stringify({ foo: 'bar' }) });
if (postResponse.ok) {
  console.log(postResponse.data.body);
}

```

## Dev

To install dependencies:

```bash
bun install
```


This project was created using `bun init` in bun v1.2.4. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.