diff --git a/apps/nammatham/blog/2024-05-08-release-nammatham-doc-site.md b/apps/nammatham/blog/2019-05-29-long-blog-post.md similarity index 100% rename from apps/nammatham/blog/2024-05-08-release-nammatham-doc-site.md rename to apps/nammatham/blog/2019-05-29-long-blog-post.md diff --git a/apps/nammatham/docs/community/local-dev.md b/apps/nammatham/docs/community/local-dev.md index 74ea999..84ee90c 100644 --- a/apps/nammatham/docs/community/local-dev.md +++ b/apps/nammatham/docs/community/local-dev.md @@ -4,16 +4,17 @@ sidebar_position: 1 # Local Development -```sh -npx nx build nammatham -# or -npx nx run nammatham:build -npx nx run nammatham:dev +## Local Dev Setup -npx nx run nammatham:test:watch - - -# Publish npm with nx -pnpm --filter nammatham publish +```bash +# Install dependencies +pnpm install +# Before dev (Update workspace to local dependencies) +pnpm pre-local && pnpm install +# While dev +pnpm dev +# After dev before submitting PRs (Update workspace to actual dependencies), `pnpm install` for making sure lockfile is correct. +pnpm post-local && pnpm install +# Release package +pnpm release ``` - diff --git a/apps/nammatham/docs/intro.md b/apps/nammatham/docs/intro.md index ae0c61b..f2760e6 100644 --- a/apps/nammatham/docs/intro.md +++ b/apps/nammatham/docs/intro.md @@ -2,263 +2,128 @@ sidebar_position: 1 --- -:::warning - -This is documentation for Nammatham 1.x, which is no longer actively maintained. - -For up-to-date documentation, see the [latest version](https://github.com/thaitype/nammatham/blob/main/README.md) (2.x). - -::: - - - +# Introduction

Nammatham Logo +Type-safe Serverless Library for Azure Functions and friends

