1 | # fusion-plugin-rpc
|
2 |
|
3 | [![Build status](https://badge.buildkite.com/5165e82185b13861275cd0a69f29c2a13bc66dfb9461ee4af5.svg?branch=master)](https://buildkite.com/uberopensource/fusion-plugin-rpc)
|
4 |
|
5 | Fetch data on the server and client with an
|
6 | [RPC](https://en.wikipedia.org/wiki/Remote_procedure_call) style interface.
|
7 |
|
8 | RPC is a natural way of expressing that a server-side function should be run in
|
9 | response to a client-side function call. Unlike
|
10 | [RESTful architectures](https://en.wikipedia.org/wiki/Representational_state_transfer),
|
11 | RPC-based architectures are not required to conform to statelessness constraints
|
12 | and are free to return session-scoped data. Additionally, the semantics of RPC
|
13 | calls are not constrained by the availability of suitably-descriptive HTTP
|
14 | methods and RPC calls can express complex state change requests more naturally
|
15 | as verbs (e.g. `returnProduct(id)`) rather than object-orientation (e.g.
|
16 | `PATCH /api/orders/:id`).
|
17 |
|
18 | If you're using React/Redux, you should use
|
19 | [`fusion-plugin-rpc-redux-react`](https://github.com/fusionjs/fusion-plugin-rpc-redux-react)
|
20 | instead of this package.
|
21 |
|
22 | ---
|
23 |
|
24 | ### Table of contents
|
25 |
|
26 | * [Installation](#installation)
|
27 | * [Usage](#usage)
|
28 | * [Setup](#setup)
|
29 | * [API](#api)
|
30 | * [Registration API](#registration-api)
|
31 | * [Dependencies](#dependencies)
|
32 | * [Service API](#service-api)
|
33 | * [`mock`](#mock)
|
34 |
|
35 | ---
|
36 |
|
37 | ### Installation
|
38 |
|
39 | ```
|
40 | yarn add fusion-plugin-rpc
|
41 | ```
|
42 |
|
43 | ---
|
44 |
|
45 | ### Usage
|
46 |
|
47 | ```js
|
48 | import {createPlugin} from 'fusion-core';
|
49 | export default createPlugin({
|
50 | deps: {RPC: RPCToken},
|
51 | middleware: ({RPCFactory}) => (ctx, next) => {
|
52 | RPC.from(ctx).request('getUser', 1).then(console.log);
|
53 | }
|
54 | );
|
55 | ```
|
56 |
|
57 | ---
|
58 |
|
59 | ### Setup
|
60 |
|
61 | ```js
|
62 | // src/main.js
|
63 | import React from 'react';
|
64 | import App, {createPlugin} from 'fusion-core';
|
65 | import RPC, {RPCToken, RPCHandlersToken, ResponseError} from 'fusion-plugin-rpc';
|
66 | import UniversalEvents, {UniversalEventsToken} from 'fusion-plugin-universal-events';
|
67 | import {FetchToken} from 'fusion-tokens';
|
68 | import fetch from 'unfetch';
|
69 |
|
70 | // Define your rpc methods server side
|
71 | const handlers = __NODE__ && {
|
72 | getUser: async (args, ctx) => {
|
73 | return {some: 'data' + args};
|
74 | },
|
75 | test: async (args, ctx) => {
|
76 | // Error Handling Example
|
77 | try {
|
78 | doThing();
|
79 | } catch(e) {
|
80 | const error = new ResponseError('Failed to do thing');
|
81 | error.code = 'DOTHING';
|
82 | error.meta = {
|
83 | custom: 'metadata'
|
84 | };
|
85 | throw error;
|
86 | }
|
87 | }
|
88 | };
|
89 |
|
90 | export default () => {
|
91 | const app = new App(<div></div>);
|
92 |
|
93 | app.register(RPCToken, RPC);
|
94 | app.register(UniversalEventsToken, UniversalEvents);
|
95 | __NODE__
|
96 | ? app.register(RPCHandlersToken, handlers);
|
97 | : app.register(FetchToken, fetch);
|
98 |
|
99 | return app;
|
100 | }
|
101 | ```
|
102 |
|
103 | ---
|
104 |
|
105 | ### API
|
106 |
|
107 | #### Registration API
|
108 |
|
109 | ##### RPC
|
110 |
|
111 | ```js
|
112 | import RPC from 'fusion-plugin-rpc';
|
113 | ```
|
114 |
|
115 | The RPC plugin. Provides the RPC [service API](#service-api).
|
116 |
|
117 | ###### `RPCToken`
|
118 |
|
119 | ```js
|
120 | import {RPCToken} from 'fusion-plugin-rpc-redux-react';
|
121 | ```
|
122 |
|
123 | The canonical token for the RPC plugin. Typically, it should be registered with
|
124 | the [RPC](#rpc) plugin.
|
125 |
|
126 | #### Dependencies
|
127 |
|
128 | ##### `UniversalEventsToken`
|
129 |
|
130 | Required. See
|
131 | [https://github.com/fusionjs/fusion-plugin-universal-events#api](https://github.com/fusionjs/fusion-plugin-universal-events#api)
|
132 |
|
133 | ##### `RPCHandlersToken`
|
134 |
|
135 | ```js
|
136 | import {RPCHandlersToken} from 'fusion-plugin-rpc-redux-react';
|
137 | ```
|
138 |
|
139 | Configures what RPC handlers exist. Required. Server-only.
|
140 |
|
141 | ###### Types
|
142 |
|
143 | ```flow
|
144 | type RPCHandlers = Object<string, () => any>
|
145 | ```
|
146 |
|
147 | You can register a value of type `RPCHandlers` or a Plugin that provides a value
|
148 | of type `RPCHandlers`.
|
149 |
|
150 | ##### `FetchToken`
|
151 |
|
152 | Required. Browser-only. See
|
153 | [https://github.com/fusionjs/fusion-tokens#fetchtoken](https://github.com/fusionjs/fusion-tokens#fetchtoken)
|
154 |
|
155 | ##### `ReduxToken`
|
156 |
|
157 | Required. See
|
158 | [https://github.com/fusionjs/fusion-plugin-react-redux](https://github.com/fusionjs/fusion-plugin-react-redux)
|
159 |
|
160 | ##### `ReducerToken`
|
161 |
|
162 | Required. See
|
163 | [https://github.com/fusionjs/fusion-plugin-react-redux](https://github.com/fusionjs/fusion-plugin-react-redux)
|
164 |
|
165 | ---
|
166 |
|
167 | #### Service API
|
168 |
|
169 | ```js
|
170 | const rpc: RPC = Rpc.from((ctx: Context));
|
171 | ```
|
172 |
|
173 | * `ctx: Context` - Required. A
|
174 | [Fusion.js context](https://github.com/fusionjs/fusion-core#context)
|
175 | * returns `rpc: {request: (method: string, args: any) => Promise<any>}`
|
176 |
|
177 | * `request: (method: string, args: any) => Promise<any>` - Makes an RPC call
|
178 | via an HTTP request. If on the server, this will directly call the `method`
|
179 | handler with `(args, ctx)`.
|
180 |
|
181 | If on the browser, this will `POST` to `/api/${method}` endpoint with JSON
|
182 | serialized args as the request body. The server will then deserialize the
|
183 | args and call the rpc handler. The response will be serialized and send back
|
184 | to the browser.
|
185 |
|
186 | * `method: string` - Required. The RPC method name
|
187 | * `args: any` - Optional. Arguments to pass to the server-side RPC handler.
|
188 | Must be JSON-serializable.
|
189 |
|
190 | ### mock
|
191 |
|
192 | The package also exports a mock rpc plugin which can be useful for testing. For
|
193 | example:
|
194 |
|
195 | ```js
|
196 | import {mock as MockRPC} from 'fusion-plugin-rpc';
|
197 | ```
|
198 |
|
199 | The package also exports a mock RPC plugin which can be useful for testing. For
|
200 | example:
|
201 |
|
202 | ```js
|
203 | app.register(RPCToken, mock);
|
204 | ```
|
205 |
|
206 | ### Error Handling
|
207 |
|
208 | Use the `ResponseError` error subclass for sending error responses. If this
|
209 | error class is not used, a generic message will be sent to the client.
|
210 |
|
211 | ```js
|
212 | import {ResponseError} from 'fusion-plugin-rpc';
|
213 |
|
214 | function testHandler() {
|
215 | try {
|
216 | doThing();
|
217 | } catch (e) {
|
218 | const error = new ResponseError('Failed to do thing');
|
219 | error.code = 'DOTHING';
|
220 | error.meta = {
|
221 | custom: 'metadata',
|
222 | };
|
223 | throw error;
|
224 | }
|
225 | }
|
226 | ```
|