jianyi-gronk commented on code in PR #395:
URL: https://github.com/apache/dubbo-js/pull/395#discussion_r1605658103


##########
docs/guide/dubboForWEB/GeneratingCode.md:
##########
@@ -1,2 +1,125 @@
 # Generating code
-# todo
+
+We mentioned earlier that the ELIZA service defines a Protocol Buffer schema. 
So what *is* that schema? It is really just a simple file that describes the 
service, its methods, and their argument and return types:
+
+```protobuf
+syntax = "proto3";
+
+service ElizaService {
+  rpc Say(SayRequest) returns (SayResponse) {}
+}
+
+message SayRequest {
+  string sentence = 1;
+}
+
+message SayResponse {
+  string sentence = 1;
+}
+```
+
+You can see the full version including comments and some additional RPCs [on 
the Buf Schema 
Registry](https://buf.build/connectrpc/eliza/file/main:connectrpc/eliza/v1/eliza.proto)
 (BSR). The `rpc` keyword stands for Remote Procedure Call — a method you can 
invoke remotely. The schema is the contract between server and client, and it 
precisely defines how data is exchanged down to the very details of 
serialization.

Review Comment:
   Modify link



##########
docs/guide/dubboForWEB/UsingClients.md:
##########
@@ -1,2 +1,102 @@
-# Using Clients
-# todo
+# Using clients
+
+Dubbo ships with two client shapes for TypeScript, one with classic callbacks, 
one that is promise based. You create a client for a service using one of the 
provided constructor functions, and you do not need to generate additional code.
+
+## Promises
+
+We have already been using the function `createPromiseClient` in the tutorial. 
The function gives us a client that uses ECMAScript promise objects. In 
combination with the `await` keyword, this lets you write asynchronous code in 
a natural and easily readable way:
+
+```ts
+import { createPromiseClient } from "@apachedubbo/dubbo";
+import { ElizaService } from "../gen/buf/connect/demo/eliza/v1/eliza_dubbo.js";

Review Comment:
   Need to modify



##########
docs/guide/dubboForWEB/Choosingaprotocol.md:
##########
@@ -1 +1,71 @@
 # Choosing a protocol
+
+In addition to the Dubbo protocol, Dubbo ships with support for the gRPC-web 
protocol. If your backend does not support the Dubbo protocol, you can still 
use Dubbo clients to interface with it.
+
+## Connect

Review Comment:
   Triple



##########
docs/guide/dubboForWEB/GeneratingCode.md:
##########
@@ -1,2 +1,125 @@
 # Generating code
-# todo
+
+We mentioned earlier that the ELIZA service defines a Protocol Buffer schema. 
So what *is* that schema? It is really just a simple file that describes the 
service, its methods, and their argument and return types:
+
+```protobuf
+syntax = "proto3";
+
+service ElizaService {
+  rpc Say(SayRequest) returns (SayResponse) {}
+}
+
+message SayRequest {
+  string sentence = 1;
+}
+
+message SayResponse {
+  string sentence = 1;
+}
+```
+
+You can see the full version including comments and some additional RPCs [on 
the Buf Schema 
Registry](https://buf.build/connectrpc/eliza/file/main:connectrpc/eliza/v1/eliza.proto)
 (BSR). The `rpc` keyword stands for Remote Procedure Call — a method you can 
invoke remotely. The schema is the contract between server and client, and it 
precisely defines how data is exchanged down to the very details of 
serialization.
+
+The schema comes to life by generating code. For the server, an interface is 
generated, and the developer can focus on filling the methods with business 
logic. For the client, there really isn't anything to do — the developer can 
just call the client methods, rely on the generated types for compile-time 
type-safety and serialization, and focus on the application logic.
+
+## Generated SDKs
+
+In the tutorial, we have been using [generated 
SDKs](https://buf.build/docs/bsr/generated-sdks/npm) with an `npm install` 
command. When the package was requested on the BSR NPM registry, it ran the 
schema through a code generator, and served the generated files as a package 
with all required dependencies.
+
+If you want to use a Dubbo or gRPC service whose schema is published on the 
BSR, you can simply use `npm` to install the package, and hit the service with 
a Dubbo client.
+
+See our [documentation on generated 
SDKs](https://buf.build/docs/bsr/generated-sdks/overview) for details.
+
+## Local generation
+
+We're going to generate our code using [Buf](https://buf.build/product/cli/), 
a modern replacement for Google's protobuf compiler, and two compiler plugins:
+
+- 
[@apachedubbo/protoc-gen-apache-dubbo-es](https://www.npmjs.com/package/@apachedubbo/protoc-gen-apache-dubbo-es)
 — generates services from your Protocol Buffer schema
+- 
[@bufbuild/protoc-gen-es](https://www.npmjs.com/package/@bufbuild/protoc-gen-es)
 — generates base types, like request and response messages
+
+The code we will generate has three runtime dependencies:
+
+- [@apachedubbo/dubbo](https://www.npmjs.com/package/@apachedubbo/dubbo) — 
provides clients, interceptors, errors, and other primitives for Dubbo
+- 
[@apachedubbo/dubbo-web](https://www.npmjs.com/package/@apachedubbo/dubbo-web) 
— provides the Dubbo and gRPC-web protocols for web browsers
+- [@bufbuild/protobuf](https://www.npmjs.com/package/@bufbuild/protobuf) — 
provides serialization and more for the base types
+
+First, let's install `buf`, the plugins and runtime dependencies:
+
+```bash
+$ npm install --save-dev @bufbuild/buf @apachedubbo/protoc-gen-apache-dubbo-es 
@bufbuild/protoc-gen-es
+$ npm install @apachedubbo/dubbo @apachedubbo/dubbo @bufbuild/protobuf
+```
+
+Next, tell Buf to use the two plugins with a new configuration file:
+
+```yaml
+# buf.gen.yaml defines a local generation template.
+# For details, see https://buf.build/docs/configuration/v1/buf-gen-yaml
+version: v1
+plugins:
+  # This will invoke protoc-gen-es and write output to src/gen
+  - plugin: es
+    out: src/gen
+    opt:
+      # Add more plugin options here
+      - target=ts
+  # This will invoke protoc-gen-apache-dubbo-es
+  - plugin: apache-dubbo-es
+    out: src/gen
+    opt:
+      # Add more plugin options here
+      - target=ts
+```
+
+If desired, you can also skip local plugin installation and use [remote 
plugins](https://buf.build/docs/bsr/remote-plugins/overview). 
+
+Finally, tell Buf to generate code for the ELIZA schema:
+
+```bash
+$ npx buf generate buf.build/apache-dubbo/eliza
+```
+
+If you prefer, you can use `protoc` instead of Buf — the plugins behave like 
any other plugin.
+
+### Output
+
+Let's take a peek at what was generated. There are two new files:
+
+- `src/gen/apache-dubbo/eliza/v1/eliza_dubbo.ts`
+- `src/gen/apache-dubbo/eliza/v1/eliza_pb.ts`
+
+The first file was generated by `protoc-gen-apache-dubbo-es` and contains the 
service:
+
+```ts
+import { SayRequest, SayResponse } from "./eliza_pb.js";
+import { MethodKind } from "@bufbuild/protobuf";
+
+export const ElizaService = {
+  typeName: "buf.connect.demo.eliza.v1.ElizaService",

Review Comment:
   Need to modify



##########
docs/guide/dubboForWEB/UsingClients.md:
##########
@@ -1,2 +1,102 @@
-# Using Clients
-# todo
+# Using clients
+
+Dubbo ships with two client shapes for TypeScript, one with classic callbacks, 
one that is promise based. You create a client for a service using one of the 
provided constructor functions, and you do not need to generate additional code.
+
+## Promises
+
+We have already been using the function `createPromiseClient` in the tutorial. 
The function gives us a client that uses ECMAScript promise objects. In 
combination with the `await` keyword, this lets you write asynchronous code in 
a natural and easily readable way:
+
+```ts
+import { createPromiseClient } from "@apachedubbo/dubbo";
+import { ElizaService } from "../gen/buf/connect/demo/eliza/v1/eliza_dubbo.js";
+
+const client = createPromiseClient(ElizaService, transport);
+
+const res = await client.say({
+  sentence: "I feel happy.",
+});
+console.log(res.sentence);
+```
+
+For server-streaming RPCs, the corresponding method on the client will return 
an async iterable stream of response messages that can be used with the `for 
await...of` statement:
+
+```ts
+for await (const res of client.introduce({ name: "Joseph" })) {
+  console.log(res);
+}
+```
+
+## Callbacks
+
+If you prefer a callback-based approach, the client returned by the function 
`createCallbackClient` should suit you:
+
+```ts
+import { createCallbackClient } from "@apachedubbo/dubbo";
+import { ElizaService } from "../gen/buf/connect/demo/eliza/v1/eliza_dubbo.js";

Review Comment:
   Need to modify



##########
docs/guide/dubboForWEB/Interceptors.md:
##########
@@ -1 +1,164 @@
 # Interceptors
+
+An interceptor can add logic to clients, similar to the decorators or 
middleware you may have seen in other libraries. Interceptors may mutate the 
request and response, catch errors and retry/recover, emit logs, or do nearly 
anything else.
+
+For a simple example, this interceptor logs all requests:
+
+```ts
+import { Interceptor } from "@apachedubbo/dubbo";
+import { createDubboTransport } from "@apachedubbo/dubbo-web"
+
+const logger: Interceptor = (next) => async (req) => {
+  console.log(`sending message to ${req.url}`);
+  return await next(req);
+};
+
+createDubboTransport({
+  baseUrl: "http://localhost:8080";,
+  interceptors: [logger],
+});
+```
+You can think of interceptors like a layered onion. A request initiated by a 
client goes through the outermost layer first. Each call to `next()` traverses 
to the next layer. In the center, the actual HTTP request is run by the 
transport. The response then comes back through all layers and is returned to 
the client. In the array of interceptors passed to the transport, the 
interceptor at the end of the array is applied first.
+
+To intercept responses, we simply look at the return value of `next()`:
+
+```ts
+const logger: Interceptor = (next) => async (req) => {
+  console.log(`sending message to ${req.url}`);
+  const res = await next(req);
+  if (!res.stream) {
+    console.log("message:", res.message);
+  }
+  return res;
+};
+```
+
+The `stream` property of the response tells us whether this is a streaming 
response. A streaming response has not fully arrived yet when we intercept it — 
we have to wrap it to see individual messages:
+
+```ts
+const logger: Interceptor = (next) => async (req) => {
+  const res = await next(req);
+  if (res.stream) {
+    // to intercept streaming response messages, we wrap
+    // the AsynchronousIterable with a generator function
+    return {
+      ...res,
+      message: logEach(res.message),
+    };
+  }
+  return res;
+};
+
+async function* logEach(stream: AsyncIterable<any>) {
+  for await (const m of stream) {
+    console.log("message received", m);
+    yield m;
+  }
+}
+```
+
+# Context values
+
+Context values are a type safe way to pass arbitary values from the call site 
or from one interceptor to the next. You can use `createContextValues` function 
to create a new `ContextValues`. The `contextValues` call option can be used to 
provide a `ContextValues` instance for each request.
+
+`ContextValues` has methods to set, get, and delete values. The keys are 
`ContextKey` objects:
+
+## Context Keys
+
+`ContextKey` is a type safe and collision free way to use context values. It 
is defined using `createContextKey` function which takes a default value and 
returns a `ContextKey` object. The default value is used when the context value 
is not set.
+
+```ts
+import { createContextKey } from "@apachedubbo/dubbo";
+
+type User = { name: string };
+
+const kUser = createContextKey<User>(
+  { name: "Anonymous" }, // Default value
+  {
+    description: "Current user", // Description useful for debugging
+  },
+);
+
+export { kUser };
+```
+
+For values where a default doesn't make sense you can just modify the type:
+
+```ts
+import { createContextKey } from "@apachedubbo/dubbo";
+
+type User = { name: string };
+
+const kUser = createContextKey<User | undefined>(undefined, {
+  description: "Authenticated user",
+});
+
+export { kUser };
+```
+
+It is best to define context keys in a separate file and export them. This is 
better for code splitting and also avoids circular imports. This also helps in 
the case where the provider changes based on the environment.
+
+## Example
+
+Let's say you want to log the response body. But you don't want to do it for 
every request. You only want to do it from a specific component. You can use 
context values to achieve this.
+
+First create a context key:
+
+```ts
+import { createContextKey } from "@apachedubbo/dubbo";
+
+const kLogBody = createContextKey<boolean>(false, {
+  description: "Log request/response body",
+});
+
+export { kLogBody };
+```
+
+Then in your interceptor, check the context value:
+
+```ts
+import type { Interceptor } from "@apachedubbo/dubbo";
+import { kLogBody } from "./log-body-context.js";
+
+const logger: Interceptor = (next) => async (req) => {
+  console.log(`sending message to ${req.url}`);
+  const res = await next(req);
+  if (!res.stream && req.contextValues.get(kLogBody)) {
+    console.log("message:", res.message);
+  }
+  return res;
+};
+```
+
+Then in your component, set the context value:
+
+```ts
+import { kLogBody } from "./log-body-context.js";
+import { elizaClient } from "./eliza-client.js";
+
+const res = elizaClient.say({ sentence: "Hey!" }, { contextValues: 
createContextValues().set(kLogBody, true) });
+```
+
+# Setting `fetch()` options
+
+Another valuable use case for interceptors is customizing the Fetch API for 
individual requests by leveraging the `request.init` object.
+
+For example, by default, Dubbo sets the Fetch option 
[redirect](https://developer.mozilla.org/en-US/docs/Web/API/fetch#redirect) to 
`error`, which means that a network error will be returned when a request is 
met with a redirect. However, if you wish to change this value to `follow` for 
example, you can do so using an interceptor.
+
+```ts
+const followRedirects: Interceptor = (next) => async (request) => {
+  return await next({
+    ...request,
+    init: {
+      ...request.init,
+      // Follow all redirects
+      redirect: "follow",
+    },
+  });
+};
+
+const client = createPromiseClient(ElizaService, createConnectTransport({

Review Comment:
   not createConnectTransport



##########
docs/guide/dubboForWEB/UsingClients.md:
##########
@@ -1,2 +1,102 @@
-# Using Clients
-# todo
+# Using clients
+
+Dubbo ships with two client shapes for TypeScript, one with classic callbacks, 
one that is promise based. You create a client for a service using one of the 
provided constructor functions, and you do not need to generate additional code.
+
+## Promises
+
+We have already been using the function `createPromiseClient` in the tutorial. 
The function gives us a client that uses ECMAScript promise objects. In 
combination with the `await` keyword, this lets you write asynchronous code in 
a natural and easily readable way:
+
+```ts
+import { createPromiseClient } from "@apachedubbo/dubbo";
+import { ElizaService } from "../gen/buf/connect/demo/eliza/v1/eliza_dubbo.js";
+
+const client = createPromiseClient(ElizaService, transport);
+
+const res = await client.say({
+  sentence: "I feel happy.",
+});
+console.log(res.sentence);
+```
+
+For server-streaming RPCs, the corresponding method on the client will return 
an async iterable stream of response messages that can be used with the `for 
await...of` statement:
+
+```ts
+for await (const res of client.introduce({ name: "Joseph" })) {
+  console.log(res);
+}
+```
+
+## Callbacks
+
+If you prefer a callback-based approach, the client returned by the function 
`createCallbackClient` should suit you:
+
+```ts
+import { createCallbackClient } from "@apachedubbo/dubbo";
+import { ElizaService } from "../gen/buf/connect/demo/eliza/v1/eliza_dubbo.js";
+
+const client = createCallbackClient(ElizaService, transport);
+
+client.say({ sentence: "I feel happy." }, (err, res) => {
+  if (!err) {
+    console.log(res.sentence);
+  }
+});
+```
+
+For server-streaming RPCs, the corresponding method on the client takes two 
callback functions: one that is called every time a response message arrives, 
and one that is called at the end of the stream.
+
+```ts
+import {DubboError} from "@apachedubbo/dubbo";
+
+client.introduce({name: "Joseph"}, (res) => {
+  console.log(res);
+}, (err?: DubboError) => {
+  if (err) {
+    console.error(err);
+  }
+});
+```
+
+The callback client is particularly useful if you want to migrate an existing 
code base from gRPC-web to Connect clients.

Review Comment:
   Connect -> connect



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to