-# Introduction - -Azure Function Lightweight frameworks with DI, providing type safe function triggers and bindings - -[![Build & Test](https://github.com/thaitype/nammatham/actions/workflows/test.yml/badge.svg)](https://github.com/thaitype/nammatham/actions/workflows/test.yml?branch=v1.x) [![codecov](https://codecov.io/gh/thaitype/nammatham/branch/main/graph/badge.svg?token=Y7ZMDKFPAN)](https://codecov.io/gh/thaitype/nammatham) [![npm version](https://img.shields.io/npm/v/nammatham)](https://www.npmjs.com/package/nammatham) [![npm download](https://img.shields.io/npm/dt/nammatham)](https://www.npmjs.com/package/nammatham) -| Version | Status | Azure Functions Node.js Lib | Branch | Build Status | -| ------- | ----------- | ------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| v1.x | Maintenance | v3.x | [v1.x][v1.x] | [![Build & Test](https://github.com/thaitype/nammatham/actions/workflows/test.yml/badge.svg?branch=v1.x)](https://github.com/thaitype/nammatham/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/thaitype/nammatham/branch/v1.x/graph/badge.svg?token=Y7ZMDKFPAN)](https://codecov.io/gh/thaitype/nammatham) | -| v2.x | Alpha | v4.x | [main][main] | [![Build & Test](https://github.com/thaitype/nammatham/actions/workflows/test.yml/badge.svg?branch=main.unittest)](https://github.com/thaitype/nammatham/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/thaitype/nammatham/branch/main/graph/badge.svg?token=Y7ZMDKFPAN)](https://codecov.io/gh/thaitype/nammatham) | + [![NPM Version (with dist tag)](https://img.shields.io/npm/v/nammatham/alpha)](https://www.npmjs.com/package/nammatham) [![npm download](https://img.shields.io/npm/dt/nammatham)](https://www.npmjs.com/package/nammatham) -[v1.x]: https://github.com/thaitype/nammatham/tree/v1.x -[main]: https://github.com/thaitype/nammatham/tree/main +> 🚧 Alpha Stage Internal Use Only 🚧 +> > Please note that Nammatham v2 is currently in its Alpha stage and is intended for internal use only. As we actively develop and refine the platform, be aware that the API may undergo frequent changes. [Tracking v2 Roadmap](https://github.com/thaitype/nammatham/issues?q=is%3Aissue+is%3Aopen+label%3Av2-blocker) > > Note: [Nammatham v1](https://www.npmjs.com/package/nammatham) is currently in maintenance mode. no new features are actively being developed +You're reading v2 docs + + +| Version | Status | Azure Functions Node.js Lib | Branch | Build Status | +| ------- | ----------- | ----------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| v1.x | Maintenance | v3.x | [v1.x][v1.x] | [![Build & Test](https://github.com/thaitype/nammatham/actions/workflows/test.yml/badge.svg?branch=v1.x)](https://github.com/thaitype/nammatham/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/thaitype/nammatham/branch/v1.x/graph/badge.svg?token=Y7ZMDKFPAN)](https://codecov.io/gh/thaitype/nammatham) | +| v2.x | Alpha | v4.x | [main][main] | [![Build & Test](https://github.com/thaitype/nammatham/actions/workflows/test.yml/badge.svg?branch=main.unittest)](https://github.com/thaitype/nammatham/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/thaitype/nammatham/branch/main/graph/badge.svg?token=Y7ZMDKFPAN)](https://codecov.io/gh/thaitype/nammatham) | + +[v1.x]: https://github.com/thaitype/nammatham/tree/v1.x +[main]: https://github.com/thaitype/nammatham/tree/main + ## Description -Nammatham (นามธรรม in Thai, pronounced `/naam ma tham/`, means **abstract** in Thai) is Azure Function Nodejs Lightweight framework with Dependency Injection. Provide type safety wrapping `function.json` +Nammatham (นามธรรม in Thai, pronounced `/naam ma tham/`, means **abstract** in Thai) is Azure Function Nodejs. -## Talks -Empowering TypeScript on Azure Functions with Nammatham, Azure Open Source Day @ Microsoft Thailand, 25 Mar 2023 -[![](./img/azure-open-source-day-2023.png)](https://www.youtube.com/watch?v=n6B4-5Lt2h0) (Thai speech, subtitle will added later) -- Slides: https://docs.google.com/presentation/d/1WUIXaUxXaiixZ2bgGCfx-f4Gdrmjl4RfbwKaEfAC6t4/edit?usp=sharing +## Getting Started for Azure Functions -## Compatibility with Azure Functions -- [Azure Function NodeJs](https://github.com/Azure/azure-functions-nodejs-worker/) : v3.x (`@azure/functions`) -- [Runtime Version](https://docs.microsoft.com/azure/azure-functions/functions-versions): 4 ([Source Code](https://github.dev/Azure/azure-functions-host/tree/release/4.x)) -- Node.js Versions: 16, 18 +### Install +```bash +# Including all packages +npm install nammatham@alpha +``` -## Introduction +### Example -**Nammatham** is a framework that allows you to use Azure Functions with TypeScript and decorators. It provides pre-defined JSON binding objects and utility functions, such as `httpTrigger`, to make it easier to create Azure Functions. +You can see [examples](https://github.com/thaitype/nammatham/tree/main/examples) or follow the minimal app getting started below: -Example: +> `initNammatham.create()` is a factory function for creating Nammatham App, it's a wrapper for Azure Functions App. -```ts -import { AuthorizationLevel, BaseFunction, functionName, httpTrigger } from "nammatham"; -import { HttpRequest } from "@azure/functions"; +```typescript +import { initNammatham, expressPlugin } from 'nammatham'; -@functionName("GetUsers", httpTrigger(AuthorizationLevel.Anonymous, ["get"])) -export class UserFunction extends BaseFunction { +const n = initNammatham.create(); +const func = n.func; +const app = n.app; - public override execute(req: HttpRequest): void { - const name = req.query.name; - this.res.send(`hello get user with ${name}`); - } -} -``` +const helloFunction = func + .httpGet('hello', { + route: 'hello-world', + }) + .handler(async c => { + c.context.log('HTTP trigger function processed a request.'); + c.context.debug(`Http function processed request for url "${c.trigger.url}"`); + const name = c.trigger.query.get('name') || (await c.trigger.text()) || 'world'; + return c.text(`Hello, ${name}!`); + }); -## Features -- Simple Framework, support all functionality with Azure Functions features -- Provide type utiltiy wrapping around `function.json` -- Better project orgnization -- Bindings & Triggers Built-in type support - - Http - - Timer - - Cosmos DB, support both [v2](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-cosmosdb-v2-trigger?tabs=in-process%2Cfunctionsv2&pivots=programming-language-javascript#configuration) and [v4](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-cosmosdb-v2-trigger?tabs=in-process%2Cextensionv4&pivots=programming-language-javascript#configuration). - - Blob - - Service Bus - - If your binding type isn't in the list, please use [custom type](./concept/define-azure-function.md#custom-binding) - - You can see [All available type support](./concept/binding-type) or create a PR ;). -- Support TypeScript and [InversifyJS](https://github.com/inversify/Inversify) as the [Most Popular Inversion of Control Container and Dependency Injection Approach](https://npmtrends.com/awilix-vs-bottlejs-vs-inversify-vs-node-dependency-injection-vs-tsyringe-vs-typedi-vs-typescript-ioc) -- Build Tool Agnostic, this framework just provide the library. It can work with all TypeScript build tool e.g. tsc, esbuild, etc. - - -## Installation -You can install nammatham using npm: +app.addFunctions(helloFunction); +const dev = process.env.NODE_ENV === 'development'; +app.register(expressPlugin({ dev })); +app.start(); ``` -npm install nammatham inversify reflect-metadata --save -``` -For the [InversifyJS](https://github.com/inversify/InversifyJS#-installation), please refer the documentation for usage. - -## Starter Project - -- [Basic Nammtham Starter Template](https://github.com/mildronize/nammatham-starter) - -## Getting Started - -Full examples please, go to [examples](https://github.com/thaitype/nammatham/tree/v1.x/examples) directory - -### 1. Basic - -This is basic to use partially type support, you can follow steps below: - -1. define `startup.ts` (or any name) - ```ts - // File: src/startup.ts - import 'reflect-metadata'; - import { NammathamApp } from 'nammatham'; - import { SampleHttpFunction } from './functions/sample-http.function'; - - const builder = NammathamApp.createBuilder(__filename); - builder.addFunctions(SampleHttpFunction); - builder.build(); - - export default builder.getApp(); - ``` - -2. Write a function handler, extend with `BaseFunction` we will auto inject Azure Function's Context - ```ts - // src/functions/sample-http.function.ts - import { AuthorizationLevel, BaseFunction, functionName, httpTrigger } from 'nammatham'; - import { HttpRequest } from '@azure/functions'; - - @functionName('SampleHttp', httpTrigger(AuthorizationLevel.Anonymous, ['get'])) - export class SampleHttpFunction extends BaseFunction { - public override execute(req: HttpRequest): void { - const name = req.query.name; - const message = `hello get user with ${name}`; - this.context.log(message); - this.res.send(message); - } - } - ``` -3. Add Azure Functions files at root - - `host.json` - - `local.settings.json` -4. Run `ts-node` to generate all Azure Functions - ```ts - export nammatham_env=build; ts-node src/startup.ts && tsc - ``` -5. Start Azure Functions - ``` - func start - ``` - -### 2. Handle `function.json` config by yourself - -This method will support full support type when bindings config is set, for example below: - -you can define your own `function.json` in Typescript object (as you can see the variable `bindings`), this will binding type into `this.context.bindings`. - -```ts -import { BaseFunction, binding, functionName } from 'nammatham'; - -const bindings = [ - binding.httpTrigger({ name: 'req' as const }), // make string to literal type - binding.http({ name: 'res' as const }), // make string to literal type -] as const; - -@functionName('GetUser', ...bindings) -export class UserFunction extends BaseFunction { - - public override execute() { - const { req } = this.context.bindings; - // ^---- `req` will be type HttpRequest - const name = req.query.name; - this.context.res = { - body: `hello get user with ${name}}`, - }; +Then edit `package.json` like this; + +```json +{ + "main": "dist/src/main.js", + "scripts": { + "dev": "cross-env NODE_ENV=development tsx watch src/main.ts", + "start": "tsc && func start" } } ``` -### 3. Add Services - +Run Dev Server on locally, (For dev server use `tsx watch` for reloading run dev server using `express` ) -```ts -// src/startup.ts -import 'reflect-metadata'; -import { NammathamApp } from 'nammatham'; -import { UserService } from './services/user.services'; -import { UserFunction } from './functions/user.function'; +``` +npm run dev +``` -const builder = NammathamApp.createBuilder(__filename); -builder.addFunctions(UserFunction); -builder.configureServices(services => { - services.addSingleton(Service); - // services.addScoped(Service); - // services.addTransient(Service); -}); -builder.build(); +Run Azure Functions on locally (Using Official Azure Functions Node.js) -export default builder.getApp(); +``` +npm start ``` -define a function handler -```ts -import { AuthorizationLevel, BaseFunction, functionName, httpTrigger } from 'nammatham'; -import { HttpRequest } from '@azure/functions'; -import { UserService } from '../services/user.service'; -import { inject } from 'inversify'; +## Nammatham Packages -@functionName('GetUsers', httpTrigger(AuthorizationLevel.Anonymous, ['get'])) -export class UserFunction extends BaseFunction { +- [core][@nammatham/core], Nammatham Core package for initializing Nammatham App - constructor(@inject(UserService) private userService: UserService){ - super(); - } - - public getUsers(): void { - const { req } = this.context.bindings; - const name = req.query.name; - const message = `hello get user with ${name}, service data: ${this.userService.getData()}`; - this.context.log(message); - this.res.send(message); - } -} -``` - -service: - -```ts -import { injectable } from 'inversify'; +### Available Adatpers -@injectable() -export class UserService { - constructor() {} +- [azure-functions][@nammatham/azure-functions], Azure Functions Adapter for Nammatham, internally, Azure Functions in local dev mode is dependend on Express.js. - public getData() { - return `Hey I'm service`; - } -} -``` +### Available Plugins -## Using Inversify API for Binding Services +- [express][@nammatham/express], Express Plugin for run server. Nammatham itself doesn't contain any server, enabling this plugin to provide better DX than the original server e.g. Azure Functions Runtime +- [trpc-azure-functions][@nammatham/trpc-azure-functions], provide [tRPC](https://trpc.io/) Plugin for Azure Functions, inclduing [express][@nammatham/express] server for local testing. -In some cases, if you want to binding services with [Inversify Container](https://github.com/inversify/InversifyJS/blob/master/wiki/container_api.md) by yourself. -In the startup file, you can simply get the Container from `builder.container` as shown in the example below: +[@nammatham/core]: https://github.com/thaitype/nammatham/tree/main/packages/core +[@nammatham/azure-functions]: https://github.com/thaitype/nammatham/tree/mainpackages/azure-functions +[@nammatham/express]: https://github.com/thaitype/nammatham/tree/mainpackages/express +[@nammatham/trpc-azure-functions]: https://github.com/thaitype/nammatham/tree/mainpackages/trpc-azure-functions -```ts -// src/startup.ts -import 'reflect-metadata'; -import { NammathamApp } from 'nammatham'; -import { UserService } from './services/user.services'; -import { UserFunction } from './functions/user.function'; -const builder = NammathamApp.createBuilder(__filename); -builder.addFunctions(UserFunction); -// Using Inversify Container API -builder.container.bind(Service).toSelf(); +## Talks +Empowering TypeScript on Azure Functions with Nammatham, Azure Open Source Day @ Microsoft Thailand, 25 Mar 2023 +[![](./img/azure-open-source-day-2023.png)](https://www.youtube.com/watch?v=n6B4-5Lt2h0) (Thai speech, subtitle will added later) +- Slides: https://docs.google.com/presentation/d/1WUIXaUxXaiixZ2bgGCfx-f4Gdrmjl4RfbwKaEfAC6t4/edit?usp=sharing -builder.build(); -export default builder.getApp(); -``` + ## Inspiration - [Azure Functions .NET](https://learn.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-csharp?tabs=azure-cli%2Cin-process) diff --git a/apps/nammatham/docusaurus.config.ts b/apps/nammatham/docusaurus.config.ts index 7b6e4b4..0cafeec 100644 --- a/apps/nammatham/docusaurus.config.ts +++ b/apps/nammatham/docusaurus.config.ts @@ -60,8 +60,8 @@ const config: Config = { navbar: { title: 'Nammatham', logo: { - alt: 'My Site Logo', - src: 'img/logo.svg', + alt: 'Nammatham Logo', + src: 'https://i.ibb.co/QmTh7x4/Nammatham-Logo-v2.png', }, items: [ { @@ -71,6 +71,12 @@ const config: Config = { label: 'Docs', }, { to: '/blog', label: 'Blog', position: 'left' }, + { + type: 'docsVersionDropdown', + // dropdownItemsAfter: [{to: '/versions', label: 'All versions'}], + // dropdownActiveClassDisabled: true, + position: 'right', + }, { href: 'https://github.com/thaitype/nammatham', label: 'GitHub', diff --git a/apps/nammatham/src/css/custom.css b/apps/nammatham/src/css/custom.css index 2bc6a4c..721b199 100644 --- a/apps/nammatham/src/css/custom.css +++ b/apps/nammatham/src/css/custom.css @@ -6,25 +6,39 @@ /* You can override the default Infima variables here. */ :root { - --ifm-color-primary: #2e8555; + /* --ifm-color-primary: #2e8555; --ifm-color-primary-dark: #29784c; --ifm-color-primary-darker: #277148; --ifm-color-primary-darkest: #205d3b; --ifm-color-primary-light: #33925d; --ifm-color-primary-lighter: #359962; - --ifm-color-primary-lightest: #3cad6e; + --ifm-color-primary-lightest: #3cad6e; */ + --ifm-color-primary: #671b0e; + --ifm-color-primary-dark: #5d180d; + --ifm-color-primary-darker: #58170c; + --ifm-color-primary-darkest: #48130a; + --ifm-color-primary-light: #711e0f; + --ifm-color-primary-lighter: #761f10; + --ifm-color-primary-lightest: #862312; --ifm-code-font-size: 95%; --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); } /* For readability concerns, you should choose a lighter palette in dark mode. */ [data-theme='dark'] { - --ifm-color-primary: #25c2a0; + /* --ifm-color-primary: #25c2a0; --ifm-color-primary-dark: #21af90; --ifm-color-primary-darker: #1fa588; --ifm-color-primary-darkest: #1a8870; --ifm-color-primary-light: #29d5b0; --ifm-color-primary-lighter: #32d8b4; - --ifm-color-primary-lightest: #4fddbf; + --ifm-color-primary-lightest: #4fddbf; */ + --ifm-color-primary: #efd0cc; + --ifm-color-primary-dark: #e4b1aa; + --ifm-color-primary-darker: #dfa199; + --ifm-color-primary-darkest: #cf7367; + --ifm-color-primary-light: #faefee; + --ifm-color-primary-lighter: #ffffff; + --ifm-color-primary-lightest: #ffffff; --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); } diff --git a/apps/nammatham/src/pages/index.tsx b/apps/nammatham/src/pages/index.tsx index 0ae0f2b..f7ec831 100644 --- a/apps/nammatham/src/pages/index.tsx +++ b/apps/nammatham/src/pages/index.tsx @@ -12,16 +12,18 @@ function HomepageHeader() { return (
+ Nammatham Logo {siteConfig.title} +

