Skip to content

Commit 6ae2900

Browse files
authored
docs: hono example (#921)
1 parent eb133f4 commit 6ae2900

File tree

1 file changed

+60
-4
lines changed

1 file changed

+60
-4
lines changed

docs/server/examples.md

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,58 @@ app.ws("/collaboration", (websocket, request) => {
6161
app.listen(1234, () => console.log("Listening on http://127.0.0.1:1234"));
6262
```
6363

64-
IMPORTANT! Some extensions use the `onRequest`, `onUpgrade` and `onListen` hooks, that will not be fired in this scenario.
64+
IMPORTANT! Some extensions use the `onRequest`, `onUpgrade` and `onListen` hooks, which will not be fired in this scenario.
65+
66+
### Hono
67+
68+
Hono is a modern web framework for multiple runtimes. It's a great fit for Hocuspocus as it supports the WebSocket protocol out of the box. Only when running in Node.js, does the Hono implementation requires a bit of extra code to support the WebSocket protocol.
69+
70+
```ts
71+
import { Hono } from "hono";
72+
import { Hocuspocus } from "@hocuspocus/server";
73+
74+
// Node.js specific
75+
import { serve } from "@hono/node-server";
76+
import { createNodeWebSocket } from "@hono/node-ws";
77+
78+
// Configure Hocuspocus
79+
const hocuspocus = new Hocuspocus({
80+
//
81+
});
82+
83+
// Setup Hono server
84+
const app = new Hono();
85+
86+
// Node.js specific
87+
const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app });
88+
89+
// We mount HocusPocus in the Hono server
90+
app.get(
91+
"/hocuspocus",
92+
upgradeWebSocket((c) => ({
93+
onOpen(_evt, ws) {
94+
hocuspocus.handleConnection(ws.raw, c.req.raw as any);
95+
},
96+
}))
97+
);
98+
99+
// Start server
100+
const server = serve({
101+
fetch: app.fetch,
102+
port: 8787,
103+
}, (info) => {
104+
hocuspocus.hooks('onListen', {
105+
instance: hocuspocus,
106+
configuration: hocuspocus.configuration,
107+
port: info.port
108+
})
109+
});
110+
111+
// Setup WebSocket support (Node.js specific)
112+
injectWebSocket(server);
113+
```
114+
115+
IMPORTANT! Some extensions use the `onUpgrade` and `onRequest` hooks, which will not be fired in this scenario.
65116

66117
### Koa
67118

@@ -101,22 +152,22 @@ app.use(async (ctx, next) => {
101152
app.listen(1234);
102153
```
103154

104-
IMPORTANT! Some extensions use the `onRequest`, `onUpgrade` and `onListen` hooks, that will not be fired in this scenario.
155+
IMPORTANT! Some extensions use the `onRequest`, `onUpgrade` and `onListen` hooks, which will not be fired in this scenario.
105156

106157
### PHP / Laravel (Draft)
107158

108159
We've created a Laravel package to make integrating Laravel and Hocuspocus seamless.
109160

110161
You can find details about it here: [ueberdosis/hocuspocus-laravel](https://github.com/ueberdosis/hocuspocus-laravel)
111162

112-
113163
The primary storage for Hocuspocus must be as a Y.Doc Uint8Array binary. At the moment, there are no compatible PHP libraries to read the YJS format therefore we have two options to access the data: save the data in a Laravel compatible format such as JSON _in addition_ to the primary storage, or create a separate nodejs server with an API to read the primary storage, parse the YJS format and return it to Laravel.
114164

115165
_Note: Do not be tempted to store the Y.Doc as JSON and recreate it as YJS binary when the user connects. This will cause issues with merging of updates and content will duplicate on new connections. The data must be stored as binary to make use of the YJS format._
116166

117167
#### Saving the data in primary storage
118168

119169
Use Laravels migration system to create a table to store the YJS binaries:
170+
120171
```
121172
return new class extends Migration
122173
{
@@ -135,6 +186,7 @@ return new class extends Migration
135186
```
136187

137188
In the Hocuspocus server, you can use the dotenv library to retrieve the DB login details from `.env`:
189+
138190
```
139191
import mysql from 'mysql2';
140192
import dotenv from 'dotenv';
@@ -153,11 +205,12 @@ And then use the [database extension](/server/extensions/database) to store and
153205

154206
##### Option 1: Additionally storing the data in another format
155207

156-
Use the [webhook extension](/server/extensions/webhook) to send requests to Laravel when the document is updated, with the document in JSON format (see https://tiptap.dev/hocuspocus/guide/transformations#tiptap).
208+
Use the [webhook extension](/server/extensions/webhook) to send requests to Laravel when the document is updated, with the document in JSON format (see <https://tiptap.dev/hocuspocus/guide/transformations#tiptap>).
157209

158210
##### Option 2: Retrieve the data on demand using a separate nodejs daemon (advanced)
159211

160212
Create a nodejs server using the http module:
213+
161214
```
162215
const server = http.createServer(
163216
...
@@ -171,6 +224,7 @@ Use the dotenv package as above to retrieve the mysql login details and perform
171224
You can use the webhook extension for auth - rejecting the `onConnect` request will cause the Hocuspocus server to disconnect - however for security critical applications it is better to use a custom `onAuthenticate` hook as an attacker may be able to retrieve some data from the Hocuspocus server before The `onConnect` hooks are rejected.
172225

173226
To authenticate with the Laravel server we can use Laravel's built-in authentication system using the session cookie and a CSRF token. Add an onAuthenticate hook to your Hocuspocus server script which passes along the headers (and therefore the session cookie) and add the CSRF token to a request to the Laravel server:
227+
174228
```
175229
const hocusServer = new Server({
176230
...
@@ -194,13 +248,15 @@ const hocusServer = new Server({
194248
```
195249

196250
And add a CSRF token to the request in the provider:
251+
197252
```
198253
const provider = new HocuspocusProvider({
199254
...
200255
token: '{{ csrf_token() }}',
201256
```
202257

203258
Finally, add a route in `api.php` to respond to the request. We can respond with an empty response and just use the request status to verify the authentication (i.e. status code 200 or 403). This example uses the built-in Laravel middleware to verify the session cookie and csrf token. You can add any further middleware here as needed such as `verified` or any custom middleware:
259+
204260
```
205261
Route::middleware(['web', 'auth'])->get('/hocus', function (Request $request) {
206262
return response('');

0 commit comments

Comments
 (0)