### Quick Start Commands for Local Development Source: https://tiptap.dev/docs/hocuspocus/server/extensions/s3 Commands to set up the complete development environment, test S3 configuration, and start S3 playground examples. ```bash # Set up complete development environment npm run dev:setup # Test S3 configuration npm run dev:test-s3 # Start S3 playground examples npm run playground:s3 ``` -------------------------------- ### Available Development Scripts Source: https://tiptap.dev/docs/hocuspocus/server/extensions/s3 Lists all npm scripts for environment setup, Docker services management, S3 configuration testing, and playground examples. ```bash # Environment setup npm run dev:setup # Complete setup (.env + Docker services) npm run dev:env # Create .env file from template npm run dev:services # Start Docker services (Redis + MinIO) npm run dev:services:down # Stop Docker services npm run dev:services:reset # Reset services and data # S3 configuration testing npm run dev:test-s3 # Test complete S3 configuration npm run dev:test-s3:minio # Test MinIO connection only npm run dev:test-s3:nodejs # Test Node.js S3 client only # Playground examples npm run playground:s3 # S3 extension examples (ports 8000-8003) npm run playground:s3-redis # S3 + Redis scaling examples ``` -------------------------------- ### Start the development environment Source: https://tiptap.dev/docs/hocuspocus/getting-started/contributing Start the Hocuspocus development environment and open it in your browser. ```bash $ npm run start ``` -------------------------------- ### Install required npm packages for Tiptap and Hocuspocus Source: https://tiptap.dev/docs/hocuspocus/provider/examples Run this npm install command to install all required extensions for the Tiptap collaboration setup. ```bash npm install @hocuspocus/provider @tiptap/core @tiptap/pm @tiptap/starter-kit @tiptap/extension-collaboration @tiptap/extension-collaboration-caret yjs y-prosemirror ``` -------------------------------- ### onConfigure hook example Source: https://tiptap.dev/docs/hocuspocus/server/hooks Example of using the onConfigure hook to log a message when the server is configured. ```javascript import { Server } from "@hocuspocus/server"; const server = new Server({ async onConfigure(data) { // Output some information console.log(`Server was configured!`); }, }); server.listen(); ``` -------------------------------- ### Install Throttle package Source: https://tiptap.dev/docs/hocuspocus/server/extensions/throttle Install the @hocuspocus/extension-throttle package using npm. ```bash npm install @hocuspocus/extension-throttle ``` -------------------------------- ### Install @hocuspocus/extension-database Source: https://tiptap.dev/docs/hocuspocus/server/extensions/database Install the database extension via npm. ```bash npm install @hocuspocus/extension-database ``` -------------------------------- ### Install dependencies and build packages Source: https://tiptap.dev/docs/hocuspocus/getting-started/contributing Install dependencies and build the packages for the Hocuspocus development environment. ```bash $ npm install ``` ```bash $ npm run build:packages ``` -------------------------------- ### Configure Database extension with SQLite Source: https://tiptap.dev/docs/hocuspocus/server/extensions/database Example server setup using the Database extension with SQLite. The fetch function retrieves a Y.js Uint8Array from the database, and the store function persists the binary state. Ensure the fetch returns the same Uint8Array that was saved to avoid creating a new history. ```javascript import { Server } from "@hocuspocus/server"; import { Database } from "@hocuspocus/extension-database"; import sqlite3 from "sqlite3"; const server = new Server({ extensions: [ new Database({ // Return a Promise to retrieve data … fetch: async ({ documentName }) => { return new Promise((resolve, reject) => { this.db?.get( ` SELECT data FROM "documents" WHERE name = $name ORDER BY rowid DESC `, { $name: documentName, }, (error, row) => { if (error) { reject(error); } resolve(row?.data); } ); }); }, // … and a Promise to store data: store: async ({ documentName, state }) => { this.db?.run( ` INSERT INTO "documents" ("name", "data") VALUES ($name, $data) ON CONFLICT(name) DO UPDATE SET data = $data `, { $name: documentName, $data: state, } ); }, }), ], }); server.listen(); ``` -------------------------------- ### Install @hocuspocus/extension-s3 Source: https://tiptap.dev/docs/hocuspocus/server/extensions/s3 Install the S3 extension using npm. ```bash npm install @hocuspocus/extension-s3 ``` -------------------------------- ### Install Webhook extension Source: https://tiptap.dev/docs/hocuspocus/server/extensions/webhook Install the @hocuspocus/extension-webhook package using npm. ```bash npm install @hocuspocus/extension-webhook ``` -------------------------------- ### Hocuspocus CLI S3 Usage Examples Source: https://tiptap.dev/docs/hocuspocus/server/extensions/s3 Shows various ways to use the Hocuspocus CLI with the S3 extension: basic usage, custom region and prefix, MinIO setup with forcePathStyle, and using environment variables. The environment variables S3_BUCKET, AWS_ACCESS_KEY_ID, and AWS_SECRET_ACCESS_KEY are set before running the CLI. ```bash # Basic usage hocuspocus --s3 --s3-bucket my-documents # With custom region and prefix hocuspocus --s3 --s3-bucket my-docs --s3-region eu-west-1 --s3-prefix "collab/" # MinIO setup (forcePathStyle automatically enabled) hocuspocus --s3 --s3-bucket hocuspocus-documents --s3-endpoint http://localhost:9000 # Using environment variables export S3_BUCKET=my-documents export AWS_ACCESS_KEY_ID=your-key export AWS_SECRET_ACCESS_KEY=your-secret hocuspocus --s3 ``` -------------------------------- ### Complete HocuspocusProvider awareness example Source: https://tiptap.dev/docs/hocuspocus/guides/awareness A full working example that sets up a HocuspocusProvider with Yjs, listens for awareness updates via the onAwarenessUpdate callback, and shares mouse coordinates on mousemove events. ```javascript import * as Y from 'yjs' import { HocuspocusProvider } from '@hocuspocus/provider' // Set up the provider provider = new HocuspocusProvider({ url: "ws://127.0.0.1:1234", name: "awareness-example", document: new Y.Doc(), // Listen for updates … onAwarenessUpdate: ({ states }) => { console.log(states); }, }); // For example, listen for mouse movements document.addEventListener("mousemove", (event) => { // Share any information you like provider.setAwarenessField("user", { name: "Kevin Jahns", color: "#ffcc00", mouseX: event.clientX, mouseY: event.clientY, }); }); ``` -------------------------------- ### Install Redis extension with npm Source: https://tiptap.dev/docs/hocuspocus/server/extensions/redis Install the @hocuspocus/extension-redis package using npm. ```bash npm install @hocuspocus/extension-redis ``` -------------------------------- ### onListen hook example Source: https://tiptap.dev/docs/hocuspocus/server/hooks Example of using the onListen hook to log the port the server is listening on. ```typescript import { Server } from "@hocuspocus/server"; const server = new Server({ async onListen(data) { // Output some information console.log(`Server is listening on port "${data.port}"!`); }, }); server.listen(); ``` -------------------------------- ### Server Initialization Source: https://tiptap.dev/docs/hocuspocus/server/hooks Example of creating a Hocuspocus server with an onAuthenticate hook and listening for connections. ```APIDOC ## Server Initialization ### Description Creates a new Hocuspocus server instance with an authentication hook and starts listening. ### Example ```javascript import { Server } from "@hocuspocus/server"; const server = new Server({ async onAuthenticate({ documentName, token }) { // Could be an API call, DB query or whatever … // The endpoint should return 200 OK in case the user is authenticated, and an http error // in case the user is not. return axios.get("/user", { headers: { Authorization: `Bearer ${token}`, }, }); }, }); server.listen(); ``` ``` -------------------------------- ### Start a redis server for testing Source: https://tiptap.dev/docs/hocuspocus/getting-started/contributing Run a Redis server in a Docker container for testing Hocuspocus. ```bash $ docker run -d --name hocuspocus-redis -p 6379:6379 redis:7 ``` -------------------------------- ### Install Logger package with npm Source: https://tiptap.dev/docs/hocuspocus/server/extensions/logger Install the @hocuspocus/extension-logger package using npm. ```bash npm install @hocuspocus/extension-logger ``` -------------------------------- ### Install Hocuspocus Provider and Yjs Source: https://tiptap.dev/docs/hocuspocus/provider/install Install the required npm packages: yjs and @hocuspocus/provider. ```bash npm install yjs @hocuspocus/provider ``` -------------------------------- ### Install dependencies for WebRTC collaboration Source: https://tiptap.dev/docs/hocuspocus/guides/collaborative-editing Install the required packages for collaborative editing with WebRTC: @tiptap/extension-collaboration, yjs, y-webrtc, and y-prosemirror. ```bash npm install @tiptap/extension-collaboration yjs y-webrtc y-prosemirror ``` -------------------------------- ### Basic AWS S3 Setup Source: https://tiptap.dev/docs/hocuspocus/server/extensions/s3 Configures the Hocuspocus server with the S3 extension using explicit AWS credentials and region. ```javascript import { Server } from "@hocuspocus/server"; import { S3 } from "@hocuspocus/extension-s3"; const server = new Server({ extensions: [ new S3({ bucket: 'my-documents-bucket', region: 'us-east-1', credentials: { accessKeyId: 'your-access-key', secretAccessKey: 'your-secret-key' } }), ], }); server.listen(); ``` -------------------------------- ### Install React collaboration dependencies Source: https://tiptap.dev/docs/hocuspocus/guides/collaborative-editing Install the required packages for using Hocuspocus with React and Tiptap collaboration extension. ```bash npm install @tiptap/extension-collaboration @hocuspocus/provider @hocuspocus/provider-react y-prosemirror yjs ``` -------------------------------- ### Install Vanilla JS collaboration dependencies Source: https://tiptap.dev/docs/hocuspocus/guides/collaborative-editing Install the required packages for using Hocuspocus with Vanilla JS and Tiptap collaboration extension. ```bash npm install @tiptap/extension-collaboration @hocuspocus/provider y-prosemirror yjs ``` -------------------------------- ### onConnect hook example Source: https://tiptap.dev/docs/hocuspocus/server/hooks Example of using the onConnect hook to log a message when a new websocket connection is established. ```javascript import { Server } from "@hocuspocus/server"; const server = new Server({ async onConnect(data) { // Output some information console.log(`New websocket connection`); }, }); server.listen(); ``` -------------------------------- ### Install y-indexeddb for offline support Source: https://tiptap.dev/docs/hocuspocus/guides/collaborative-editing Install the y-indexeddb package to add offline support to your collaborative editor. ```bash npm install y-indexeddb ``` -------------------------------- ### Server.listen(port, callback) Source: https://tiptap.dev/docs/hocuspocus/server/usage Starts the built-in Hocuspocus server, which spins up a webserver and a websocket server. The port can be configured via the constructor or passed to listen(). ```APIDOC ## Server.listen(port, callback) ### Description Starts the built-in Hocuspocus server. The server listens on the specified port and begins accepting WebSocket connections. ### Method listen ### Parameters - **port** (number) - Optional - The port to listen on. If not provided, uses the port from the server configuration. - **callback** (function) - Optional - A callback function invoked when the server starts. ### Request Example ```javascript import { Server } from "@hocuspocus/server"; const server = new Server({ port: 1234, }); server.listen(); ``` ### Response No return value documented. ``` -------------------------------- ### Install SQLite extension and types Source: https://tiptap.dev/docs/hocuspocus/server/extensions/sqlite Install the SQLite extension and its dependency better-sqlite3, plus the TypeScript types as a dev dependency. Since v4, better-sqlite3 replaces the unmaintained sqlite3 package. ```bash npm install @hocuspocus/extension-sqlite better-sqlite3 npm install -D @types/better-sqlite3 ``` -------------------------------- ### beforeSync hook example Source: https://tiptap.dev/docs/hocuspocus/server/hooks Shows how to use the beforeSync hook to inspect sync messages before they are handled. Note that this hook is NOT async. ```typescript import { Server } from '@hocuspocus/server' const server = new Server({ beforeSync({ payload, document, documentName, type }) { console.log(`Server will handle a sync message: "${payload}"!`) }, }) server.listen() ``` -------------------------------- ### onChange hook example with debounce and TiptapTransformer Source: https://tiptap.dev/docs/hocuspocus/server/hooks Example of using the onChange hook to save the document as JSON after a debounce of 4000ms. Uses TiptapTransformer.fromYdoc to convert the y-doc to ProseMirror JSON and writes it to a file. Not intended as primary storage; use the Database extension for that. ```javascript import { debounce } from "debounce"; import { Server } from "@hocuspocus/server"; import { TiptapTransformer } from "@hocuspocus/transformer"; import { writeFile } from "fs"; let debounced; const server = new Server({ async onChange(data) { const save = () => { // Convert the y-doc to something you can actually use in your views. // In this example we use the TiptapTransformer to get JSON from the given // ydoc. const prosemirrorJSON = TiptapTransformer.fromYdoc(data.document); // Save your document. In a real-world app this could be a database query // a webhook or something else writeFile(`/path/to/your/documents/${data.documentName}.json`, prosemirrorJSON); // Maybe you want to store the user who changed the document? // Guess what, you have access to your custom context from the // onConnect hook here. See authorization & authentication for more // details console.log(`Document ${data.documentName} changed by ${data.context.user.name}`); }; debounced?.clear(); debounced = debounce(save, 4000); debounced(); }, }); server.listen(); ``` -------------------------------- ### Register custom extension with Server Source: https://tiptap.dev/docs/hocuspocus/guides/custom-extensions Shows how to import and register the custom extension in a Hocuspocus Server instance, passing configuration options, and then start listening. ```typescript import { Server } from "@hocuspocus/server"; import { MyHocuspocusExtension } from "./extensions/my-hocuspocus-extension"; const server = new Server({ extensions: [ new MyHocuspocusExtension({ myConfigurationOption: "baz", myOptionalConfigurationOption: 1337, }), ], }); server.listen(); ``` -------------------------------- ### beforeBroadcastStateless hook example Source: https://tiptap.dev/docs/hocuspocus/server/hooks Shows how to use the beforeBroadcastStateless hook to log stateless messages before they are broadcast. ```typescript import { Server } from '@hocuspocus/server' const server = new Server({ beforeBroadcastStateless({ payload }) { console.log(`Server will broadcast a stateless message: "${payload}"!`) }, }) server.listen() ``` -------------------------------- ### onCreate response with document import Source: https://tiptap.dev/docs/hocuspocus/server/extensions/webhook Example response for the onCreate webhook. Returns documents keyed by field name to import into Hocuspocus. ```javascript response.writeHead(200, { "Content-Type": "application/json" }); response.end( JSON.stringify({ // Document for the "secondary" field secondary: {}, // Document for the "default" field default: { type: "doc", content: [ { type: "paragraph", content: [ { type: "text", text: "What is love?", }, ], }, ], }, }) ); ``` -------------------------------- ### Environment Variables for MinIO and AWS S3 Source: https://tiptap.dev/docs/hocuspocus/server/extensions/s3 Example .env file with MinIO local development configuration and commented-out production AWS S3 settings. ```env # MinIO S3 Configuration (for local development) S3_ENDPOINT=http://localhost:9000 S3_BUCKET=hocuspocus-documents S3_REGION=us-east-1 AWS_ACCESS_KEY_ID=minioadmin AWS_SECRET_ACCESS_KEY=minioadmin # Production AWS S3 Configuration # S3_BUCKET=your-production-bucket # S3_REGION=us-west-2 # AWS_ACCESS_KEY_ID=your-aws-access-key # AWS_SECRET_ACCESS_KEY=your-aws-secret-key ``` -------------------------------- ### Bind event listener with provider.on Source: https://tiptap.dev/docs/hocuspocus/provider/events Register an event listener after initialization using the provider.on method. This example binds a listener for the 'synced' event. ```javascript const provider = new HocuspocusProvider({ // … }); provider.on("synced", () => { // … }); ``` -------------------------------- ### Server upgrade: Old way with .configure() Source: https://tiptap.dev/docs/hocuspocus/getting-started/upgrade The old way to initialize the server before version 3.0. It uses Server.configure() with a port and then calls listen(). ```javascript import { Server } from "@hocuspocus/server"; const server = Server.configure({ port: 1234, }); server.listen(); ``` -------------------------------- ### Manually reconnect the websocket provider Source: https://tiptap.dev/docs/hocuspocus/provider/configuration Call connect() on the websocket provider to start a fresh round of attempts after maxAttemptsFailed, for example from a 'try again' button. ```javascript provider.configuration.websocketProvider.connect() ``` -------------------------------- ### Start Hocuspocus CLI server Source: https://tiptap.dev/docs/hocuspocus/guides/collaborative-editing Boots a minimal Hocuspocus server on port 1234 with SQLite persistence. Drop --sqlite to keep changes in memory only. ```bash npx @hocuspocus/cli --port 1234 --sqlite ``` -------------------------------- ### Run Hocuspocus CLI with various options Source: https://tiptap.dev/docs/hocuspocus/server/examples Commands to start a local Hocuspocus instance quickly. Options include setting the port, webhook URL, SQLite storage, and S3 bucket. ```bash npx @hocuspocus/cli npx @hocuspocus/cli --port 8080 npx @hocuspocus/cli --webhook http://localhost/webhooks/hocuspocus npx @hocuspocus/cli --sqlite npx @hocuspocus/cli --s3 --s3-bucket my-documents ``` -------------------------------- ### Server upgrade: Using onListen hook in configuration Source: https://tiptap.dev/docs/hocuspocus/getting-started/upgrade Shows how to add a callback using the onListen hook in the server configuration, which is the recommended way after the signature change. ```javascript import { Server } from "@hocuspocus/server"; const server = new Server({ async onListen(data) { console.log(`Server is listening on port "${data.port}"!`); }, }); server.listen() ``` -------------------------------- ### Rebuild better-sqlite3 from source Source: https://tiptap.dev/docs/hocuspocus/server/extensions/sqlite If the server throws 'Could not locate the bindings file' on start-up, rebuild the native binding from source. Requires a working C++ toolchain. ```bash npm rebuild better-sqlite3 --build-from-source ``` -------------------------------- ### onDestroy hook example Source: https://tiptap.dev/docs/hocuspocus/server/hooks Example of using the onDestroy hook to log a message when the server is shut down. ```javascript import { Server } from "@hocuspocus/server"; const server = new Server({ async onDestroy(data) { // Output some information console.log(`Server was shut down!`); }, }); server.listen(); ``` -------------------------------- ### Server upgrade: New way with new Server() Source: https://tiptap.dev/docs/hocuspocus/getting-started/upgrade The new way to initialize the server in version 3.0. It uses the new keyword with the Server class, and the import remains unchanged. ```javascript import { Server } from "@hocuspocus/server"; const server = new Server({ port: 1234, }); server.listen(); ``` -------------------------------- ### Configure Redis extension in Hocuspocus server Source: https://tiptap.dev/docs/hocuspocus/server/extensions/redis Shows how to import and configure the Redis extension with required host and port options, and optional awaitInitialSyncTimeout. The server is then started with server.listen(). ```javascript import { Server } from "@hocuspocus/server"; import { Redis } from "@hocuspocus/extension-redis"; const server = new Server({ extensions: [ new Redis({ // [required] Hostname of your Redis instance host: "127.0.0.1", // [required] Port of your Redis instance port: 6379, // [optional] Maximum time in milliseconds to wait for the current state // from another instance when loading a document. Defaults to 1000. awaitInitialSyncTimeout: 1000, }), ], }); server.listen(); ``` -------------------------------- ### Configure Tiptap editor with Y.js and WebRTC provider Source: https://tiptap.dev/docs/hocuspocus/guides/collaborative-editing Create a new Y document, register it with a WebRTC provider, and configure the Tiptap editor with the Collaboration extension. Disable StarterKit's undoRedo because Collaboration provides its own history handling. ```javascript import { Editor } from '@tiptap/core' import StarterKit from '@tiptap/starter-kit' import Collaboration from '@tiptap/extension-collaboration' import * as Y from 'yjs' import { WebrtcProvider } from 'y-webrtc' // A new Y document const ydoc = new Y.Doc() // Registered with a WebRTC provider const provider = new WebrtcProvider('example-document', ydoc) const editor = new Editor({ extensions: [ StarterKit.configure({ // The Collaboration extension comes with its own history/undoRedo handling undoRedo: false, }), // Register the document with Tiptap Collaboration.configure({ document: ydoc, }), ], }) ``` -------------------------------- ### onAwarenessUpdate example with HocuspocusProvider Source: https://tiptap.dev/docs/hocuspocus/server/hooks Example of using the onAwarenessUpdate callback in a HocuspocusProvider to capture the current awareness states. ```javascript const provider = new HocuspocusProvider({ url: "ws://127.0.0.1:1234", name: "example-document", document: ydoc, onAwarenessUpdate: ({ states }) => { currentStates = states; }, }); ``` -------------------------------- ### onConfigure Source: https://tiptap.dev/docs/hocuspocus/server/hooks The onConfigure hook is called after the server was created. It receives a payload with configuration and version information. It should return a Promise. ```APIDOC ## onConfigure ### Description The `onConfigure` hook is called after the server was created. It should return a Promise. ### Hook Payload The `data` passed to the `onConfigure` hook has the following attributes: ```typescript import { Configuration } from "@hocuspocus/server"; const data = { configuration: Configuration, version: string, instance: Hocuspocus, }; ``` ### Example ```typescript import { Server } from "@hocuspocus/server"; const server = new Server({ async onConfigure(data) { // Output some information console.log(`Server was configured!`); }, }); server.listen(); ``` ``` -------------------------------- ### Basic Hocuspocus Server Configuration Source: https://tiptap.dev/docs/hocuspocus/server/configuration Creates a new Hocuspocus server with common settings such as name, port, timeout, debounce, and websocket options. The server is started with the listen() method. ```javascript import { Server } from '@hocuspocus/server' const server = new Server({ name: 'hocuspocus-fra1-01', port: 1234, timeout: 60000, debounce: 5000, maxDebounce: 30000, quiet: true, websocketOptions: { maxPayload: 1024 * 1024 }, }) server.listen() ``` -------------------------------- ### connected hook example Source: https://tiptap.dev/docs/hocuspocus/server/hooks Example of using the connected hook to log the number of active connections after a new connection is established. ```typescript import { Server } from "@hocuspocus/server"; const server = new Server({ async connected() { console.log("connections:", server.getConnectionsCount()); }, }); server.listen(); ``` -------------------------------- ### afterUnloadDocument hook example Source: https://tiptap.dev/docs/hocuspocus/server/hooks Example of using the afterUnloadDocument hook to log when a document is closed. The hook receives data with documentName and logs it. ```javascript import { Server } from "@hocuspocus/server"; const server = new Server({ async afterUnloadDocument(data) { // Output some information console.log(`Document ${data.documentName} was closed`); }, }); server.listen(); ``` -------------------------------- ### onDisconnect hook example Source: https://tiptap.dev/docs/hocuspocus/server/hooks Example of using the onDisconnect hook to log a user's disconnection. The hook receives data with context from the onConnect hook. ```typescript import { Server } from "@hocuspocus/server"; const server = new Server({ async onDisconnect(data) { // Output some information console.log(`"${data.context.user.name}" has disconnected.`); }, }); server.listen(); ``` -------------------------------- ### beforeHandleMessage hook example Source: https://tiptap.dev/docs/hocuspocus/server/hooks Example of using beforeHandleMessage to reject a message when the token has expired. Throwing an error with a CloseEvent closes the connection with a custom reason. ```typescript import { debounce } from "debounce"; import { Server } from "@hocuspocus/server"; import { TiptapTransformer } from "@hocuspocus/transformer"; import { writeFile } from "fs"; let debounced; const server = new Server({ beforeHandleMessage(data) { if (data.context.tokenExpiresAt <= new Date()) { const error: CloseEvent = { reason: "Token expired", }; throw error; } }, }); server.listen(); ``` -------------------------------- ### MinIO + Redis for Local Development Source: https://tiptap.dev/docs/hocuspocus/server/extensions/s3 Creates a reusable server factory for local development using MinIO as the S3-compatible storage endpoint and Redis for synchronization. Uses default MinIO credentials (minioadmin) and forcePathStyle for local endpoint. ```javascript import { Server } from "@hocuspocus/server"; import { Logger } from "@hocuspocus/extension-logger"; import { Redis } from "@hocuspocus/extension-redis"; import { S3 } from "@hocuspocus/extension-s3"; // Development server setup with MinIO and Redis const createServer = (name, port) => new Server({ name, port, extensions: [ new Logger(), new Redis({ host: "127.0.0.1", port: 6379, }), new S3({ bucket: 'hocuspocus-documents', endpoint: 'http://localhost:9000', forcePathStyle: true, credentials: { accessKeyId: 'minioadmin', secretAccessKey: 'minioadmin' } }), ], }); // Start multiple instances createServer("dev-server-1", 8001).listen(); createServer("dev-server-2", 8002).listen(); ``` -------------------------------- ### onRequest hook example Source: https://tiptap.dev/docs/hocuspocus/server/hooks Example of implementing the onRequest hook to create a custom route. Rejecting the promise stops the hook chain and allows custom responses. ```typescript import { Server } from "@hocuspocus/server"; const server = new Server({ onRequest(data) { return new Promise((resolve, reject) => { const { request, response } = data; // Check if the request hits your custom route if (request.url?.split("/")[1] === "custom-route") { // Respond with your custom content response.writeHead(200, { "Content-Type": "text/plain" }); response.end("This is my custom response, yay!"); // Rejecting the promise will stop the chain and no further // onRequest hooks are run return reject(); } resolve(); }); }, }); server.listen(); ``` -------------------------------- ### Use Redis extension with multiple servers and SQLite Source: https://tiptap.dev/docs/hocuspocus/server/extensions/redis Demonstrates setting up two Hocuspocus servers with unique names, Redis for syncing, and SQLite for persistence. Each server listens on a different port. ```javascript import { Server } from "@hocuspocus/server"; import { Logger } from "@hocuspocus/extension-logger"; import { Redis } from "@hocuspocus/extension-redis"; import { SQLite } from "@hocuspocus/extension-sqlite"; // Server 1 const server = new Server({ name: "server-1", // make sure to use unique server names port: 1234, extensions: [ new Logger(), new Redis({ host: "127.0.0.1", // make sure to use the same Redis instance :-) port: 6379, }), new SQLite(), ], }); server.listen(); // Server 2 const anotherServer = new Server({ name: "server-2", port: 1235, extensions: [ new Logger(), new Redis({ host: "127.0.0.1", port: 6379, }), new SQLite(), ], }); anotherServer.listen(); ``` -------------------------------- ### onTokenSync Hook Example Source: https://tiptap.dev/docs/hocuspocus/server/hooks Example implementation of the onTokenSync hook. Validates the token, updates the connection's readOnly permission if changed, and returns a lastTokenSync timestamp. ```javascript import { Server } from "@hocuspocus/server"; const server = new Server({ async onTokenSync({ token, context, connection }) { // Validate the current token const isValid = await validateToken(token); if (!isValid) { throw new Error("Token has expired or is invalid"); } // Update permissions if changed const permissions = await getUserPermissions(context.userId); if (permissions.readOnly !== connection.readOnly) { connection.readOnly = permissions.readOnly; } return { lastTokenSync: new Date() }; }, }); server.listen(); ```