{siteConfig.tagline}

- Docusaurus Tutorial - 5min ⏱️ + Nammatham Tutorial - 5min ⏱️
diff --git a/apps/nammatham/versioned_docs/version-1.x/community/_category_.json b/apps/nammatham/versioned_docs/version-1.x/community/_category_.json new file mode 100644 index 0000000..d672bab --- /dev/null +++ b/apps/nammatham/versioned_docs/version-1.x/community/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "Community", + "position": 3, + "link": { + "type": "generated-index" + } +} diff --git a/apps/nammatham/versioned_docs/version-1.x/community/local-dev.md b/apps/nammatham/versioned_docs/version-1.x/community/local-dev.md new file mode 100644 index 0000000..74ea999 --- /dev/null +++ b/apps/nammatham/versioned_docs/version-1.x/community/local-dev.md @@ -0,0 +1,19 @@ +--- +sidebar_position: 1 +--- + +# Local Development + +```sh +npx nx build nammatham +# or +npx nx run nammatham:build +npx nx run nammatham:dev + +npx nx run nammatham:test:watch + + +# Publish npm with nx +pnpm --filter nammatham publish +``` + diff --git a/apps/nammatham/versioned_docs/version-1.x/concept/_category_.json b/apps/nammatham/versioned_docs/version-1.x/concept/_category_.json new file mode 100644 index 0000000..4bcf10f --- /dev/null +++ b/apps/nammatham/versioned_docs/version-1.x/concept/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Concepts", + "position": 2, + "link": { + "type": "generated-index", + "description": "Nammatham Concepts." + } +} diff --git a/apps/nammatham/versioned_docs/version-1.x/concept/binding-type.md b/apps/nammatham/versioned_docs/version-1.x/concept/binding-type.md new file mode 100644 index 0000000..fa900a8 --- /dev/null +++ b/apps/nammatham/versioned_docs/version-1.x/concept/binding-type.md @@ -0,0 +1,130 @@ +--- +sidebar_position: 3 +--- +# All Built-in Binding Type + +You can see all built-in binding type in [test case](https://github.com/thaitype/nammatham/blob/v1.x/packages/core/src/test-usecases/all-bindings/fixtures/functions/all-bindings.function.ts) as shown below: + +```ts +import { BaseFunction, binding, functionName } from '../../../../main'; +import { responseHelper } from '../../../response-helper'; +import { HttpRequest, HttpResponse, Timer } from '@azure/functions'; + +const bindings = [ + binding.httpTrigger({ name: 'req' as const }), // make string to literal type + binding.http({ name: 'res' as const }), // make string to literal type + binding.http_withReturn(), + binding.timerTrigger({ name: 'timer' as const, schedule: '*' }), // make string to literal type + binding.cosmosDBTrigger_v2({ + name: 'document_trigger_v2' as const, + collectionName: '', + connectionStringSetting: '', + databaseName: '', + }), + binding.cosmosDBTrigger_v4({ + name: 'document_trigger_v4' as const, + connection: '', + containerName: '', + databaseName: '', + }), + binding.cosmosDBTrigger({ + name: 'document_trigger_default' as const, + connection: '', + containerName: '', + databaseName: '', + }), + binding.cosmosDB_output_v2({ + name: 'document_output_v2' as const, + collectionName: '', + connectionStringSetting: '', + createIfNotExists: true, + databaseName: '', + }), + binding.cosmosDB_output_v4({ + name: 'document_output_v4' as const, + createIfNotExists: true, + databaseName: '', + connection: '', + containerName: '', + }), + binding.cosmosDB_output({ + name: 'document_output_default' as const, + createIfNotExists: true, + databaseName: '', + connection: '', + containerName: '', + }), + binding.cosmosDB_input_v2({ + name: 'document_input_v2' as const, + collectionName: '', + connectionStringSetting: '', + databaseName: '', + id: '', + partitionKey: '', + sqlQuery: '', + }), + binding.cosmosDB_input_v4({ + name: 'document_input_v4' as const, + databaseName: '', + id: '', + partitionKey: '', + sqlQuery: '', + connection: '', + containerName: '', + }), + binding.cosmosDB_input({ + name: 'document_input_default' as const, + databaseName: '', + id: '', + partitionKey: '', + sqlQuery: '', + connection: '', + containerName: '', + }), + binding.blobTrigger({ + connection: '', + name: 'blob_trigger' as const, + path: '', + }), + binding.blob_input({ + connection: '', + name: 'blob_input' as const, + path: '', + }), + binding.blob_output({ + connection: '', + name: 'blob_output' as const, + path: '', + }), +] as const; + +const customBindings = binding.custom({ name: 'req' as const, type: 'custom', direction: 'in' }); + +@functionName('AllBindingsFunction', ...bindings, customBindings) +export class AllBindingsFunction extends BaseFunction { + public override execute() { + const req: HttpRequest = this.bindings.req; + const res: HttpResponse = this.bindings.res; + const timer: Timer = this.bindings.timer; + + const document_output_v2: any = this.bindings.document_output_v2; + const document_output_v4: any = this.bindings.document_output_v4; + const document_output_default: any = this.bindings.document_output_default; + + const document_input_v2: any = this.bindings.document_input_v2; + const document_input_v4: any = this.bindings.document_input_v4; + const document_input_default: any = this.bindings.document_input_default; + + const document_trigger_v2: any = this.bindings.document_trigger_v2; + const document_trigger_v4: any = this.bindings.document_trigger_v4; + const document_trigger_default: any = this.bindings.document_trigger_default; + + const blob_trigger: any = this.bindings.blob_trigger; + const blob_input: any = this.bindings.blob_input; + const blob_output: any = this.bindings.blob_output; + + const { name } = this.req.query; + this.res.send(responseHelper(name)); + } +} +``` diff --git a/apps/nammatham/versioned_docs/version-1.x/concept/define-azure-function.md b/apps/nammatham/versioned_docs/version-1.x/concept/define-azure-function.md new file mode 100644 index 0000000..c94f4d5 --- /dev/null +++ b/apps/nammatham/versioned_docs/version-1.x/concept/define-azure-function.md @@ -0,0 +1,170 @@ +--- +sidebar_position: 2 +--- + +# Define Azure Functions + +In order to define Azure Functions, simply do the following steps: +1. Define a `Function` Class which extend class `BaseFunction` +2. Using `@functionName` decorator at the class to define our Azure Functions + +## @functionName decorator + +`@functionName` decorator gets 2 paramters: +1. Function Name +2. Azure Function Binding, which is accept array of `BaseFunctionBinding` Object or `BaseFunctionBinding` Object. This will be convert to [function.json](https://learn.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-node?tabs=azure-cli%2Cbrowser#functionjson) (The configuration file of Azure Function) + +As you can see in the function defintion: + +```ts +function functionName( + name: string, + ...bindings: Array< + BaseFunctionBinding | + [BaseFunctionBinding, BaseFunctionBinding] + > +): HandlerDecorator; +``` + + +Basically, this framework doesn't strict with some trigger, any trigger binding also support in just simply define JSON binding Object. + +The example below show how to use Azure Functions with HTTP Trigger, This framework also provides a utility function like `httpTrigger` which is pre-defined JSON Binding Object in `function.json` file. + +```ts +import { AuthorizationLevel, BaseFunction, functionName, httpTrigger } from "nammatham"; +import { HttpRequest } from "@azure/functions"; + +@functionName("GetUsers", httpTrigger(AuthorizationLevel.Anonymous, ["get"])) +export class UserFunction extends BaseFunction { + + public override execute(req: HttpRequest): void { + const name = req.query.name; + this.res.send(`hello get user with ${name}`); + } +} +``` + +As you can see, we use `httpTrigger` function like this: + +```ts +httpTrigger(AuthorizationLevel.Anonymous, ["get"]) +``` + +the `httpTrigger` function will return simple JSON Binding Object like this: + +```json +[ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "req", + "methods": [ + "get" + ] + }, + { + "type": "http", + "direction": "out", + "name": "res" + } +] +``` + + +## Using Built-in Type Supported in Binding + +In case your want to define your [function.json](https://learn.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-node?tabs=azure-cli%2Cbrowser#functionjson) by yourself, you just simply create array of `FunctionBinding` type and passing into the `@functionName` decorator, using spread operator `...` + +```ts +import { BaseFunction, Binding, functionName } from 'nammatham'; + +const bindings = [ + binding.httpTrigger({ name: 'req' as const }), // make string to literal type + binding.http({ name: 'res' as const }), // make string to literal type +] as const; + +@functionName('GetUser', ...bindings) +export class UserFunction extends BaseFunction { + + public override execute() { + const { req } = this.context.bindings; + const name = req.query.name; + this.context.res = { + body: `hello get user with ${name}}`, + }; + } +} +``` + +Providing `typeof bindings` into base class `BaseFunction`, it will auto binding type into `this.context.bindings` object. For example: + +```ts +const { req } = this.context.bindings; + // ^------ + type TypedContext = { + req: HttpRequest; + res: HttpResponse; + } +``` + +However, the `@azure/functions` doesn't provide any type binding based on `function.json`. + +In case you want custom type for binding, please read the section [Custom Binding](./define-azure-function.md#custom-binding) + +## Custom Binding + +In `@functionName` decorator support any JSON Binding Object that you can self-define it. + +For example, if you want to use `customTrigger`, you can simply do like this: + +> Note: `customTrigger` type is not available in Azure Functions, just show the example of the custom type + +```ts +import { BaseFunction, Binding, functionName } from 'nammatham'; + +const bindings = [ + binding.httpTrigger({ name: 'req' as const }), // make string to literal type + binding.http({ name: 'res' as const }), // make string to literal type +] as const; + +// the type should be supported by Azure Functions runtime +const customBindings = binding.custom({ name: 'custom' as const, direction: 'in', type: 'customTrigger' }); + +@functionName('CustomType', ...bindings, customBindings) +export class CustomTypeFunction extends BaseFunction { + public override execute() { + const { req, custom } = this.context.bindings; + console.log(`Do something with custom binding ${custom}`); + const name = req.query.name; + + this.context.res = { + body: `hello get user with ${name}}`, + }; + } +} +``` + +Or can use simply object like this: + +```ts +const bindings = [ + { + name: 'req', + type: 'httpTrigger', + direction: 'in', + }, + { + name: 'res', + direction: 'out', + type: 'http', + }, +] as const; + +const customBindings = { + name: 'custom', + direction: 'in', + type: 'customTrigger', +} as const; +``` diff --git a/apps/nammatham/versioned_docs/version-1.x/concept/how-it-work.md b/apps/nammatham/versioned_docs/version-1.x/concept/how-it-work.md new file mode 100644 index 0000000..7cabde7 --- /dev/null +++ b/apps/nammatham/versioned_docs/version-1.x/concept/how-it-work.md @@ -0,0 +1,269 @@ +--- +sidebar_position: 4 +--- +# How does Nammatham works? + +Nammatham is a framework for building Azure Functions using TypeScript. It works by running two steps: + +1. The `bootstrap` phase generates two files for each function: `index.js` and `function.json`, using following command + ```ts + const builder = NammathamApp.createBuilder(__filename); + builder.build(); + export default builder.getApp(); // Default export for each function, will use in `functionBootstrap` phase + ``` +2. The `functionBootstrap` phase is called by `index.js` from the previous step, and it loads all dependencies and returns the actual method defined in the controller, using following command + ```ts + import app from '../src/startup'; // from the `bootstrap` phase + // Start execute the function + app.run(/** ... **/); + ``` + +Both phases use the `attachControllers()` function to inject the dependencies defined in the decorator `functionName` decorator. In production, only the `functionBootstrap` phase is run to execute each function endpoint. This allows for better performance and lower overhead. + +Note: We use the word "Controller" to same meaning as "Function Class", to avoid confusing in the library development. + +## 1. How does it work? + +it will autogenerate, 2 files per function + +1. function.json + ```json + { + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "req", + "methods": [ + "get" + ] + }, + { + "type": "http", + "direction": "out", + "name": "res" + } + ], + "scriptFile": "../dist/src/functions/user.function.js" + } + ``` + +2. index.ts + ```ts + import 'reflect-metadata'; + import { AzureFunction, Context } from '@azure/functions'; + import { UserController } from '../src/functions/user.function'; + import app from '../src/startup'; + + const GetUsers: AzureFunction = async function ( + context: Context, + ...args: any[] + ): Promise { + return app.run({ + classTarget: UserController, + azureFunctionParams: [context, ...args] + }); + }; + + export default GetUsers; + ``` + +## 2. Understand Azure Function Runtime + +[Azure Functions Runtime](https://github.com/Azure/azure-functions-host) is a system that provides a hosting platform for Azure Functions services. In order to use the runtime, developers must create a function that takes in a specific argument, known as the `AzureFunction` type below: + +```ts +import { AzureFunction } from '@azure/functions'; // 3.5.0 +type AzureFunction = (context: Context, ...args: any[]) => Promise | void +``` + +This argument is made up of two parts: +- the **first argument**, which is injected with a `Context` object. +- the **rest of the arguments**, which are injected with any other objects specified in the `function.json` file. + +For example, if you set up `function.json` like this: + +```json +{ + "bindings": [ + { + "type": "httpTrigger", + "direction": "in", + "name": "req" + }, + { + "type": "http", + "direction": "out", + "name": "res" + } + ] +} +``` + +For example, if the `function.json` file includes bindings for `httpTrigger` and `http`, the runtime will inject the matched objects, `HttpRequest` and `HttpResponse` respectively, into the function's arguments. This allows developers to use these objects within the function for handling incoming requests and sending responses, for example: + +```ts +import { AzureFunction, HttpRequest, HttpResponse } from '@azure/functions'; // 3.5.0 +const httpTrigger: AzureFunction = async function(context: Context, req: HttpRequest, res: HttpResponse) { + // do something +} +``` + +However, we cannot use `HttpResponse` from the function arguments, so we usually provide only `HttpRequest`: + +```ts +const httpTrigger = function(context: Context, req: HttpRequest){} +``` + +Moreover, the Azure Functions Runtime will inject the same object in `Context.bindings`, the key name will match with prop `name` in `function.json`. so, it should be a type: + +```ts +type MyContextBindings = { + req: HttpRequest; + res: HttpResponse; +} +``` + +And Azure Functions Runtime will inject `req` as `HttpRequest` and `res` as `Record` in the `Context` object, we usually use both objects in the `Context`, we usually use `Context.res` for giving response: + +```ts +this.context.res = { + status: 200, + body: `hello world`, +}; +``` + +However, the Azure Functions library (@azure/functions@3.5.0) does not provide a specific type for the `Context.bindings` object. This can make it difficult for developers to correctly type the objects being injected into the function. + + + +```ts +@functionName('WithTypeUtility', ...functionBinding1) +export class WithTypeUtilityFunction extends BaseFunction { + + public override execute() { + const { req, res } = this.context.bindings; + const name = req.query.name; + this.context.res = { + body: `hello WithTypeUtility with ${name}`, + }; + } +} +``` + +## 3. Resolve dependency in the container at startup time + +.... + + +## 4. Why a single class per azure function + +Previous design, we're required to create a function bindings config (same as `function.json`) before starting declare the class. +Because we need a type of function bindings and using `GetBindings` type to extract type from it. + +This can make inconvenience we're using this library in a couple of reasons: +1. When you have to declare more than 1 function in a single class, the function bindings config will be seperated from the method (`@functionName` decorator) you defined the azure functions. +2. We need to use `GetBindings` type to extract type from `Context.bindings` to passing into the method arguments. + +As you can see the example below: + +```ts +// nammatham@0.4.0-alpha +import { BaseController, controller, functionName, GetBindings, httpTriggerBinding, HttpBinding } from 'nammatham'; + +const functionBinding1 = [ + { + name: 'req', + type: 'httpTrigger', + direction: 'in', + } as httpTriggerBinding<'req'>, + { + name: 'res', + direction: 'out', + type: 'http', + } as HttpBinding<'res'>, +]; + +const functionBinding2 = [ + { + name: 'req', + type: 'httpTrigger', + direction: 'in', + } as httpTriggerBinding<'req'>, + { + name: 'res', + direction: 'out', + type: 'http', + } as HttpBinding<'res'>, +]; + +@controller() +export class MyController extends BaseController { + @functionName('function1', ...functionBinding1) + public function1({ req }: GetBindings): void { + const name = req.query.name; + this.context.res = { + body: `hello function1 with ${name}`, + }; + } + + @functionName('function2', ...functionBinding2) + public function2({ req }: GetBindings): void { + const name = req.query.name; + this.context.res = { + body: `hello function1 with ${name}`, + }; + } +} +``` + +From the above reasons, we want go get type when `Context` object already injected by Azure Functions runtime. +So, we decided to design 1 azure function per a single class which extends `BaseFunction`, and allow only implement azure function handler inside `execute` method only. + +The `BaseFunction` will accept type of function bindings config when creating this object, the `Context` object will know the exact type, by using `this.context`. This can make the azure function class below cleaner and simple. + +```ts +// nammatham version 0.5.0-alpha or above +import { BaseFunction, binding, functionName } from 'nammatham'; + +const functionBinding1 = [ + { + name: 'req', + type: 'httpTrigger', + direction: 'in', + } as binding.HttpTriggerBinding, + { + name: 'res', + direction: 'out', + type: 'http', + } as binding.HttpBinding, +] as const; + + +@functionName('WithTypeUtility', ...functionBinding1) +export class WithTypeUtilityFunction extends BaseFunction { + + public override execute() { + const { req, res } = this.context.bindings; + const name = req.query.name; + this.context.res = { + body: `hello WithTypeUtility with ${name}`, + }; + } +} +``` + +Moreover, we can use `Binding` object which a helper to create a function binding with confident type, as show below: + +```ts +import { binding} from 'nammatham'; + +const functionBinding1 = [ + binding.httpTrigger({ name: 'req' as const }), // make string to literal type + binding.http({ name: 'res' as const }), // make string to literal type +] as const; +``` + +This is the same value as above declaration, but more fluent api. + diff --git a/apps/nammatham/versioned_docs/version-1.x/concept/motivation.md b/apps/nammatham/versioned_docs/version-1.x/concept/motivation.md new file mode 100644 index 0000000..5c81965 --- /dev/null +++ b/apps/nammatham/versioned_docs/version-1.x/concept/motivation.md @@ -0,0 +1,17 @@ +--- +sidebar_position: 1 +--- +# Motivation + +This framework aims to improve the development experience for Azure Functions using Node.js by providing a clear project structure, built-in Azure Function Configuration with the code, and built-in Dependency Injection. + +.NET is a first-class supported in Azure Function which ... (Write Later) + +We heavily get inspired from Azure Functions .NET version which provide clearly project strucutre, built-in Azure Function Configuration with the Code, and also provide built-in Dependency Injection. + +- **Ugly Project Structure** -The Azure Functions Node.js library only provides basic tools to connect with the Azure Function Runtime. All function endpoints are located in the root of the project and only accept one export in the `index.js` file, which is the Azure Function Runtime will inject runtime object for that such as `Context`. Other code such as services, constants, and middleware must be located at the same level as the function endpoints. +- **Separate JS Code and the Function configuration** - The separation of JS code and function configuration makes it harder to understand how the function app works. In contrast, the [.NET version also provide configuration inline of the C# Code](https://learn.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-csharp?tabs=azure-cli%2Cin-process#httpexamplecs), no type support when binding input and output from `function.json` + - To create an Azure Function endpoint, two files are required: + 1. [index.js](https://learn.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-node?tabs=azure-cli%2Cbrowser#indexjs) which must have only one export. + 2. [function.json](https://learn.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-node?tabs=azure-cli%2Cbrowser#functionjson) which is a plain JSON file with no autocomplete or easy-to-use configuration, requiring the user to open the documentation to configure it. +- **No Dependecy Injection** - Azure Functions Node.js does not provide any built-in Dependency Injection tool, unlike the [.NET Azure Function provides built-in Dependency Injection](https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection) diff --git a/apps/nammatham/versioned_docs/version-1.x/img/azure-open-source-day-2023.png b/apps/nammatham/versioned_docs/version-1.x/img/azure-open-source-day-2023.png new file mode 100644 index 0000000..ad5f688 Binary files /dev/null and b/apps/nammatham/versioned_docs/version-1.x/img/azure-open-source-day-2023.png differ diff --git a/apps/nammatham/versioned_docs/version-1.x/intro.md b/apps/nammatham/versioned_docs/version-1.x/intro.md new file mode 100644 index 0000000..11acf61 --- /dev/null +++ b/apps/nammatham/versioned_docs/version-1.x/intro.md @@ -0,0 +1,269 @@ +--- +sidebar_position: 1 +--- + +# Introduction + +:::warning + +This is documentation for Nammatham 1.x, which is no longer actively maintained. For up-to-date documentation, see the [latest version](https://github.com/thaitype/nammatham/blob/main/README.md) (2.x). + +::: + + + +

