thaitype / nammatham

Azure Functions Framework
https://nammatham.thaitype.dev/
MIT License
65 stars 2 forks source link

Proposal for Nammatham v3 #129

Open mildronize opened 5 months ago

mildronize commented 5 months ago

Background

After Nammatham v2 entered the Alpha stage and underwent real-world usage testing, it performed well as an Azure Function library, more developer-friendly than Azure's official one.

Following that, I conducted research to see the current trends in Serverless Frameworks, which have developed significantly, offering API-agnostic and more modern approaches like Hono or itty-router.

Now, let's discuss where Nammatham stands in the market. Originally planned as an agnostic-router library for Serverless platforms to address pain points in v1, I feel the current library performs better than what I could write myself.

Therefore, I believe the plan to release the Beta version of Nammatham v2 will be expedited by eliminating all unnecessary elements for Azure Functions (and likely no plans to support other serverless providers).

In other words, Nammatham will no longer position itself as an agnostic-router library but will specialize in enhancing features for Azure Functions to be a viable option for other serverless platforms as well.

Expectations for v3 include seamless navigation in the Serverless world (though unsure of the limitations of Azure Functions, which will require PoC to determine flexibility).

So, v3 will support SSR, Bun.js/Node.js, Full ESM, and fundamentally rely on an agnostic-router library (uncertain which one) and many other features, making it a comprehensive Azure Functions framework.

Nammatham v2 Objectives:

Rationale for Nammatham v3:

Nammatham v2 aimed to establish an agnostic router library compatible with various runtimes, including Azure Functions. However, the discovery of existing solutions like Hono and itty-router, which are proven to be effective in providing API-agnostic routing with extensive middleware support, prompts a reevaluation.

The emergence of Server-Side Rendering (SSR) capabilities for edge functions (utilizing technologies such as HTMX, Alpine.js) and the availability of runtime tools for platforms like Cloudflare Workers and Vercel Functions highlight the need for Nammatham v3. This version will focus on enhancing compatibility and functionality in serverless architectures.

Implementation Design

Main Proposal:

Relevant Resources:

Key Enhancements:

Alternative Consideration

itty-router:

mildronize commented 5 months ago

Poc Type for Hono Middleware Playground

the idea behind this, when consume the timer trigger,

it need to provide path with any HTTP method

app.get(
  "/timer-trigger",
  ...createTimerTrigger({ schedule: "0 */5 * * * *" }, (c) => {
    const data = c.get("trigger");
    return c.json(data);
  })
);

But it got some problems, the Hono's handler must return http response value, it cannot support other trigger type.

Potential Solution

// PoC version for combining between v2 and v3 prposal
import { nammatham } from 'nammatham';
import { Hono } from 'hono';

const n = nammatham.create();
const func = n.func;
const app = n.app; // Create Hono Instance, like `new Hono();`

app.get('/', (c) => {
  return c.text('Hello, World!')
});

const helloFunction = func.timer({ schedule: "0 */5 * * * *" }, async (c) => {
  return c.text(`Hello, timer!`);
});

const dev = process.env.NODE_ENV === "development";

nammatham.handle({
  dev,
  app,
  triggers: {
    helloFunction
  }
});

PoC with ExtraInput & ExtraOutput Playground

app.get(
  "/copy-blob",
  ...createHttp({
    authLevel: "function",
    inputs: {
      blobInput: {
        type: 'blobStorage',
        connection: 'AzureWebJobsStorage',
        path: 'demo-input/xxx.txt',
      },
    },
    outputs: {
      blobOutput: {
        connection: 'AzureWebJobsStorage',
        path: 'demo-output/xxx-{rand-guid}.txt',
      },
    }
  }, (c) => {
    // Access with ExtraInput
    const blob = c.get("inputs").blobInput;
    // Access with ExtraOutput
    c.get('outputs').blobOutput.set(blob);
    return c.text('success');
  })
);

v3 branch: v3.x

mildronize commented 2 months ago

Interface Declaration Design

the interface design with should with Hono or other framework

Note: Add type later

// Path: nammatham.ts
import { Nammatham } from "nammatham";
import { handle } from "nammatham/hono";

const app = new Nammatham({
  default: {
    httpTrigger: {
      authLevel: "function",
    }
  },
}) as any;

// Http Method without args
app.post("/test", (c: any) => c.json("list books"));

// Http Method with args
app.get(
  "/test",
  {
    authLevel: "function",
  },
  (c: any) => c.json("list books")
);

// Another Trigger basic with few type-safe
app.storageBlob(
  {
    name: "storageBlob",
    inputs: {
      blobInput: {
        type: "blobStorage",
        connection: "AzureWebJobsStorage",
        path: "demo-input/xxx.txt",
      },
    },
  },
  (c: any) => {
    c.json(c.inputs.blobInput);
  }
);

// Another Trigger advance  with strong type-safe, and helper utility
app.storageBlob(
  ({ input, output }: any) => ({
    name: "storageBlob",
    inputs: {
      blobInput: input.blob({
        connection: "AzureWebJobsStorage",
        path: "demo-input/xxx.txt",
      }),
      blogOutput: output.blob({
        connection: 'AzureWebJobsStorage',
        path: 'demo-output/xxx-{rand-guid}.txt',
      }),
    },
  }),
  (c: any) => {
    c.json(c.inputs.blobInput);
  }
);

// using hono plugin for nammatham to converting type into Hono  
export default handle(app);

Usage Example

Method 1: Use Manually with Hono (Bun Runtime)

// Path: index.ts
// ---------------------------------------
import { Hono } from "hono";
import { nammatham } from "./nammatham";
const app = new Hono();

const routes = app.route("/", nammatham.route);

export default {
  port: 3000,
  fetch: app.fetch,
  nammatham: nammatham.metadata,
};

Method 2: Use automatically with Nammatham Plugin for Hono (Bun Runtime)

// Path: index.ts
// ---------------------------------------
import { Hono } from "hono";
import { nammatham } from "./nammatham";
import { register } from "nammatham/hono"; // Getting hono plugin from nammatham

export default register(nammatham);

Method 3: Use automatically with Nammatham Plugin for Hono (Node.js Runtime)

// Path: index.ts
// ---------------------------------------
import { Hono } from "hono";
import { serve } from '@hono/node-server'
import { nammatham } from "./nammatham";
import { register } from "nammatham/hono"; // Getting hono plugin from nammatham

const app = register(nammatham)
serve(app);

Playground

mildronize commented 2 months ago

I've seen, the Hono has suppport Azure Functions as a adapter

Note for Proxy:

mildronize commented 2 months ago

Poc type on Hono, working on https://github.com/thaitype/nammatham/pull/142

Playground

import { Hono } from 'hono';
import { logger } from 'hono/logger';
import { FunctionTrigger } from 'nammatham';

// DO NOT SET `basePath` for Hono App, Azure Functions will handle it
const app = new Hono();
app.use(logger());

const trigger = new FunctionTrigger();

app.all(
  ...trigger.http({
    route: '/SimpleHttpTrigger',
  }),
  c => {
    // Getting the function context
    const context = c.var.func;

    context.log('JavaScript HTTP trigger function processed a request.');
    context.log(`invocationid is: ${context.invocationId}`);
    context.log(`The third log message.`);

    return context.json({
      hello: 'world',
    });
  }
);