UNPKG

@tbd54566975/dwn-proxy-js

Version:

Bidirectional proxy between DWM's <> RESTful

153 lines (122 loc) 6.07 kB
# DWN Proxy <!-- omit in toc --> Making DWN integrations with traditional backend services easy. ⚠️ UNDER DEVELOPMENT ⚠️ `dwn-proxy-js` is a bidirectional proxy between [Decentralized Web Nodes](https://identity.foundation/decentralized-web-node/spec) and your web services. * [Design](#design) * [Usage](#usage) * [`new DwnProxy(options)`](#new-dwnproxyoptions) * [`DwnProxy.listen(port)`](#dwnproxylistenport) * [`DwnProxy.addHandler(match, handler)`](#dwnproxyaddhandlermatch-handler) * [`DwnProxy.server.api`](#dwnproxyserverapi) * [Project Resources](#project-resources) ![Intro diagram](./images/intro.png) # Design At it's lightest, this package can act as a network router for DWN Message's. At it's heaviest, this package can be used to selectively abstract DWN-concepts from your web services. You have optionality as to the degree to which you differentiate across the two network interfaces. Like the [`dwn-server`](https://github.com/TBD54566975/dwn-server), this package is intended to be used server-side, wherein DWN Messages are interfaced with via JSON-RPC (compatible with [`web5-js`](https://github.com/TBD54566975/web5-js)'s Agent [interface](https://github.com/TBD54566975/web5-js/tree/main/packages/web5-agent)). However, unlike [`dwn-server`](https://github.com/TBD54566975/dwn-server), this package offers a programmatic interface for handling DWN Messages, both inbound and outbound, with the design intent of integrating with traditional backend services. # Usage In a new directory, run: ```cli npm init -y npm install @tbd54566975/dwn-proxy-js ``` Then edit the package.json to have `"type":"module"` in it. Add a file called `index.js` with the following contents: ```javascript import { DwnProxy, readReq } from '@tbd54566975/dwn-proxy-js'; const isMessageA = (dwnRequest) => dwnRequest.message.descriptor.interface === 'Records' && dwnRequest.message.descriptor.method === 'Query' && dwnRequest.message.descriptor.filter.schema === 'https://tbd.website/resources/message-a'; const isMessageB = (dwnRequest) => dwnRequest.message.descriptor.interface === 'Records' && dwnRequest.message.descriptor.method === 'Write' && dwnRequest.message.descriptor.schema === 'https://tbd.website/resources/message-b'; class MyProxy extends DwnProxy { async handlerA(request) { // do whatever you want // ... // example: maybe process the message using the DWN instance const { id } = this.options.didState; await this.dwn.processMessage(id, request.message, request.payload); } async handlerB(request) { // do whatever you want // ... // example: maybe forward the request onto your backend await fetch('/your-backend', { method: 'POST', body: JSON.stringify(request) }); } async apiC(req, res) { const body = await readReq(req); // do whatever you want // ... // maybe send the message onto a user await this.client.send(body.to, body.dwnRecordsWrite, JSON.stringify(body.data)); } async apiD(req, res) { const body = await readReq(req); // do whatever you want // ... } // overriding the default DwnProxy.listen() async listen(port) { await super.listen(port); // wire-up your dwn handlers this.addHandler(isMessageA, this.handlerA); this.addHandler(isMessageB, this.handlerB); // wire-up your server handlers this.server.api.post('/handler-c', this.apiC); this.server.api.post('/handler-d', this.apiD); } } const PORT = 8080; const proxy = new MyProxy({}); await proxy.listen(PORT); ``` ```cli node index.js ``` And you have a proxy running! ## `new DwnProxy(options)` - `options`: - (optional) `serviceEndpoint` - (optional) `didState` ## `DwnProxy.listen(port)` Start a JSON-RPC server, hosting an HTTP server at the given `port`. - (required) `port`: number ## `DwnProxy.addHandler(match, handler)` Add a handler for inbound DWN Messages. ```typescript const isMyMessage = req => req.message.descriptor.interface === 'Records' && req.message.descriptor.method === 'Write' && req.message.descriptor.schema === 'https://your-schema/file.json' proxy.addHandler( isMyMessage, async req => { // do whatever you would like with the given DwnRequest } ) ``` - (required) `match`: `(req: DwnRequest) => boolean` - if evaluated to `true` then use `handler` for the given message - (required) `handler`: `(dwnRequest: DwnRequest) => Promise<void | DwnResponse>` - if return type is `void` then the underlying `DwnHttpServer` will call `dwn.processMessage()` whereafter it will respond to the client w/ the given result - Else, you can explicitly specify your `DwnResponse` which will *not* result in a subsequent call to `dwn.processMessage()` ## `DwnProxy.server.api` Directly interface with the [Express](https://expressjs.com/) server ```typescript proxy.server.api.post('/some-outbound-api', async (req, res) => { // do whatever you would like res.status(200) res.end() }) ``` # Project Resources | Resource | Description | | ------------------------------------------ | ----------------------------------------------------------------------------- | | [CODEOWNERS](./CODEOWNERS) | Outlines the project lead(s) | | [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md) | Expected behavior for project contributors, promoting a welcoming environment | | [CONTRIBUTING.md](./CONTRIBUTING.md) | Developer guide to build, test, run, access CI, chat, discuss, file issues | | [GOVERNANCE.md](./GOVERNANCE.md) | Project governance | | [LICENSE](./LICENSE) | Apache License, Version 2.0 |