Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/makay11/rpc
An RPC library for quick development of seamless full-stack applications.
https://github.com/makay11/rpc
real-time rpc server-sent-events sse subscriptions typescript
Last synced: about 2 months ago
JSON representation
An RPC library for quick development of seamless full-stack applications.
- Host: GitHub
- URL: https://github.com/makay11/rpc
- Owner: Makay11
- License: mpl-2.0
- Created: 2024-03-17T00:06:08.000Z (10 months ago)
- Default Branch: main
- Last Pushed: 2024-11-20T18:20:17.000Z (2 months ago)
- Last Synced: 2024-11-20T19:26:53.141Z (2 months ago)
- Topics: real-time, rpc, server-sent-events, sse, subscriptions, typescript
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/@makay/rpc
- Size: 99.6 KB
- Stars: 9
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# 🌐 @makay/rpc
An RPC library for quick development of seamless full-stack applications.
Powered by a [Vite](https://vitejs.dev/) plugin and inspired by [Telefunc](https://telefunc.com/), [tRPC](https://trpc.io/) and other similar libraries.
---
[✨ Features](#-features)
[🔧 Installation and setup](#-installation-and-setup)
[🚀 Usage](#-usage)[📝 Input validation](#-input-validation)
[🚨 Errors](#-errors)
[📦 Async server state](#-async-server-state)
[👍 Results](#-results)
[📡 Subscriptions](#-subscriptions)[🔌 Adapters](#-adapters)
[🔥 Hono](#-hono)
[💎 Zod](#-zod)[🧑🏻💻 Contributing](#🧑🏻-contributing)
[📄 License](#-license)---
## ✨ Features:
- 🎉 End-to-end TypeScript
- 🚫 Zero boilerplate
- 📡 Optional [server-sent events](https://en.wikipedia.org/wiki/Server-sent_events) support for real-time [subscriptions](#-subscriptions)
- 🪶 Extremely small client bundle size addition
- 🔗 Directly import and call tailored server functions from client code
- 📄 Colocate server and client files (or don't)
- 📦 Front-end and back-end framework agnostic
- 📦 Validation library agnostic
- 🚫 Low server overhead with no implicit run-time validations
- 🪝 Use the [composables](https://vuejs.org/guide/reusability/composables)/[hooks](https://react.dev/reference/react/hooks) pattern in server code
- 🔌 Includes adapters for popular libraries like [Hono](https://hono.dev/) and [Zod](https://zod.dev/)
- 🧰 Includes utilities for [async server state](https://github.com/Makay11/rpc/blob/main/lib/src/server/state.ts) and [results](https://github.com/Makay11/rpc/blob/main/lib/src/result.ts)## 🔧 Installation and setup
1. Install a single package:
```sh
npm i @makay/rpc
``````sh
yarn add @makay/rpc
``````sh
pnpm add @makay/rpc
``````sh
bun add @makay/rpc
```Everything is included out-of-the-box!
2. Set up the Vite plugin:
```ts
// vite.config.ts
import { rpc } from "@makay/rpc/vite"
import { defineConfig } from "vite"export default defineConfig({
plugins: [rpc()],
})
```You can run both `vite` to start a dev server or `vite build` to build for production.
3. Set up the RPC server (example using the included [Hono](https://hono.dev/) adapter):
```ts
// src/server.ts
import { serve } from "@hono/node-server"
import { createRpc } from "@makay/rpc/hono"
import { Hono } from "hono"
import { cors } from "hono/cors"const app = new Hono()
app.use(
"/rpc",
cors({ origin: "http://localhost:5173", credentials: true }),
await createRpc()
)serve(app, (info) => {
console.log(`Server is running on http://localhost:${info.port}`)
})
```You can run the above file with something like `npx tsx src/server.ts`.
You can also run `npx tsx watch src/server.ts` to auto-reload during development.
4. Configure your client:
```ts
// src/main.ts
import { config } from "@makay/rpc/client"config.url = "http://localhost:3000/rpc"
config.credentials = "include"
```## 🚀 Usage
Create client and server files and seamlessly import server types and functions from client code with full TypeScript support!
```ts
// src/components/Todos.ts
import { createTodo, getTodos, type Todo } from "./Todos.server"let todos: Todo[] = []
async function main() {
todos = await getTodos()console.log(todos)
const newTodo = await createTodo("New Todo")
console.log(newTodo)
}main()
``````ts
// src/components/Todos.server.ts
export type Todo = {
id: string
text: string
}const todos: Todo[] = []
export async function getTodos() {
return todos
}export async function createTodo(text: string) {
const todo = {
id: crypto.randomUUID(),
text,
}todos.push(todo)
return todo
}
```Serve the above `src/components/Todos.ts` through Vite and you should see the array of todos printed to your browser console. Reload the page a bunch of times and you should see the array grow since the state is persisted in the server!
In a real scenario you would store your data in a database rather than in the server memory, of course. The snippets above are merely illustrative.
## 📝 Input validation
There is no implicit run-time validation of inputs in the server. In the example above, the function `createTodo` expects a single string argument. However, if your server is exposed publicly, bad actors or misconfigured clients might send something unexpected which can cause undefined behavior in you program.
Therefore, it is **extremely recommended** that you validate all function inputs. You can use any validation library you want for this.
Here's a basic example using [Zod](https://zod.dev/):
```ts
// src/components/Todos.server.ts
import { z } from "zod"const TextSchema = z.string().min(1).max(256)
export async function createTodo(text: string) {
TextSchema.parse(text)// `text` is now safe to use since Zod would have
// thrown an error if it was invalid
}
```When using the [Hono](https://hono.dev/) adapter, for instance, the code above will result in a `500 Internal Server Error` when you send an invalid input. In order to return the expected `400 Bad Request` instead, you have many options depending on the libraries, frameworks, and adapters you are using. Here are a few examples:
1. Catch the [Zod](https://zod.dev/) error and throw a `ValidationError` from `@makay/rpc/server` instead:
```ts
import { ValidationError } from "@makay/rpc/server"const TextSchema = z.string().min(1).max(256)
export async function createTodo(text: string) {
try {
TextSchema.parse(text)
} catch (error) {
if (error instanceof ZodError) {
throw new ValidationError(error.error.format())
}
throw error
}// `text` is now safe to use
}
```This is of course a bit too verbose to be practical.
2. Use the included [Zod](https://zod.dev/) adapter:
```ts
import { z, zv } from "@makay/rpc/zod"const TextSchema = z.string().min(1).max(256)
export async function createTodo(text: string) {
zv(text, TextSchema)// `text` is now safe to use
}
```This is much less verbose than the previous option.
3. Use the `onError` callback of the [Hono](https://hono.dev/) adapter:
```ts
import { serve } from "@hono/node-server"
import { createRpc } from "@makay/rpc/hono"
import { Hono } from "hono"
import { cors } from "hono/cors"
import { ZodError } from "zod"const app = new Hono()
const rpc = await createRpc({
onError(ctx, error) {
if (error instanceof ZodError) {
return ctx.json(error.error.format(), 400)
}
throw error
},
})app.use(
"/rpc",
cors({ origin: "http://localhost:5173", credentials: true }),
rpc
)serve(app, (info) => {
console.log(`Server is running on http://localhost:${info.port}`)
})
```This allows you to catch any unhandled [Zod](https://zod.dev/) errors and return `400 Bad Request` regardless of which server function threw the error.
You can easily adapt any of the examples above to work with any libraries and frameworks you are using. Remember that `@makay/rpc` is completely agnostic.
## 🚨 Errors
WIP
## 📦 Async server state
WIP
## 👍 Results
WIP
## 📡 Subscriptions
WIP
## 🔌 Adapters
### 🔥 Hono
WIP
### 💎 Zod
WIP
## 🧑🏻💻 Contributing
Contributions, issues, suggestions, ideas and discussions are all welcome!
This is an extremely young library and a lot can still change, be added and removed.
## 📄 License
[MPL-2.0](https://www.mozilla.org/en-US/MPL/2.0/)