This is a simple and type-safe Remote Procedure Call (RPC
) implementation in a SvelteKit
project.
It demonstrates how to call server-side functions directly and typesafely from the client using a minimal setup.
Server-side functions are defined in $lib/server/routers/*.ts
and combined inside the appRouter
in $lib/server/root.ts
.
The /api/[...endpoints]
route catches RPC
requests and runs the matching function using the callRouter
function in $lib/server/caller.ts
.
The client makes a fetch call to that route using the callApi
function in $lib/client/caller.ts
. Which provides full type safety and IntelliSense
for the defined route.
Zod
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
in $lib/server/setup.ts
.
TypeScript
ensures type safety for arguments and return values but also IntelliSense
for the developer.
git clone https://github.com/Falk33n/sveltekit-rpc.git
cd sveltekit-rpc
# (or use `bun install` / `pnpm install` / `yarn install` if you prefer).
npm install
# (or use `bun run dev` / `pnpm run dev` / `yarn run dev` if you prefer).
npm run dev
Your app should now be running at http://localhost:5173.
import { exampleInputSchema, exampleOutputSchema } from '$lib/schemas/example';
import { createRouter, publicProcedure } from '../setup';
export const exampleRouter = createRouter({
example: {
method: 'post',
handler: publicProcedure
.input(exampleInputSchema)
.output(exampleOutputSchema)
.resolve(async (event, input) => {
// The input is directly parsed from the input schema.
const { id } = input;
// The event is the same as the event that comes from SvelteKit backend.
console.log(event.params);
// The output is directly parsed from the output schema.
return {
status: 200,
data: { id, name: 'hanna' },
message: 'OK',
};
}),
},
});
import { exampleRouter } from './routers';
import { createAppRouter } from './setup';
// This is the root of all the endpoints, we define this to get a better structure of different
// categories of routers.
export const appRouter = createAppRouter({
example: exampleRouter,
});
<script lang="ts">
import { callApi } from '../lib/client/caller';
async function onclick() {
// Fully typesafe api caller, that will give you the correct input,
// output and method based on the endpoint string.
const api = await callApi('example.example', {
input: { id: 'hoho' },
method: 'post',
});
// Logs the return object of the api.
console.log(api);
}
</script>
<button {onclick}>test</button>
import { publicProcedure } from '../setup';
// This is how we define an middleware to be used and called inbefore each endpoint runs.
const exampleProcedure = publicProcedure.use((event) => console.log(event.cookies.getAll()));
Distributed under the MIT
License. This project is open source and free to use, modify, and distribute under the terms of the MIT
License.
You can find the full license text in the LICENSE.md
file.
Open an issue or create a pull request. Contributions are always welcome!
If you discover a security vulnerability, please do not open an issue or create a pull request. Instead, report it privately by emailing [email protected] or [email protected]. Thank you for being responsible!