|
| 1 | +# SvelteKit RPC |
| 2 | + |
| 3 | +This is a simple and type-safe [Remote Procedure Call (`RPC`)](https://www.techtarget.com/searchapparchitecture/definition/Remote-Procedure-Call-RPC) implementation in a [`SvelteKit`](https://svelte.dev/docs/kit/introduction) project. |
| 4 | +It demonstrates how to call server-side functions _directly_ and _typesafely_ from the client using a minimal setup. |
| 5 | + |
| 6 | +--- |
| 7 | + |
| 8 | +## 📚 Table of Contents |
| 9 | + |
| 10 | +- [🧠 How It Works](#-how-it-works) |
| 11 | +- [🚀 Getting Started](#-getting-started) |
| 12 | + - [1. Clone the repository](#1-clone-the-repository) |
| 13 | + - [2. Install dependencies](#2-install-dependencies) |
| 14 | + - [3. Start the dev server](#3-start-the-dev-server) |
| 15 | +- [🛠️ How To Use](#-how-to-use) |
| 16 | + - [1. Define the routes](#1-define-the-routes) |
| 17 | + - [2. Create the app router](#2-create-the-app-router) |
| 18 | + - [3. Calling the API](#3-calling-the-api) |
| 19 | + - [4. Creating an middleware](#4-creating-an-middleware) |
| 20 | +- [📃 License](#license) |
| 21 | +- [🐛 Found a Bug?](#-found-a-bug) |
| 22 | + |
| 23 | +--- |
| 24 | + |
| 25 | +## 🧠 How It Works |
| 26 | + |
| 27 | +Server-side functions are defined in [`$lib/server/routers/*.ts`](./src/lib/server/routers) and combined inside the [`appRouter`](./src/lib/server/root.ts) in [`$lib/server/root.ts`](./src/lib/server/root.ts). |
| 28 | + |
| 29 | +The [`/api/[...endpoints]`](./src/routes/api/[...endpoints]/+server.ts) route catches [`RPC`](https://www.techtarget.com/searchapparchitecture/definition/Remote-Procedure-Call-RPC) requests and runs the matching function using the [`callRouter`](./src/lib/server/caller.ts) function in [`$lib/server/caller.ts`](./src/lib/server/caller.ts). |
| 30 | + |
| 31 | +The client makes a fetch call to that route using the [`callApi`](./src/lib/client/caller.ts) function in [`$lib/client/caller.ts`](./src/lib/client/caller.ts). Which provides full type safety and [`IntelliSense`](https://code.visualstudio.com/docs/editing/intellisense) for the defined route. |
| 32 | + |
| 33 | +[`Zod`](https://zod.dev/?id=introduction) is used to make validations of the input before the actual function runs but also validates the output before it reaches its end destination. This functionality is wrapped inside the base middleware [`publicProcedure`](./src/lib/server/setup.ts) in [`$lib/server/setup.ts`](./src/lib/server/setup.ts). |
| 34 | + |
| 35 | +[`TypeScript`](https://www.typescriptlang.org/docs/) ensures type safety for arguments and return values but also [`IntelliSense`](https://code.visualstudio.com/docs/editing/intellisense) for the developer. |
| 36 | + |
| 37 | +## 🚀 Getting Started |
| 38 | + |
| 39 | +### 1. Clone the repository |
| 40 | + |
| 41 | +```bash |
| 42 | +git clone https://github.com/Falk33n/sveltekit-rpc.git |
| 43 | +cd sveltekit-rpc |
| 44 | +``` |
| 45 | + |
| 46 | +### 2. Install dependencies |
| 47 | + |
| 48 | +```bash |
| 49 | +# (or use `bun install` / `pnpm install` / `yarn install` if you prefer). |
| 50 | +npm install |
| 51 | +``` |
| 52 | + |
| 53 | +### 3. Start the dev server |
| 54 | + |
| 55 | +```bash |
| 56 | +# (or use `bun run dev` / `pnpm run dev` / `yarn run dev` if you prefer). |
| 57 | +npm run dev |
| 58 | +``` |
| 59 | + |
| 60 | +Your app should now be running at [http://localhost:5173](http://localhost:5173). |
| 61 | + |
| 62 | +## 🛠️ How To Use |
| 63 | + |
| 64 | +### 1. Define the routes |
| 65 | + |
| 66 | +```ts |
| 67 | +import { exampleInputSchema, exampleOutputSchema } from '$lib/schemas/example'; |
| 68 | +import { createRouter, publicProcedure } from '../setup'; |
| 69 | + |
| 70 | +export const exampleRouter = createRouter({ |
| 71 | + example: { |
| 72 | + method: 'post', |
| 73 | + handler: publicProcedure |
| 74 | + .input(exampleInputSchema) |
| 75 | + .output(exampleOutputSchema) |
| 76 | + .resolve(async (event, input) => { |
| 77 | + // The input is directly parsed from the input schema. |
| 78 | + const { id } = input; |
| 79 | + |
| 80 | + // The event is the same as the event that comes from SvelteKit backend. |
| 81 | + console.log(event.params); |
| 82 | + |
| 83 | + // The output is directly parsed from the output schema. |
| 84 | + return { |
| 85 | + status: 200, |
| 86 | + data: { id, name: 'hanna' }, |
| 87 | + message: 'OK', |
| 88 | + }; |
| 89 | + }), |
| 90 | + }, |
| 91 | +}); |
| 92 | +``` |
| 93 | + |
| 94 | +### 2. Create the app router |
| 95 | + |
| 96 | +```ts |
| 97 | +import { exampleRouter } from './routers'; |
| 98 | +import { createAppRouter } from './setup'; |
| 99 | + |
| 100 | +// This is the root of all the endpoints, we define this to get a better structure of different |
| 101 | +// categories of routers. |
| 102 | +export const appRouter = createAppRouter({ |
| 103 | + example: exampleRouter, |
| 104 | +}); |
| 105 | +``` |
| 106 | + |
| 107 | +### 3. Calling the API |
| 108 | + |
| 109 | +```svelte |
| 110 | +<script lang="ts"> |
| 111 | + import { callApi } from '../lib/client/caller'; |
| 112 | +
|
| 113 | + async function onclick() { |
| 114 | + // Fully typesafe api caller, that will give you the correct input, |
| 115 | + // output and method based on the endpoint string. |
| 116 | + const api = await callApi('example.example', { |
| 117 | + input: { id: 'hoho' }, |
| 118 | + method: 'post', |
| 119 | + }); |
| 120 | +
|
| 121 | + // Logs the return object of the api. |
| 122 | + console.log(api); |
| 123 | + } |
| 124 | +</script> |
| 125 | +
|
| 126 | +<button {onclick}>test</button> |
| 127 | +``` |
| 128 | + |
| 129 | +### 4. Creating an middleware |
| 130 | + |
| 131 | +```ts |
| 132 | +import { publicProcedure } from '../setup'; |
| 133 | + |
| 134 | +// This is how we define an middleware to be used and called inbefore each endpoint runs. |
| 135 | +const exampleProcedure = publicProcedure.use((event) => console.log(event.cookies.getAll())); |
| 136 | +``` |
| 137 | + |
| 138 | +## License |
| 139 | + |
| 140 | +Distributed under the [`MIT` License](https://memgraph.com/blog/what-is-mit-license). This project is open source and free to use, modify, and distribute under the terms of the [`MIT` License](https://memgraph.com/blog/what-is-mit-license). |
| 141 | + |
| 142 | +You can find the full license text in the [`LICENSE.md`](./LICENSE.md) file. |
| 143 | + |
| 144 | +## 🐛 Found a Bug? |
| 145 | + |
| 146 | +Open an issue or create a pull request. Contributions are always welcome! |
| 147 | + |
| 148 | +If you discover a **security vulnerability**, please **do not** open an issue or create a pull request. |
| 149 | +Instead, report it **privately** by emailing [**tim.falk00@gmail.com**](mailto:tim.falk00@gmail.com) _or_ [**nils-pettsson@outlook.com**](mailto:nils-pettsson@outlook.com). Thank you for being responsible! |
0 commit comments