grpc-web-hacker-news

所属分类:微服务
开发工具:TypeScript
文件大小:0KB
下载次数:0
上传日期:2023-03-06 22:33:40
上 传 者sh-1993
说明:  一个示例应用程序,在React中使用gRPC Web和Go后端实现黑客新闻阅读器,
(An example app implementing a Hacker News reader in React with gRPC-Web and Go backend,)

文件列表:
.editorconfig (393, 2022-02-15)
LICENSE (1069, 2022-02-15)
app/ (0, 2022-02-15)
app/index.html (472, 2022-02-15)
app/index.tsx (348, 2022-02-15)
app/src/ (0, 2022-02-15)
app/src/Stories.tsx (1998, 2022-02-15)
app/src/StoryList.tsx (1026, 2022-02-15)
app/src/StoryView.tsx (460, 2022-02-15)
app/src/actions/ (0, 2022-02-15)
app/src/actions/index.ts (94, 2022-02-15)
app/src/actions/stories.ts (1607, 2022-02-15)
app/src/index.css (63, 2022-02-15)
app/src/middleware/ (0, 2022-02-15)
app/src/middleware/grpc.ts (3651, 2022-02-15)
app/src/proto/ (0, 2022-02-15)
app/src/proto/hackernews_pb.d.ts (2882, 2022-02-15)
app/src/proto/hackernews_pb.js (16929, 2022-02-15)
app/src/proto/hackernews_pb_service.js (2047, 2022-02-15)
app/src/proto/hackernews_pb_service.ts (634, 2022-02-15)
app/src/reducers/ (0, 2022-02-15)
app/src/reducers/stories.js (1459, 2022-02-15)
app/src/reducers/stories.ts (1127, 2022-02-15)
app/src/store.ts (454, 2022-02-15)
go.mod (903, 2022-02-15)
go.sum (54565, 2022-02-15)
package-lock.json (201868, 2022-02-15)
package.json (987, 2022-02-15)
proto/ (0, 2022-02-15)
proto/hackernews.proto (382, 2022-02-15)
protoc.sh (254, 2022-02-15)
screenshots/ (0, 2022-02-15)
... ...

