Quickstart
Learn how to quickly get started and setup with @pluv/io.
Installation
pluv.io is broken up into multiple packages, so that you can install only what you need, particular to your codebase and framework selections.
Purpose | Location | Install command |
---|---|---|
Register websockets and custom events | Server | npm install @pluv/io yjs |
Call and listen to events. Interact with shared storage | Client | npm install @pluv/client yjs |
React-bindings for @pluv/client | Client | npm install @pluv/react yjs |
Adapter for Node.js runtime | Server | npm install @pluv/platform-node ws |
Adapter for Cloudflare Workers runtime | Server | npm install @pluv/platform-cloudflare |
Installation Example
Here is an example installation for npm, assuming you are building for Node.js, React and TypeScript.
1# For the server2npm install @pluv/io @pluv/platform-node3# Server peer-dependencies4npm install yjs ws zod56# For the client7npm install @pluv/react8# Client peer-dependencies9npm install react react-dom yjs zod
Defining a backend PluvIO instance
Let's step through how we'd put together a real-time API for Node.js. In this example, this API will define 2 type-safe events.
Create PluvIO instance
Define an io (websocket client) instance on the server codebase:
1// backend/io.ts23import { createIO } from "@pluv/io";4import { platformNode } from "@pluv/platform-node";56export const io = createIO({ platform: platformNode() });78// Export the websocket client io type, instead of the client itself9export type AppPluvIO = typeof io;
Create type-safe server events
Use io.event
to define type-safe websocket events on the io instance. The two properties on the event function are:
input
: zod validation schema that validates and casts the input for the event.resolver
: This is the implementation of the event. It accepts an input of the validated input of the incoming event, and returns an event record to emit back to the frontend client.
1// backend/io.ts23import { createIO } from "@pluv/io";4import { platformNode } from "@pluv/platform-node";5import { z } from "zod";67export const io = createIO({ platform: platformNode() })8 // When event "SEND_MESSAGE" is sent by the frontend and received9 // on the server10 .event("SEND_MESSAGE", {11 // Define a zod validation schema for the input12 input: z.object({13 message: z.string(),14 }),15 // Emit a "MESSAGE_RECEIVED" from the server to the client16 resolver: ({ message }) => ({ MESSAGE_RECEIVED: { message } }),17 })18 .event("EMIT_EMOJI", {19 input: z.object({20 emojiCode: z.number(),21 }),22 resolver: ({ emojiCode }) => ({ EMOJI_RECEIVED: { emojiCode } }),23 });2425// Export the io type instance of the io itself26export type AppPluvIO = typeof io;
Integrate PluvIO with ws
Important: Demonstration is for Node.js only.
Integrate with ws on Node.js.
1// backend/server.ts23import express from "express";4import Http from "http";5import WebSocket from "ws";6import { io } from "./io";78const PORT = 3000;910const app = express();11const server = Http.createServer();12const wsServer = new WebSocket.Server({ server });1314const parseRoomId = (url: string): string => {15 /* get room from req.url */16};1718wsServer.on("connection", async (ws, req) => {19 const roomId = parseRoomId(req.url);20 const room = io.getRoom(roomId);2122 await room.register(ws);23});2425server.listen(PORT, () => {26 console.log(`Server is listening on port: ${port}`);27});
Connecting the frontend to PluvIO
Now that the io instance is setup on the backend, we can setup the frontend client and connect the exported io type from the server.
Create the React bundle
1// frontend/io.ts23import { createBundle, createClient, y } from "@pluv/react";4import type { AppPluvIO } from "server/io";56const client = createClient<AppPluvIO>({7 // Define a ws endpoint url based on how you parse the room name8 wsEndpoint: (room) => `ws://localhost:3000/api/room/${room}`,9});1011export const {12 // factories13 createRoomBundle,1415 // components16 PluvProvider,1718 // hooks19 usePluvClient,20} = createBundle(client);2122export const {23 // components24 MockedRoomProvider,25 PluvRoomProvider,2627 // hooks28 usePluvBroadcast,29 usePluvConnection,30 usePluvEvent,31 usePluvMyPresence,32 usePluvMyself,33 usePluvOther,34 usePluvOthers,35 usePluvRoom,36 usePluvStorage,37} = createRoomBundle();
Wrap with your pluv.io providers
Wrap your component with PluvRoomProvider
to connect to a realtime room and enable the rest of your room react hooks.
1// frontend/Room.tsx23import { FC } from "react";4import { PluvRoomProvider } from "./io";5import { ChatRoom } from "./ChatRoom";67export const Room: FC = () => {8 return (9 <PluvRoomProvider room="my-example-room">10 <ChatRoom />11 </PluvRoomProvider>12 );13};
Send and receive events
Use usePluvBroadcast
and usePluvEvent
to send and receive type-safe events in your React component.
1// frontend/ChatRoom.tsx23import { FC, useCallback, useState } from "react";4import { emojiMap } from "./emojiMap";5import { usePluvBroadcast, usePluvEvent } from "./io";67export const ChatRoom: FC = () => {8 const broadcast = usePluvBroadcast();910 const [messages. setMessages] = useState<string[]>([]);1112 usePluvEvent("MESSAGE_RECEIVED", ({ data }) => {13 // ^? (property) data: { message: string }14 setMessages((prev) => [...prev, data.message]);15 });1617 usePluvEvent("EMOJI_RECEIVED", ({ data }) => {18 // ^? (property) data: { emojiCode: number }19 const emoji = emojiMap[data.emojiCode];2021 console.log(emoji);22 });2324 const onMessage = useCallback((message: string): void => {25 // 2nd parameter will be statically typed from server/io.ts26 broadcast("SEND_MESSAGE", { message });27 }, [broadcast]);2829 const onEmoji = useCallback((emojiCode: number): void => {30 broadcast("EMIT_EMOJI", { emojiCode });31 }, [broadcast]);3233 // ...34};
Next steps
This example only scratches the surface of the realtime capabilities offered by pluv.io.