createCaller
AuthPC was renamed to pRPC
Use this method to interact with the api, you can choose between a query
or a mutation
(default is query) and also choose to use either GET
/POST
as the request method (default is GET & wrapped with Solid's query
function).
API
No Schema
import { createCaller } from '@solid-mediakit/prpc'
const myServerQuery = createCaller( ({ session$ }) => { console.log(session$, event$.request.headers.get('user-agent')) return 'Who do i say hey to' }, { method: 'GET', },)
// client.tsximport { myServerQuery } from './server'const query = myServerQuery()
Zod
import { createCaller } from '@solid-mediakit/prpc'import { z } from 'zod'
const mySchema = z.object({ name: z.string() })
export const myServerQuery = createCaller( mySchema, ({ input$, session$, event$ }) => { console.log(session$, event$.request.headers.get('user-agent')) return `Hey there ${input$.name}` }, { method: 'GET', },)
// client.tsximport { myServerQuery } from './server'const query = myServerQuery(() => ({ name: 'Demo' }))
Valibot
import { createCaller } from '@solid-mediakit/prpc'import * as v from 'valibot'
const mySchema = v.object({ name: v.string() })
export const myServerQuery = createCaller( mySchema, ({ input$, session$, event$ }) => { console.log(session$, event$.request.headers.get('user-agent')) return `Hey there ${input$.name}` }, { method: 'GET', },)
// client.tsximport { myServerQuery } from './server'const query = myServerQuery(() => ({ name: 'Demo' }))
Error Handling
Errors are thrown using the PRPClientError
class. When the server function has a schema defined, you can use the isValidationError
to get the issues.
import { createEffect } from 'solid-js'import { myServerQuery } from './server'
const MyClient = () => { const query = myServerQuery(() => ({ name: 'Demo' }))
createEffect(() => { if (query.error) { if (query.error.isValidationError()) { query.error.cause.fieldErrors.name // string[] } else { console.error('What is this', query.error.cause) } } })
return (...)}
export default MyClient
Methods
You can choose any method (GET || POST), regardless of the function type (default is GET & wrapped with Solid's query
function)
GET
This is the default so you don't have to mention it
import { createCaller, response$ } from '@solid-mediakit/prpc'
export const getRequest = createCaller( () => { return response$( { iSetTheHeader: true }, { headers: { 'cache-control': 'max-age=60' } }, ) }, { method: 'GET', },)
export const getRequest2 = createCaller(() => { return response$( { iSetTheHeader: true }, { headers: { 'cache-control': 'max-age=60' } }, )})
export const protectedQuery = createCaller( ({ session$ }) => { console.log(session$.user) return `Hey there ${session$.user.name}` }, { protected: true, },)
POST
You can also use POST
with queries.
When using mutations
/ actions
you don't have to specify the method and the default will be POST
import { createCaller, response$ } from '@solid-mediakit/prpc'
export const postRequest = createCaller( () => { return response$({ iSetTheHeader: true }, { headers: { 'X-Testing': '1' } }) }, { method: 'POST', },)
export const postRequest2 = createCaller( () => { return response$({ iSetTheHeader: true }, { headers: { 'X-Testing': '1' } }) }, { type: 'action', },)
Function Types
In addition to methods, you can also choose a function type (the function type will not affect the request method).
query
This is the default, you don't have to specify this:
import { createCaller } from '@solid-mediakit/prpc'
const thisIsAQuery = createCaller(() => { return 1})
const alsoIsAQuery = createCaller( () => { return 1 }, { type: 'query', },)
// client sideconst query = thisIsAQuery()query.data // number
mutation
You can either specify {type: 'action'}
or use the createAction
method
import { createCaller, createAction } from '@solid-mediakit/prpc'
const thisIsAMutation = createCaller( () => { return 1 }, { type: 'action', },)
const alsoIsAMutation = createAction(() => { return 1})
// client sideconst mutation = thisIsAMutation()mutation.mutate()
Middlewares & .use
This method allows you create a reuseable caller that contains your middlewares. You can combine multiple callers / middlewares and import them to different files, take a look at this example.
file1.ts
In this file we create a caller with a custom middleware and then export it so it could be use in other files as-well.
import { createCaller } from '@solid-mediakit/prpc'
export const withMw1 = createCaller.use(() => { return { myFile1: 1, }})
export const action1 = withMw1(({ ctx$ }) => { return `hey ${ctx$.myFile1} `})
file2.ts
In this file we can actually import the caller we created and then add more middlewares to it or use it as is.
import { withMw1 } from './file1'
export const withMw2 = withMw1.use(({ ctx$ }) => { return { ...ctx$, myFile2: 2, }})
export const action2 = withMw2(({ ctx$ }) => { return `hey ${ctx$.myFile1} ${ctx$.myFile2}`})
Utils
pRPC contains many utils out of the box, like redirection, erroring, setting cookies, etc.
redirect$
Use this function to redirect the user (this will not affect the function type):
import { createCaller, redirect$ } from '@solid-mediakit/prpc'
let redirect = false
export const myQuery = createCaller(() => { if (redirect) { return redirect$('/login') } return 'yes'})
error$
Use this function to throw an error on the user side (this will not affect the function type):
import { createCaller, error$ } from '@solid-mediakit/prpc'
let shouldError = false
export const myQuery = createCaller(() => { if (shouldError) { return error$('Why did i error') } return 'yes'})
response$
Use this function to return data & modify headers (the return type is also infered from the data passed to response$):
import { createCaller, response$ } from '@solid-mediakit/prpc'
let setHeader = false
export const myQuery = createCaller(() => { if (setHeader) { return response$(1, { headers: { this: 'that' }, }) } return 'yes'})
// respone type: number | string
Optimistic Updates
Similiar to tRPC, each query function has a useUtils
method, so lets say we import a server action we created using createCaller
called serverQuery1
import { serverQuery1, serverMutation1 } from '~/server/etc'
const MyComponent = () => { const listPostQuery = serverQuery1() const serverQueryUtils = serverQuery1.useUtils()
const postCreate = serverMutation1(() => ({ async onMutate(newPost) { // Cancel outgoing fetches (so they don't overwrite our optimistic update) await serverQueryUtils.cancel()
// Get the data from the queryCache const prevData = serverQueryUtils.getData()
// Optimistically update the data with our new post serverQueryUtils.setData(undefined, (old) => [...old, newPost])
// Return the previous data so we can revert if something goes wrong return { prevData } }, onError(err, newPost, ctx) { // If the mutation fails, use the context-value from onMutate serverQueryUtils.setData(undefined, ctx.prevData) }, onSettled() { // Sync with server once mutation has settled serverQueryUtils.invalidate() }, }))}
raw
Each function has function.raw
which is the transformed server function.
import { createCaller } from '@solid-mediakit/prpc'
const fn = createCaller(() => 1)
const callFn = async () => await fn.raw() // number
createAsync
Having this, we can ofc use Solid's built in createAsync
import { createAsync } from '@solidjs/router'
const myAsyncSignal = createAsync(() => fn.raw(), { deferStream: true })
Last updated: 5/30/25, 9:16 AM