## grpc-web-hacker-news An example app implementing a Hacker News reader. This example aims to demonstrate usage of [grpc-web](https://github.com/improbable-eng/grpc-web)(v0.5.0) with React. It additionally shows how to integrate with Redux. ### Running To start both the Go backend server and the frontend React application, run the following: ```bash ./start.sh ``` ![Screenshot](https://raw.githubusercontent.com/easyCZ/grpc-web-hacker-news/master/screenshots/grpc-web-hacker-news.png) The backend server is running on `http://localhost:8900` while the frontend will by default start on `http://localhost:3000` ## Contributing Contributions are welcome, please open an Issue or raise a PR. ## Notable setup points ### Disable TSLint for protobuf generated classes Gernerated proto classes do not confirm to TS lint requirements, [disable linting](https://github.com/easyCZ/grpc-web-hacker-news/blob/master/app/tslint.json#L4) ```json { "linterOptions": { "exclude": [ "src/proto/*" ] }, } ``` ### Configure protobuf compiler script In this example, we're using a `protoc.sh` script to aid compilation ```bash protoc \ --go_out=plugins=grpc:./server \ --plugin=protoc-gen-ts=./app/node_modules/.bin/protoc-gen-ts \ --ts_out=service=true:./app/src \ --js_out=import_style=commonjs,binary:./app/src \ ./proto/hackernews.proto ``` ### Generated proto classes with Redux Redux favors plain objects over object instances. This complicates usage of the generated proto classes, in this example class [`Story`](https://github.com/easyCZ/grpc-web-hacker-news/blob/master/app/src/proto/hackernews_pb.d.ts#L6). In order to use the generated classes with redux, we must use the `Story.AsObject` type which is the plain object representation. For example our reducer should look like this: ```js export type StoryState = { readonly stories: { [storyId: number]: Story.AsObject }, readonly error: Error | null, readonly loading: boolean, readonly selected: Story.AsObject | null, }; export default function (state: StoryState = initialState, action: RootAction): StoryState { switch (action.type) { case ADD_STORY: const story: Story.AsObject = action.payload.toObject(); const selected = state.selected !== null ? state.selected : story; if (story.id && story.id) { return { ...state, loading: false, stories: {...state.stories, [story.id]: story}, selected, }; } return state; default: return state; } } ``` Note the usage of `Story.AsObject` rather than just `Story` ### gRPC-Web Redux Middleware An [example redux middleware is included](https://github.com/easyCZ/grpc-web-hacker-news/blob/master/app/src/middleware/grpc.ts). ```js import { Action, Dispatch, Middleware, MiddlewareAPI } from 'redux'; import { Code, grpc, Metadata, Transport } from 'grpc-web-client'; import * as jspb from 'google-protobuf'; const GRPC_WEB_REQUEST = 'GRPC_WEB_REQUEST'; // const GRPC_WEB_INVOKE = 'GRPC_WEB_INVOKE'; // Descriptor of a grpc-web payload // life-cycle methods mirror grpc-web but allow for an action to be dispatched when triggered export type GrpcActionPayload = { // The method descriptor to use for a gRPC request, equivalent to grpc.invoke(methodDescriptor, ...) methodDescriptor: grpc.MethodDefinition, // The transport to use for grpc-web, automatically selected if empty transport?: Transport, // toggle debug messages debug?: boolean, // the URL of a host this request should go to host: string, // An instance of of the request message request: RequestType, // Additional metadata to attach to the request, the same as grpc-web metadata?: Metadata.ConstructorArg, // Called immediately before the request is started, useful for toggling a loading status onStart?: () => Action | void, // Called when response headers are received onHeaders?: (headers: Metadata) => Action | void, // Called on each incoming message onMessage?: (res: ResponseType) => Action | void, // Called at the end of a request, make sure to check the exit code onEnd: (code: Code, message: string, trailers: Metadata) => Action | void, }; // Basic type for a gRPC Action export type GrpcAction = { type: typeof GRPC_WEB_REQUEST, payload: GrpcActionPayload, }; // Action creator, Use it to create a new grpc action export function grpcRequest( payload: GrpcActionPayload ): GrpcAction { return { type: GRPC_WEB_REQUEST, payload, }; } export function newGrpcMiddleware(): Middleware { return ({getState, dispatch}: MiddlewareAPI<{}>) => (next: Dispatch<{}>) => (action: any) => { // skip non-grpc actions if (!isGrpcWebUnaryAction(action)) { return next(action); } const payload = action.payload; if (payload.onStart) { payload.onStart(); } grpc.invoke(payload.methodDescriptor, { debug: payload.debug, host: payload.host, request: payload.request, metadata: payload.metadata, transport: payload.transport, onHeaders: headers => { if (!payload.onHeaders) { return; } const actionToDispatch = payload.onHeaders(headers); return actionToDispatch && dispatch(actionToDispatch); }, onMessage: res => { if (!payload.onMessage) { return; } const actionToDispatch = payload.onMessage(res); return actionToDispatch && dispatch(actionToDispatch); }, onEnd: (code, msg, trailers) => { const actionToDispatch = payload.onEnd(code, msg, trailers); return actionToDispatch && dispatch(actionToDispatch); }, }); return next(action); }; } function isGrpcWebUnaryAction(action: any): action is GrpcAction { return action && action.type && action.type === GRPC_WEB_REQUEST && isGrpcWebPayload(action); } function isGrpcWebPayload(action: any): boolean { return action && action.payload && action.payload.methodDescriptor && action.payload.request && action.payload.onEnd && action.payload.host; } ```

近期下载者

相关文件


收藏者