{"id":25858512,"url":"https://github.com/twnzl/nanium","last_synced_at":"2025-09-13T08:05:11.578Z","repository":{"id":57307765,"uuid":"426987455","full_name":"twnzl/nanium","owner":"twnzl","description":"nanoservice based fullstack software architecture framework","archived":false,"fork":false,"pushed_at":"2025-09-10T13:37:36.000Z","size":735,"stargazers_count":2,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-13T08:02:22.027Z","etag":null,"topics":["api","framework","nanoservices","typescript","webdevelopment"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/twnzl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-11-11T12:09:17.000Z","updated_at":"2025-06-27T12:21:46.000Z","dependencies_parsed_at":"2024-06-28T14:02:57.944Z","dependency_job_id":"b77018c8-9045-4914-acc9-898d6359748e","html_url":"https://github.com/twnzl/nanium","commit_stats":{"total_commits":224,"total_committers":3,"mean_commits":74.66666666666667,"dds":0.4419642857142857,"last_synced_commit":"4ec765f8f06bb50d2b505cb057d51532b8f3f584"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/twnzl/nanium","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twnzl%2Fnanium","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twnzl%2Fnanium/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twnzl%2Fnanium/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twnzl%2Fnanium/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/twnzl","download_url":"https://codeload.github.com/twnzl/nanium/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twnzl%2Fnanium/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274935979,"owners_count":25376836,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-13T02:00:10.085Z","response_time":70,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","framework","nanoservices","typescript","webdevelopment"],"created_at":"2025-03-01T20:19:43.936Z","updated_at":"2025-09-13T08:05:11.533Z","avatar_url":"https://github.com/twnzl.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# nanium\n\nNanium is the material that modern web applications and APIs are made of.\n\nIt is a nanoservice-based fullstack software architecture framework that takes full advantage of typescript to solve\nmany problems of traditional ways of building client-server applications.\n\n## Features\n\n* seamlessly type-save even across API borders\n* full-featured objects (instead of plain data transfer objects) on both sides of the API\n* code completion for API calls and easy API-refactoring\n* maximum reusable logic for server and client\n* easily exchangeable transfer protocols (http, websockets, tcp, etc.) and formats (json, xml, etc.).\n* automatic generation of SDKs for the services you offer\n* faster, cleaner and more flexible way to develop software\n\n## Short and sweet\n\n1. create a service\n\n```bash\nnanium g stuff/get public\n```\n\n2. execute it whether you are on the server or the client\n\n```ts\nconst response: Stuff = new StuffGetRequest({ id: 1 }).execute();\n```\n\n3. enjoy the response as a full-featured object\n\n```ts\nif (response.isGoodStuff()) {\n\tconsole.log(response.aDate.toLocaleString() + ' :-)');\n}\n```\n\n## Table of contents\n\n- [Features](#Features)\n- [Short and sweet](#Short-and-sweet)\n- [Installation](#Installation)\n- [Documentation](#Documentation)\n    - [Video tutorials](#Video-tutorials)\n    - [Demo app](#Demo-app)\n    - [Initialization](#Initialization)\n        - [Init the server (nodejs)](#Init-the-server-(nodejs))\n        - [Init the client (browser)](#Init-the-client-(browser))\n    - [Services](#Services)\n        - [Create a service](#Create-a-service)\n        - [Execute a service](#Execute-a-service)\n        - [Prepare the contracts](#Prepare-the-contracts)\n    - [Interceptors](#Interceptors)\n    - [Serializers](#Serializers)\n    - [Streaming](#Streaming)\n    - [Queues](#Queues)\n    - [Events](#Events)\n    - [SDKs](#SDKs)\n    - [Tests](#Tests)\n    - [REST, GraphQL \u0026 Co](#REST,-GraphQL-\u0026-Co)\n    - [Extensibility](#Extensibility)\n\n## Installation\n\n```bash\n$ npm install nanium\n```\n\n## Documentation\n\n### Video tutorials\n\nThe best way to get started is to watch the video tutorials at\nhttps://www.youtube.com/channel/UCV0pLzUzkdGazPXKJoGDjow\n\n### Demo app\n\nYou can download a ready-to-take-off demo app via\n\n```bash\n$ git clone https://github.com/twnzl/nanium-demo.git\n```\n\n## Initialization\n\n### Init the server (nodejs)\n\n```bash\n$ nanium init\n```\n\nThis will create the config file 'nanium.json' and the directory 'services' containing the following files:\n\n- main.interceptor.ts\n- serviceRequestBase.ts\n- serviceRequestContext.ts\n- serviceRequestHead.dto.ts\n- streamServiceRequestBase.ts\n\nFor now, leave them as they are. Later you can adapt this to meet your needs.\n\nIn the 'nanium.json' enter a namespace.\n\nNext create a node script, initiate a default Http Server and add a ServiceProvider with an HTTP channel. Channels are\nways through which public services can be executed from outside the server (e.g. a web client using http/websockets\nor another server using tcp).\n\n```ts\nimport * as http from 'http';\nimport { Nanium } from 'nanium/core';\nimport { NaniumHttpChannel } from 'nanium/managers/providers/channels/http';\nimport { NaniumProviderNodejs } from 'nanium/managers/providers/nodejs';\n\nconst httpServer: http.Server = http.createServer(() =\u003e {\n});\nhttpServer.listen(3000);\n\nasync function run(): Promise\u003cvoid\u003e {\n\tawait Nanium.addManager(new NaniumProviderNodejs({\n\t\tchannels: [\n\t\t\tnew NaniumHttpChannel({\n\t\t\t\tapiPath: '/api',\n\t\t\t\tserver: httpServer // https-server or an express-like app are also possible\n\t\t\t})\n\t\t]\n\t}));\n}\n\nrun();\n```\n\n### Init the client (browser)\n\n```ts\nawait Nanium.addManager(new NaniumConsumerBrowserHttp({ apiUrl: '/api' }));\n```\n\n## Services\n\n### Create a service\n\n```bash\nnanium g stuff/get public\n```\n\nThis will generate two files:\n\n- __services/stuff/get.contract.ts__: The contract for the service. The request defines information that a service\n  consumer must supply if he wants the service to be executed. And the response defines which information the result of\n  the service execution will contain.\n\n```ts\nimport { ServiceRequestBase } from '../serviceRequestBase';\nimport { RequestType } from 'nanium/objects';\nimport { NaniumObject } from './objects';\n\nexport class StuffGetRequestBody extends NaniumObject\u003cStuffGetRequestBody\u003e {\n}\n\nexport class StuffGetResponse extends NaniumObject\u003cStuffGetResponse\u003e {\n}\n\n@RequestType({\n\tresponseType: StuffGetResponse,\n\tgenericTypes: { TRequestBody: StuffGetRequestBody },\n\tscope: 'public'\n})\nexport class StuffGetRequest extends ServiceRequestBase\u003cStuffGetRequestBody, StuffGetResponse\u003e {\n\tstatic serviceName: string = 'NaniumTest:stuff/get';\n}\n\n``` \n\nProbably most of the time you would use instances of your domain's entities instead of StuffGetRequestBody and\nStuffGetResponse. So you can delete these generated classes and define your own, either within the same .contract.ts\nfile or in a separate file but with the extension __.contractpart.ts__.\n\n- __services/stuff/get.executor.ts__: The implementation of the service. It uses the values of the request and\n  calculates the response.\n\n```ts\nimport { ServiceExecutor } from 'nanium/interfaces/serviceExecutor';\nimport { StuffGetRequest, StuffGetResponse } from './get.contract';\nimport { ServiceRequestContext } from '../serviceRequestContext';\n\nexport class StuffGetExecutor implements ServiceExecutor\u003cStuffGetRequest, StuffGetResponse\u003e {\n\tstatic serviceName: string = 'NaniumTest:stuff/get';\n\n\tasync execute(request: StuffGetRequest, executionContext: ServiceRequestContext): Promise\u003cStuffGetResponse\u003e {\n\t\t// todo: Do what is described through the request, calculate the resppnse and return it.\n\t}\n}\n```\n\n### Execute a service\n\nWhether you're in the node script that hosts the service or in the browser, it's the same thing, and you do not have to\nworry about.\n\n```ts\nconst response = await new StuffRequest().execute();\n```\n\n### Prepare the contracts\n\n#### Decorators\n\nUnfortunately, the typescript compiler still does not support the generation of type information that can be used at\nruntime. But this information is necessary to make the contract serialization and deserialization work. Therefore,\nnanium uses decorators to fill this gap.\n\nCurrently, there are two essential decorators.\n\n- __@Type()__: Used for Properties. The first Parameter is either generic Type-ID if the property has a generic Type or\n  it is the class/constructor of the property. The second parameter is a dictionary with GenericTypeIDs as key and\n  class/constructor as value.\n- __@RequestType()__: Use the property 'responseType' to set the class of the response. And for each defined generic\n  type identifier specify the concrete class using the property 'genericTypes'\n\ncomplex example:\n\n```ts\nimport { NaniumObject } from './objects';\n\nexport class GenericStuff\u003cTStuffSubType\u003e extends NaniumObject\u003cGenericStuff\u003cTStuffSubType\u003e\u003e {\n\t@Type(String) aString?: string;\n\t@Type(Number) aNumber?: number;\n\t@Type(Boolean) aBoolean?: boolean;\n\t@Type('TStuffSubType') theGeneric?: TStuffSubType;\n}\n\nexport enum StuffEnum {\n\tzero = 'z',\n\tone = 'o',\n\ttwo = 't'\n}\n\nexport class Stuff\u003cTStuffSubType\u003e extends NaniumObject\u003cStuff\u003cTStuffSubType\u003e\u003e {\n\t@Type(String) aString?: string;\n\t@Type(Number) aNumber?: number;\n\t@Type(Boolean) aBoolean?: boolean;\n\t@Type(String) anEnum?: StuffEnum;\n\t@Type(Date) aDate?: Date;\n\n\t// property 'theGeneric' of 'anObject' will be a Date \n\t// as globaly set by decorator RequestType of surrounding Type StuffRequest\n\t@Type(Stuff) anObject?: Stuff\u003cTStuffSubType\u003e;\n\t// property 'theGeneric' of 'aGenericObject' will be a Number (local definition overwrites global)\n\t@Type(GenericStuff, { 'TStuffSubType': Number }) aGenericObject?: GenericStuff\u003cTStuffSubType\u003e;\n\n\t@Type(Array, Stuff) anObjectArray?: Stuff\u003cTStuffSubType\u003e[];\n\t@Type(Array, String) aStringArray?: string[];\n\t@Type(Array, GenericStuff) aGenericObjectArray?: GenericStuff\u003cTStuffSubType\u003e[];\n\n\t@Type(Object, Boolean) aBooleanDictionary: { [key: string]: Boolean };\n\n\t// the type of property 'config' is determined dynamically using the given arrow function   \n\t@Type(String) configType: 'a' | 'b';\n\t@Type((p: Stuff\u003cany\u003e) =\u003e a.configType === 'a' ? A : B) config: A | B;\n\n\tget aCalculatedProperty(): string {\n\t\treturn this.aStringArray?.join(' ');\n\t}\n\n\taFunction(): number {\n\t\treturn this.aStringArray?.length;\n\t}\n}\n\n@RequestType({\n\tresponseType: Stuff,\n\tgenericTypes: {\n\t\tTStuffSubType: Date,\n\t\tTRequestBody: Stuff,\n\t\tTResponseBody: Stuff,\n\t\tTPartialResponse: Stuff\n\t},\n\tscope: 'public'\n})\nexport class StuffRequest extends ServiceRequestBase\u003cStuff\u003cDate\u003e, Stuff\u003cDate\u003e[]\u003e {\n\tstatic serviceName: string = 'NaniumTest:test/stuff';\n}\n```\n\n#### initializers\n\nIn the example above, the contract classes extend the type NaniumObject. This is not necessary, but it automatically\nprovides an initializer constructor for classes. So when you create new instances, you can pass initial data to the\nconstructor and nanium will put the data into your new Object in a type save way. That means all property values, even\nthose of sub objects will be real instances of its classes defined by the Type decorators.\n\nexample:\n\n```ts\nclass Person extends NaniumObject\u003cPerson\u003e {\n\t@Type(String) name: string;\n\t@Type(Array, Person) friends?: Person[];\n}\n\nconst john = new Person({\n\tname: 'John',\n\tfriends: [\n\t\tnew Person({ name: 'Jane' }),\n\t\t{ name: 'Bob' },\n\t]\n});\n\nif (john.friends[1] instanceof Person) {\n\tconsole.log([\n\t\tjohn.name,\n\t\tjohn.friends[0].name,\n\t\tjohn.friends[1].name\n\t].join(' \u0026 '));\n}\n```\n\nThe output is 'John \u0026 Jane \u0026 Bob'.\n\nThe initialization of Bob will also work, but if there are any getters or functions in class Person typescript will\ncomplain that they are missing here. But not, when using the constructor like for Jane.\n\nThe constructor will have two more parameters. The second one you can use to specify Types for generic\ntype ids, if there are some. And if you set the third parameter *strict* to true nanium will ensure that only that\nproperties will be\ncreated in the new instance, that are decorated with the Type decorator. This is e.g. useful to convert between internal\ndata structures and external data structures and to ensure no internal data leave the server and no invalid external\ndata are stored to the database.\n\n#### ORM\n\nWhen your business entities derive from **NaniumObject**, they automatically get basic ORM functionality that is\nindependent of the data source. Just load a record/object from any database and put it into the constructor of your\nbusiness entity class. Or if you get data via the API, do the same and only valid properties with correct type, reach\nthe entity and the database. If you do not want to use NaniumObject as a base class you can achieve the same with the\nstatic functions of **NaniumObject** like **init**.\n\nMaybe this will be extended in the future to provide additional features like property renaming or conditional property\nmapping or extended validation.\n\n## Exception/Error handling\n\nIf a service executor throws an Error, it can be caught as usual when using promises. Again, it does not matter whether\nyou are on the server or on a remote client.\n\n```ts\ntry {\n\tconst response = new StuffRequest().execute();\n} catch (e: Error) {\n\tdocument.write(e.message);\n}\n```\n\nSince streamed services would return Observables you would use the error handler of the Observable in that case.\n\n```ts\nconst response = new StuffRequest().stream().subscribe({\n\tnext: (value: TestDto): void =\u003e {\n\t\tdtoList.push(value);\n\t},\n\tcomplete: (): void =\u003e resolve(),\n\terror: (e: Error) =\u003e {\n\t\tdocument.write(e.message);\n\t}\n});\n```\n\n## Interceptors\n\nAn interceptor is a piece of code that can analyse or modify a request, either on the consumer side before it is sent to\na provider, or on the provider side before it is executed. Typically, you would use this e.g. on a web client to add\nauthentication information to the request before it will be sent to the server, or on the server site to check the\nauthentication.\n\n### implement a request interceptor for consumer site\n\nIn this example a client request interceptor is implemented as an angular service. If the user has already logged into\nthe application it adds the auth-token from the users' session. Additionally, it adds the preferred language and time\nzone of the current user so the server can take this into account. The head of the ServiceRequestBase class is the best\nplace for this type of always needed/usable information. If the user is not logged in, it loads the login page and\nreturns undefined to cancel the request.\n\n```ts\n@Injectable({ providedIn: 'root' })\nexport class ClientRequestInterceptorService implements ServiceRequestInterceptor\u003cany\u003e {\n\n\tconstructor(private session: SessionService) {\n\t}\n\n\tasync execute(request: ServiceRequestBase\u003cany, any\u003e): Promise\u003cServiceRequestBase\u003cany, any\u003e\u003e {\n\t\tif (!this.session.isLoggedIn \u0026\u0026 request.head \u0026\u0026 (!request.head.email || !request.head.password)) {\n\t\t\tawait this.router.navigate(['/login']);\n\t\t\treturn undefined;\n\t\t}\n\t\trequest.head = request.head || {};\n\t\tif (!request.head.email \u0026\u0026 !request.head.password) {\n\t\t\trequest.head.token = this.session.token;\n\t\t}\n\t\trequest.head.language = navigator.language || navigator['userLanguage'];\n\t\trequest.head.timezone = Intl?.DateTimeFormat()?.resolvedOptions()?.timeZone;\n\n\t\treturn request;\n\t}\n}\n```\n\n### implement a request interceptor for provider site\n\n\"nanium init\" will create an example interceptor 'main.interceptor.ts', which you can use as a template for a\nserver-side request interceptor. For an authentication interceptor e.g. check the user and password (as the case may be)\nin the request and add the user entity from the database to the executionContext, so the executor or later interceptors\nwill have access to all user information. If the credentials are not right, throw an error.\n\n```ts\nimport { ServiceRequestInterceptor } from 'nanium/interfaces/serviceRequestInterceptor';\nimport { ServiceRequestBase } from './serviceRequestBase';\nimport { ServiceRequestContext } from './serviceRequestContext';\n\nexport class RequestInterceptor implements ServiceRequestInterceptor\u003cServiceRequestBase\u003cany, any\u003e\u003e {\n\n\tasync execute(request: ServiceRequestBase\u003cany, any\u003e, executionContext: ServiceRequestContext): Promise\u003cServiceRequestBase\u003cany, any\u003e\u003e {\n\t\tif (\n\t\t\trequest.head.userName === 'jack' \u0026\u0026 request.head.password === '1234' ||\n\t\t\trequest.head.userName === 'jenny' \u0026\u0026 request.head.password === '4321'\n\t\t) {\n\t\t\t// pseudo code: Load the user entity from your database to the execution context, so it will be easily \n\t\t\t// available in each executor\n\t\t\texecutionContext.user = Database.get\u003cUser\u003e(request.head.userName);\n\t\t} else {\n\t\t\tthrow new Error('not authorized');\n\t\t}\n\t\treturn request;\n\t}\n}\n\n```\n\n### register an interceptor\n\nSet the property 'requestInterceptors' of the provider or consumer that is passed to the Nanium.addManager() function.\nIt is an array of interceptor classes (needing a parameterless constructor) or instances, so you can add multiple\ninterceptors which are executed sequentially according to its order in the array.\n\n```ts\nawait Nanium.addManager(new NaniumConsumerBrowserHttp({\n\tapiUrl: '/api',\n\trequestInterceptors: [MyInterceptorService]\n}));\n```\n\n### skip interceptors\n\nFor example, if you have an interceptor that checks authentication, but you want to have a service that is callable\nwithout authorization (anonymous), you can skip the execution of this interceptor for this special service. To do so use\nthe property 'skipInterceptors' of the RequestType Decorator. If set to true, then all interceptors are skipped. If you\nonly want to skip specific interceptors, use an array with the names of the interceptor classes. To skip interceptors\ndepending on the execution scope, you can use an object with the scope as properties and bool or string array as values.\n\n```ts\n@RequestType({\n\tresponseType: ServiceResponseBase,\n\tskipInterceptors: ['MyInterceptorService'],\n\tscope: 'public'\n})\nexport class AnonymousRequest extends ServiceRequestBase\u003cvoid, string\u003e {\n\tstatic serviceName: string = 'NaniumTest:test/anonymous';\n}\n```\n\n## Serializers\n\nBy default, objects are transported over the network as JSON. However, you can also use other serializers. For example,\nto transfer data via UBJSON, XML, or any own binary format. For this you just need to implement the interface\n**NaniumSerializer** and pass an instance of the serializer to the provider channels and consumers.\n\n```ts\n// server\nawait Nanium.addManager(\n\tnew NaniumProviderNodejs({\n\t\tchannels: [\n\t\t\tnew NaniumHttpChannel({\n\t\t\t\tapiPath: '/api',\n\t\t\t\teventPath: '/events',\n\t\t\t\tserver: httpServer,\n\t\t\t\tserializer: new NaniumJsonSerializer(),\n\t\t\t}),\n\t\t]\n\t})\n);\n\n// consumer\nconst serializer = new NaniumJsonSerializer();\nserializer.packageSeparator = '\\0';\nNanium.addManager(\n\tnew NaniumConsumerBrowserHttp({\n\t\tapiUrl: baseUrl + '/api',\n\t\tapiEventUrl: baseUrl + '/events',\n\t\tserializer: serializer,\n\t\thandleError: async (err: any): Promise\u003cany\u003e =\u003e {\n\t\t\tthrow { handleError: err };\n\t\t}\n\t})\n);\n```\n\n### Binary data\n\nRegardless of which serializer you use, binary data is always treated specially. If you define the result type of a\nservice as NaniumBuffer, the data is not serialized or deserialized, but transported to the client as it\nis.\n\n```ts\n// contract\n@RequestType({\n\tresponseType: NaniumBuffer,\n\tscope: 'public'\n})\nexport class TestGetBinaryRequest extends SimpleServiceRequestBase\u003cvoid, NaniumBuffer\u003e {\n\tstatic serviceName: string = 'NaniumTest:test/getBinary';\n}\n\n// executor\nexport class TestGetBinaryExecutor implements ServiceExecutor\u003cTestGetBinaryRequest, NaniumBuffer\u003e {\n\tstatic serviceName: string = 'NaniumTest:test/getBinary';\n\n\tasync execute(request: TestGetBinaryRequest, executionContext: ServiceRequestContext): Promise\u003cNaniumBuffer\u003e {\n\t\treturn new NaniumBuffer('this is a text that will be send as binary data');\n\t}\n}\n```\n\nNaniumBuffers can also be included as Properties of Requests. E.g. to send Files or other binary or large data together\nwith other information like IDs or file names etc.\n\n```ts\n// contract\nimport { NaniumBuffer } from './naniumBuffer';\n\n@RequestType({ responseType: String, scope: 'public' })\nexport class TestMeasurementStoreRequest extends SimpleServiceRequestBase\u003cvoid, string\u003e {\n\tstatic serviceName: string = 'NaniumTest:test/bigData/Store';\n\n\t@Type(Date) startTime: Date;\n\t@Type(Date) endTime: Date;\n\t@Type(Array, String) enabledSensors: string[];\n\t@Type(NaniumBuffer) sensorValues: NaniumBuffer;\n\t@Type(NaniumBuffer) video: NaniumBuffer;\n}\n\n// executor\nexport class TestMeasurementStoreExecutor implements ServiceExecutor\u003cTestMeasurementStoreRequest, string\u003e {\n\tstatic serviceName: string = 'NaniumTest:test/bigData/Store';\n\n\tasync execute(request: TestMeasurementStoreRequest, executionContext: ServiceRequestContext): Promise\u003cstring\u003e {\n\t\tconst id: string = randomUUID();\n\t\tawait fs.promises.writeFile(id + '.mp4', request.body.video.asUint8Array());\n\t\t// ...\n\t\treturn id;\n\t}\n}\n```\n\n## Streaming\n\nIf you want a service executor to provide the possibility to return partial results, you can use NaniumStream as a\nservice result\n\n```ts\n@RequestType({\n\tresponseType: [NaniumStream, TestDto],\n\tscope: 'public'\n})\nexport class TestStreamedQueryRequest extends SimpleServiceRequestBase\u003cTestStreamedQueryRequestBody, NaniumStream\u003cTestDto\u003e\u003e {\n\tstatic serviceName: string = 'NaniumTest:test/streamedQuery';\n}\n```\n\nThe example shows an object stream. Result type NaniumStream\u003cNaniumBuffer\u003e would be a binary stream. On the callers side\nthe result can be consumed in small parts using the onData() function:\n\n```ts\nconst response: NaniumStream\u003cTestDto\u003e = await new TestStreamedQueryRequest().execute();\nresponse.onData((value: TestDto): void =\u003e dtoList.push(value));\nresponse.onEnd(() =\u003e resolve());\nresponse.onError((err: Error) =\u003e console.error(err));\n```\n\nIt is also possible to consume the result as whole package using the toPromise() function.\nEven in this case nanium will at least use the benefits of streaming internally - e.g. parallelism of\ndata transmission and deserialization.\n\n```ts\nconst responseStream: NaniumStream\u003cTestDto\u003e = await new TestStreamedQueryRequest().execute();\nconst dtoList: TestDto[] = await responseStream.toPromise();\n```\n\n### Binary data\n\nRegardless of which serializer you use, binary data is always treated specially. If you define the result-type of a\nservice as NaniumBuffer, the data is not serialized or deserialized, but transported to the client as it is.\n\n```ts\n// the contract\n@RequestType({\n\tresponseType: NaniumBuffer,\n\tscope: 'public'\n})\nexport class TestGetStreamedBufferRequest extends ServiceRequestBase\u003cvoid, NaniumBuffer\u003e {\n\tstatic serviceName: string = 'NaniumTest:test/getStreamedBuffer';\n}\n\n// the executor\nexport class TestGetStreamedBufferExecutor implements StreamServiceExecutor\u003cTestGetStreamedBufferRequest, ArrayBuffer\u003e {\n\tstatic serviceName: string = 'NaniumTest:test/getStreamedBuffer';\n\n\tstream(request: TestGetStreamedBufferRequest, executionContext: ServiceRequestContext): Observable\u003cArrayBuffer\u003e {\n\t\treturn new Observable((observer: Observer\u003cArrayBuffer\u003e): void =\u003e {\n\t\t\tconst enc: TextEncoder = new TextEncoder();\n\t\t\tconst buf: ArrayBuffer = enc.encode('This is a string converted to a Uint8Array');\n\t\t\tobserver.next(buf.slice(0, 4));\n\t\t\tsetTimeout(() =\u003e observer.next(buf.slice(4, 20)), 500);\n\t\t\tsetTimeout(() =\u003e observer.next(buf.slice(20, buf.byteLength)), 1000);\n\t\t\tsetTimeout(() =\u003e observer.complete(), 1500);\n\t\t});\n\t}\n}\n\n// the client call\nnew TestGetStreamedBufferRequest(undefined, { token: '1234' }).stream().subscribe({\n\tnext: (part: NaniumBuffer): void =\u003e {\n\t\tconsole.log(part.asString());\n\t\t// output:\n\t\t// This\n\t\t//  is a string convert\n\t\t// ed to a Uint8Array\n\t}\n});\n```\n\n## Queues\n\nMaybe you want to execute a request at a later time or periodically, or you just want to have a log of executed requests\nand their results, and a chance to restart any failed requests, or similar. In that case, a request queue is what you\nneed.\n\nEvery nanoservice within a nanium-based app can be executed via queue. You just have to decide what kind of queue you\nwant to use, and make it known to nanium. Use an existing queue or provide your own (use a database, the filesystem, a\nGoogle sheet or whatever you prefer - it's up to you). For Example, using the mongodb queue, which holds requests in a\nmongodb collection, would look like this:\n\n### Install the desired queue\n\n```bash\nnpm i --save nanium-queue-mongodb\n```\n\n### Add the queue\n\n```ts\nconst mongoQueue = new NaniumMongoQueue({\n\tcheckInterval: 10,\n\tserverUrl: 'mongodb://localhost:27017',\n\tdatabaseName: 'nanium_test',\n\tcollectionName: 'rq',\n});\nawait Nanium.addQueue(mongoQueue);\n```\n\n### Add a request from code\n\n```ts\nawait new AdminInfoMailSendRequest('hello admin').enqueue({ startDate: new Date('2099-31-01T00:00:00.000Z') });\n```\n\nThis is a server-only feature because of security reasons. To use it from a client, just crate a public service that\nwraps the enqueue and use your default authorization mechanism.\n\n### Add a request directly into the queue\n\nOf course, you can also add requests directly to the collection using a mongodb client or the shell, etc. The state must\nbe set to 'ready';\n\n```js\ndb.requestQueue.insert([\n  {\n    \"serviceName\": \"NaniumDemo:adminInfoMail/send\",\n    \"groupId\": \"\",\n    \"request\": {\n      \"body\": {\n        text: \"hello admin\"\n      }\n    },\n    \"response\": null,\n    \"state\": \"ready\",\n    \"startDate\": \"2085-02-06T14:40:24.555Z\",\n    \"endDate\": null,\n    \"interval\": 3600,\n    \"endOfInterval\": null\n  }])\n```\n\n## Events\n\n**(experimental)** Currently, the NaniumHttpChannel has basic support for this feature, but it is still experimental.\n\nA provider can emit events:\n\n```ts\nnew StuffAddedEvent(stuff).emit(executionContext);\n```\n\nConsumers may subscribe to events ...\n\n```ts\nawait StuffAddedEvent.subscribe((value: Stuff) =\u003e {\n\t// e.g. update app state or cache\n});\n```\n\n... and unsubscribe:\n\n```ts\nconst subscription = await StuffAddedEvent.subscribe((value: Stuff) =\u003e {\n\t// ...\n});\n// to deregister a specific handler, use the returned subscription\nsubscription.unsubscribe();\n// or, to deregister all registered handler functions for an event type, use:\nStuffAddedEvent.unsubscribe();\n```\n\nVia Nanium.addManager you can configure which channel should be used for the transmission of events, and you can add\nevent interceptors.\n\n**server:**\n\n```ts\nawait Nanium.addManager(new NaniumNodejsProvider({\n\tservicePath: 'services',\n\tchannels: [\n\t\tnew NaniumHttpChannel({ apiPath: '/api', eventPath: '/events', server: server })\n\t],\n\teventSubscriptionReceiveInterceptors: [DemoEventSubscriptionReceiveInterceptor],\n\teventEmissionSendInterceptors: [DemoEventEmissionSendInterceptor]\n}));\n```\n\n**browser:**\n\n```ts\nawait Nanium.addManager(new NaniumConsumerBrowserHttp({\n\tapiUrl: 'http://localhost:3000/api',\n\tapiEventUrl: 'http://localhost:3000/events',\n\teventSubscriptionSendInterceptors: [DemoEventSubscriptionSendInterceptor]\n}));\n```\n\n## SDKs\n\nIf you want to use your services in another project, or if you want to provide an easy way for other people to use them,\nyou can easily create an SDK.\n\n```bash\nnanium sdk b \n```\n\nThis will generate a npm bundle as a .tgz file, that contains all your public contracts.\n\n```bash\nnanium sdk p \n```\n\nUsing the option \"p\", will publish it directly to the npm registry. So, after using \"npm i nanium \u003cyour-sdk\u003e\" in the\nother project, you will have all you need to create and execute requests of the other project/domain. Just use the __\nisResponsible__ property to adjust which nanium-provider or nanium-consumer is responsible for which services. Most of\nthe time, the namespace of the services should be enough to distinguish that.\n\n```ts\n// foreign services\nconst domain1ServiceConsumer: NocatConsumerNodejsHttp = new NocatConsumerNodejsHttp({\n\tapiUrl: \"http://.../api\",\n\tisResponsible: (_request: any, serviceName: string) =\u003e Promise.resolve(serviceName.startsWith('Domain1:') ? 'yes' : 'no')\n});\nawait Nocat.addManager(domain1ServiceConsumer);\n\n// own services\nconst myServiceProvider: NocatNodejsProvider = new NocatNodejsProvider({\n\tservicePath: path.join(__dirname, 'services'),\n\tisResponsible: (_request: any, _serviceName: string) =\u003e Promise.resolve('fallback'),\n});\nawait Nocat.addManager(myServiceProvider);\n```\n\nYou can use the property \"sdkPackage\" in the nanium.json to specify all the values you want to have in the package.json\nof the sdk bundle. And if you want to have other settings for the Typescript compiler, you can set them in the \"\nsdkTsConfig\" property in the nanium.json.\n\nThe sdk functions will surely be completed one day, so that alternatively to the SDK a standard API documentation can be\ngenerated as an alternative to the SDK, but for Typescript users the SDK is far better than just documentation.\n\n## Tests\n\nDue to the loosely coupled nature of nanium, it is easy to swap implementations. So in your server unit tests you should\njust leave the servicePath-Property of the NaniumNodejsProvider empty, so no services will be registered. And in the\nsecond step, add the original service you want to test. And add mock implementations for services that are used by this\ntest unit.\n\n### server tests\n\n```ts\n// init nanium\nbeforeEach(async () =\u003e {\n\tconst provider: NaniumNodejsProvider = new NaniumNodejsProvider({/* servicePath: '' */ });\n\tawait Nanium.addManager(provider);\n\tprovider.addService(StuffCalculateRequest, SuffCalculateExecutor);\n\tprovider.addService(MockStuffStoreRequest, MockStuffQueryExecutor);\n\tprovider.addService(MockStuffQueryRequest, class {\n\t\tasync execute(_request: MockStuffQueryRequest, _executionContext: ServiceRequestContext): Promise\u003cStuffDto[]\u003e {\n\t\t\treturn [];\n\t\t}\n\t});\n});\n\nafterEach(async () =\u003e {\n\tawait Nanium.shutdown();\n});\n```\n\n### client tests\n\nTo test webclients you can add a (or an additional) **NaniumProviderBrowser** to the list of managers. The isResponsible\nfunction should return a value higher than the one of the Consumer that normally handles server requests. So all server\nrequests can be mocked.\n\n```ts\n// init nanium\nbeforeEach(async () =\u003e {\n\tconst mockServerProvider = new NaniumProviderBrowser({\n\t\tisResponsible: async (request, serviceName) =\u003e {\n\t\t\treturn serviceName.startsWith('NaniumTest:') ? 2 : 0;\n\t\t},\n\t\tisResponsibleForEvent: async (eventName) =\u003e {\n\t\t\treturn eventName.startsWith('NaniumTest:') ? 2 : 0;\n\t\t},\n\t});\n\tNanium.addManager(mockServerProvider);\n\tmockServerProvider.addService(\n\t\tTestGetRequest,\n\t\tclass {\n\t\t\tasync execute(request: TestGetRequest): Promise\u003cTestGetResponse\u003e {\n\t\t\t\treturn new TestGetResponse({\n\t\t\t\t\toutput1: 'mock1',\n\t\t\t\t\toutput2: 2222,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t);\n\t// now all calls to new TestGetRequest(...).execute() will be handled by the mock implementation \n});\n\nafterEach(async () =\u003e {\n\tawait Nanium.shutdown();\n});\n```\n\n## REST, GraphQL \u0026 Co\n\nNanoservices, especially Nanium services, are not hardwired to a protocol or any kind of API. The backend programmer can\njust focus on implementing the logic and the frontend/client developer, while consuming the API, doesn't have to mess\naround with protocols like HTTP or with code generators or special query languages and tether his logic to them.\n\nIf you are using typescript as an API consumer, you will usually want to use Nanium for calling the service to take\nadvantage of all the benefits of Nanium (e.g. IntelliSense, type safety or easy refactoring across API boundaries).\nHowever, you can also call Nanium services via traditional ways.\n\nOn the one hand, the **NaniumHttpChannel** automatically provides a single-endpoint HTTP POST API:\n\n```ts\nconst req: Request = new Request(\n\t'http://localhost:3001/api',\n\t{\n\t\tmethod: 'post',\n\t\tbody: JSON.stringify({\n\t\t\tserviceName: \"NaniumTest:Stuff/query\",\n\t\t\trequest: {\n\t\t\t\tbody: {\n\t\t\t\t\ttype: 'goodStuff'\n\t\t\t\t},\n\t\t\t\thead: {\n\t\t\t\t\ttoken: 'rmufas9i6fsfq2x32w38fdbs3sviv7frs54wldfuy3s7udfmheg1jz1owix9s8nv6hni',\n\t\t\t\t\tlanguage: 'de-DE',\n\t\t\t\t\ttimezone: 'Europe/Berlin'\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n);\nfetch(req)\n\t.then(async (response) =\u003e {\n\t\tif (response.ok) {\n\t\t\tconst data: Stuff[] = await response.json();\n\t\t\tconsole.log(data.length + ' items of good stuff received');\n\t\t} else {\n\t\t\tthrow await response.json();\n\t\t}\n\t})    \n```\n\nIn addition, other Nanium channels, in combination with other serializers, can present services to the outside world in\ncompletely different ways (even in parallel). For example, the **NaniumRestChannel** makes the services available in the\nusual REST manner using service contract files to create the endpoint and the name of the contract file to choose the\nHTTP method. Of course, this could be realized even better - but it is only an example channel to show the principle.\n\n```bash\nnpm i nanium-channel-rest\n```\n\n```ts\nawait Nanium.addManager(new NaniumProviderNodejs({\n\tservicePath: 'services',\n\tchannels: [\n\t\tnew NaniumHttpChannel({ apiPath: '/api', eventPath: '/events', server: server }),\n\t\tnew NaniumRestChannel({ apiBasePath: '/api2', server: server })\n\t],\n\teventSubscriptionReceiveInterceptors: [DemoEventSubscriptionReceiveInterceptor],\n\teventEmissionSendInterceptors: [DemoEventEmissionSendInterceptor]\n}));\n```\n\n```ts\nconst req: Request = new Request(\n\t'http://localhost:3001/api/stuff',\n\t{\n\t\tmethod: 'get',\n\t\tbody: JSON.stringify({ // the body\n\t\t\ttype: 'goodStuff'\n\t\t}),\n\t\theaders: { // the head\n\t\t\ttoken: 'rmufas9i6fsfq2x32w38fdbs3sviv7frs54wldfuy3s7udfmheg1jz1owix9s8nv6hni',\n\t\t\tlanguage: 'de-DE',\n\t\t\ttimezone: 'Europe/Berlin'\n\t\t}\n\t}\n);\nfetch(req)\n\t.then(async (response) =\u003e {\n\t\tif (response.ok) {\n\t\t\tconst data: Stuff[] = await response.json();\n\t\t\tconsole.log(data.length + ' items of good stuff received');\n\t\t} else {\n\t\t\tthrow await response.json();\n\t\t}\n\t})\n```\n\n## Extensibility\n\nNanium defines interfaces for all its basic parts and each of these building blocks is interchangeable. So you can\ncreate your own managers (provider or consumer), channels, interceptors, serializers and queues.   \nJust write a class that implements the corresponding interface.\n\n### Plugins\n\nServices written by third parties can be published as npm packages and easily be added to your app. The third party\nshould implement an init function that takes database connections or other configuration values and should return an\ninitialized Nanium service provider. Or it returns a list of pairs of request and executor class constructors, so you\ncan care for the service registration and the wanted channels yourself.\n\nSince this enables plug-ins with well-defined API functions, suitable frontend components can also be implemented and\nmade available. In this way, complete parts of client-server applications can be provided - reusable in multiple\napplications. Just think of a complete login and user management or an admin frontend for managing entries in nanium\nqueues. You can host this as a separate application or add it to an existing application. Or do the one today and the\nother tomorrow or even both at the same time.\n\n## Version info\n\nInformation about new features, breaking and non breaking changes and upgrade steps can be found\nin [RELEASES](./RELEASES.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwnzl%2Fnanium","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftwnzl%2Fnanium","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwnzl%2Fnanium/lists"}