+ Nammatham Logo +

+ +Azure Function Lightweight frameworks with DI, providing type safe function triggers and bindings + +[![Build & Test](https://github.com/thaitype/nammatham/actions/workflows/test.yml/badge.svg)](https://github.com/thaitype/nammatham/actions/workflows/test.yml?branch=v1.x) [![codecov](https://codecov.io/gh/thaitype/nammatham/branch/main/graph/badge.svg?token=Y7ZMDKFPAN)](https://codecov.io/gh/thaitype/nammatham) [![npm version](https://img.shields.io/npm/v/nammatham)](https://www.npmjs.com/package/nammatham) [![npm download](https://img.shields.io/npm/dt/nammatham)](https://www.npmjs.com/package/nammatham) + + +| Version | Status | Azure Functions Node.js Lib | Branch | Build Status | +| ------- | ----------- | ------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| v1.x | Maintenance | v3.x | [v1.x][v1.x] | [![Build & Test](https://github.com/thaitype/nammatham/actions/workflows/test.yml/badge.svg?branch=v1.x)](https://github.com/thaitype/nammatham/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/thaitype/nammatham/branch/v1.x/graph/badge.svg?token=Y7ZMDKFPAN)](https://codecov.io/gh/thaitype/nammatham) | +| v2.x | Alpha | v4.x | [main][main] | [![Build & Test](https://github.com/thaitype/nammatham/actions/workflows/test.yml/badge.svg?branch=main.unittest)](https://github.com/thaitype/nammatham/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/thaitype/nammatham/branch/main/graph/badge.svg?token=Y7ZMDKFPAN)](https://codecov.io/gh/thaitype/nammatham) | + +[v1.x]: https://github.com/thaitype/nammatham/tree/v1.x +[main]: https://github.com/thaitype/nammatham/tree/main + +> Please note that Nammatham v2 is currently in its Alpha stage and is intended for internal use only. As we actively develop and refine the platform, be aware that the API may undergo frequent changes. [Tracking v2 Roadmap](https://github.com/thaitype/nammatham/issues?q=is%3Aissue+is%3Aopen+label%3Av2-blocker) +> +> Note: [Nammatham v1](https://www.npmjs.com/package/nammatham) is currently in maintenance mode. no new features are actively being developed + +## Description +Nammatham (นามธรรม in Thai, pronounced `/naam ma tham/`, means **abstract** in Thai) is Azure Function Nodejs Lightweight framework with Dependency Injection. Provide type safety wrapping `function.json` + +## Talks +Empowering TypeScript on Azure Functions with Nammatham, Azure Open Source Day @ Microsoft Thailand, 25 Mar 2023 +[![](./img/azure-open-source-day-2023.png)](https://www.youtube.com/watch?v=n6B4-5Lt2h0) (Thai speech, subtitle will added later) +- Slides: https://docs.google.com/presentation/d/1WUIXaUxXaiixZ2bgGCfx-f4Gdrmjl4RfbwKaEfAC6t4/edit?usp=sharing + +## Compatibility with Azure Functions +- [Azure Function NodeJs](https://github.com/Azure/azure-functions-nodejs-worker/) : v3.x (`@azure/functions`) +- [Runtime Version](https://docs.microsoft.com/azure/azure-functions/functions-versions): 4 ([Source Code](https://github.dev/Azure/azure-functions-host/tree/release/4.x)) +- Node.js Versions: 16, 18 + + +## Introduction + +**Nammatham** is a framework that allows you to use Azure Functions with TypeScript and decorators. It provides pre-defined JSON binding objects and utility functions, such as `httpTrigger`, to make it easier to create Azure Functions. + +Example: + +```ts +import { AuthorizationLevel, BaseFunction, functionName, httpTrigger } from "nammatham"; +import { HttpRequest } from "@azure/functions"; + +@functionName("GetUsers", httpTrigger(AuthorizationLevel.Anonymous, ["get"])) +export class UserFunction extends BaseFunction { + + public override execute(req: HttpRequest): void { + const name = req.query.name; + this.res.send(`hello get user with ${name}`); + } +} +``` + +## Features +- Simple Framework, support all functionality with Azure Functions features +- Provide type utiltiy wrapping around `function.json` +- Better project orgnization +- Bindings & Triggers Built-in type support + - Http + - Timer + - Cosmos DB, support both [v2](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-cosmosdb-v2-trigger?tabs=in-process%2Cfunctionsv2&pivots=programming-language-javascript#configuration) and [v4](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-cosmosdb-v2-trigger?tabs=in-process%2Cextensionv4&pivots=programming-language-javascript#configuration). + - Blob + - Service Bus + - If your binding type isn't in the list, please use [custom type](./concept/define-azure-function.md#custom-binding) + - You can see [All available type support](./concept/binding-type) or create a PR ;). +- Support TypeScript and [InversifyJS](https://github.com/inversify/Inversify) as the [Most Popular Inversion of Control Container and Dependency Injection Approach](https://npmtrends.com/awilix-vs-bottlejs-vs-inversify-vs-node-dependency-injection-vs-tsyringe-vs-typedi-vs-typescript-ioc) +- Build Tool Agnostic, this framework just provide the library. It can work with all TypeScript build tool e.g. tsc, esbuild, etc. + + +## Installation +You can install nammatham using npm: + +``` +npm install nammatham inversify reflect-metadata --save +``` + +For the [InversifyJS](https://github.com/inversify/InversifyJS#-installation), please refer the documentation for usage. + +## Starter Project + +- [Basic Nammtham Starter Template](https://github.com/mildronize/nammatham-starter) + +## Getting Started + +Full examples please, go to [examples](https://github.com/thaitype/nammatham/tree/v1.x/examples) directory + +### 1. Basic + +This is basic to use partially type support, you can follow steps below: + +1. define `startup.ts` (or any name) + ```ts + // File: src/startup.ts + import 'reflect-metadata'; + import { NammathamApp } from 'nammatham'; + import { SampleHttpFunction } from './functions/sample-http.function'; + + const builder = NammathamApp.createBuilder(__filename); + builder.addFunctions(SampleHttpFunction); + builder.build(); + + export default builder.getApp(); + ``` + +2. Write a function handler, extend with `BaseFunction` we will auto inject Azure Function's Context + ```ts + // src/functions/sample-http.function.ts + import { AuthorizationLevel, BaseFunction, functionName, httpTrigger } from 'nammatham'; + import { HttpRequest } from '@azure/functions'; + + @functionName('SampleHttp', httpTrigger(AuthorizationLevel.Anonymous, ['get'])) + export class SampleHttpFunction extends BaseFunction { + public override execute(req: HttpRequest): void { + const name = req.query.name; + const message = `hello get user with ${name}`; + this.context.log(message); + this.res.send(message); + } + } + ``` +3. Add Azure Functions files at root + - `host.json` + - `local.settings.json` +4. Run `ts-node` to generate all Azure Functions + ```ts + export nammatham_env=build; ts-node src/startup.ts && tsc + ``` +5. Start Azure Functions + ``` + func start + ``` + +### 2. Handle `function.json` config by yourself + +This method will support full support type when bindings config is set, for example below: + +you can define your own `function.json` in Typescript object (as you can see the variable `bindings`), this will binding type into `this.context.bindings`. + +```ts +import { BaseFunction, binding, functionName } from 'nammatham'; + +const bindings = [ + binding.httpTrigger({ name: 'req' as const }), // make string to literal type + binding.http({ name: 'res' as const }), // make string to literal type +] as const; + +@functionName('GetUser', ...bindings) +export class UserFunction extends BaseFunction { + + public override execute() { + const { req } = this.context.bindings; + // ^---- `req` will be type HttpRequest + const name = req.query.name; + this.context.res = { + body: `hello get user with ${name}}`, + }; + } +} +``` + +### 3. Add Services + + +```ts +// src/startup.ts +import 'reflect-metadata'; +import { NammathamApp } from 'nammatham'; +import { UserService } from './services/user.services'; +import { UserFunction } from './functions/user.function'; + +const builder = NammathamApp.createBuilder(__filename); +builder.addFunctions(UserFunction); +builder.configureServices(services => { + services.addSingleton(Service); + // services.addScoped(Service); + // services.addTransient(Service); +}); +builder.build(); + +export default builder.getApp(); +``` + +define a function handler + +```ts +import { AuthorizationLevel, BaseFunction, functionName, httpTrigger } from 'nammatham'; +import { HttpRequest } from '@azure/functions'; +import { UserService } from '../services/user.service'; +import { inject } from 'inversify'; + +@functionName('GetUsers', httpTrigger(AuthorizationLevel.Anonymous, ['get'])) +export class UserFunction extends BaseFunction { + + constructor(@inject(UserService) private userService: UserService){ + super(); + } + + public getUsers(): void { + const { req } = this.context.bindings; + const name = req.query.name; + const message = `hello get user with ${name}, service data: ${this.userService.getData()}`; + this.context.log(message); + this.res.send(message); + } +} +``` + +service: + +```ts +import { injectable } from 'inversify'; + +@injectable() +export class UserService { + constructor() {} + + public getData() { + return `Hey I'm service`; + } +} +``` + +## Using Inversify API for Binding Services + +In some cases, if you want to binding services with [Inversify Container](https://github.com/inversify/InversifyJS/blob/master/wiki/container_api.md) by yourself. +In the startup file, you can simply get the Container from `builder.container` as shown in the example below: + +```ts +// src/startup.ts +import 'reflect-metadata'; +import { NammathamApp } from 'nammatham'; +import { UserService } from './services/user.services'; +import { UserFunction } from './functions/user.function'; + +const builder = NammathamApp.createBuilder(__filename); +builder.addFunctions(UserFunction); +// Using Inversify Container API +builder.container.bind(Service).toSelf(); + +builder.build(); + +export default builder.getApp(); +``` + +## Inspiration +- [Azure Functions .NET](https://learn.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-csharp?tabs=azure-cli%2Cin-process) +- [inversify-express-utils](https://github.com/inversify/inversify-express-utils) - We use inversify as a Dependency Injection Tool. +- [Nestjs](https://nestjs.com/) +- [typestack/routing-controllers](https://github.com/typestack/routing-controllers) +- [azure-middleware](https://github.com/emanuelcasco/azure-middleware) - Azure Functions Middleware Libray + +## Author +- Thada Wangthammang, Software Engineer, Thailand diff --git a/apps/nammatham/versioned_sidebars/version-1.x-sidebars.json b/apps/nammatham/versioned_sidebars/version-1.x-sidebars.json new file mode 100644 index 0000000..caea0c0 --- /dev/null +++ b/apps/nammatham/versioned_sidebars/version-1.x-sidebars.json @@ -0,0 +1,8 @@ +{ + "tutorialSidebar": [ + { + "type": "autogenerated", + "dirName": "." + } + ] +} diff --git a/apps/nammatham/versions.json b/apps/nammatham/versions.json new file mode 100644 index 0000000..056f6d5 --- /dev/null +++ b/apps/nammatham/versions.json @@ -0,0 +1,3 @@ +[ + "1.x" +]