Skip to content

Commit

Permalink
feat: add a pRPC package (#34)
Browse files Browse the repository at this point in the history
* add prpc

* fix prpc

* middleware api and docs
  • Loading branch information
OrJDev authored Mar 22, 2024
1 parent 31c5edc commit 65bf66f
Show file tree
Hide file tree
Showing 51 changed files with 2,592 additions and 661 deletions.
26 changes: 22 additions & 4 deletions docs/src/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,23 @@ export const SIDEBAR: Sidebar = {
{ text: 'Contributors', link: 'contributors' },
{ text: 'Sponsors', link: 'sponsors' },
],
tRPC: [
{ text: 'Install', link: 'trpc/install' },
{ text: 'Router', link: 'trpc/router' },
{ text: 'API Handler', link: 'trpc/handler' },
pRPC: [
{ text: 'query$', link: 'prpc/query' },
{ text: 'query$', link: 'prpc/mutation' },
{ text: 'middleware$', link: 'prpc/middleware' },
{ text: 'pipe$', link: 'prpc/pipe' },
{
text: 'error$',
link: 'prpc/error',
},
{
text: 'PRPCClientError',
link: 'prpc/prpc-client-error',
},
{
text: 'hideRequest',
link: 'prpc/hideRequest',
},
],
Auth: [
{ text: 'Install', link: 'auth/install' },
Expand All @@ -50,6 +63,11 @@ export const SIDEBAR: Sidebar = {
{ text: 'createSession', link: 'auth/createsession' },
{ text: 'getSession', link: 'auth/getsession' },
],
tRPC: [
{ text: 'Install', link: 'trpc/install' },
{ text: 'Router', link: 'trpc/router' },
{ text: 'API Handler', link: 'trpc/handler' },
],
Media: [
{ text: 'Install', link: 'media/install' },
{ text: 'createVideo', link: 'media/createvideo' },
Expand Down
62 changes: 62 additions & 0 deletions docs/src/content/docs/prpc/error.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
title: 'error$'
description: 'API for the error$ function'
---

**API for the error$ function**

This function will be used from the server-side, in order to throw errors on the client side and break out of running middlewares.

## Usage

```ts
import {
error$,
hideRequest,
middleware$,
pipe$,
query$,
} from '@solid-mediakit/prpc'
import { z } from 'zod'

const myMiddleware1 = middleware$(({ event$ }) => {
console.log('ua', event$.request.headers.get('user-agent'))
const random = Math.random()
const test = random > 0.5 ? 'test' : null
console.log({ test })
return { test }
})

const middleWare2 = pipe$(myMiddleware1, (ctx) => {
if (!ctx.test) {
return error$('Expected test to be defined')
// this will throw an error on the client side and will break out of the middlewares
}
return {
test: ctx.test,
o: 1,
}
})

const middleware3 = pipe$(middleWare2, (ctx) => {
// ctx.test is inferred to be string because we checked it in the previous middleware
return {
...ctx,
b: 2,
}
})

export const add = query$({
queryFn: ({ payload, ctx$ }) => {
console.log({ ctx$: hideRequest(ctx$) })
const result = payload.a + payload.b
return { result }
},
key: 'add',
schema: z.object({
a: z.number().max(5),
b: z.number().max(10),
}),
middleware: middleware3,
})
```
69 changes: 69 additions & 0 deletions docs/src/content/docs/prpc/hideRequest.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
title: 'hideRequest'
description: 'API for the hideRequest function'
---

**API for the hideRequest function**

This function will be used to remove the `event$` object from the context passed to a function.

## Usage

```ts
import { hideRequest, middleware$, pipe$, query$ } from '@solid-mediakit/prpc'
import { z } from 'zod'

const myMiddleware1 = middleware$(({ event$ }) => {
console.log('ua', event$.request.headers.get('user-agent'))
return { test: null }
})

const middleWare2 = pipe$(myMiddleware1, (ctx) => {
return {
test: ctx.test,
o: 1,
}
})

const middleware3 = pipe$(middleWare2, (ctx) => {
return {
...ctx,
b: 2,
}
})

export const add = query$({
queryFn: ({ payload, ctx$ }) => {
console.log({ ctx$: hideRequest(ctx$) })
const result = payload.a + payload.b
return { result }
},
key: 'add',
schema: z.object({
a: z.number().max(5),
b: z.number().max(10),
}),
middleware: middleware3,
})
```

## Parameters

`hideRequest` also takes in a second argument - `fully`, setting this to true will use the `delete` op to remove the event$ from the context, so you can use this a bit differently:

```ts
export const add = query$({
queryFn: ({ payload, ctx$ }) => {
hideRequest(ctx$, true)
console.log({ ctx$ })
const result = payload.a + payload.b
return { result }
},
key: 'add',
schema: z.object({
a: z.number().max(5),
b: z.number().max(10),
}),
middleware: middleware3,
})
```
35 changes: 35 additions & 0 deletions docs/src/content/docs/prpc/install.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
title: 'Install'
description: 'Install pRPC'
---

**Install pRPC**

### Installation

```bash
pnpm install @solid-mediakit/prpc @tanstack/solid-query @solid-mediakit/prpc-plugin
```

### Adding The Vite Plugin

Go ahead to `app.config.ts` and add the following:

```ts
import { defineConfig } from '@solidjs/start/config'
import { prpcVite } from '@solid-mediakit/prpc-plugin' // ->
export default defineConfig({
ssr: true,
vite: {
plugins: [prpcVite({ log: false })], // ->
},
})
```

Basically, what is going to happen is that the Vite plugin will make this piece of code run on the server, causing it so Zod doesn't leak to the client nor any other server-side code. What's so special about it is that it includes many useful features which aren't build in Vinxi, like Zod validation, `event$` property, custom error handling and more. The transformation process is done via Babel at real time so you don't have to worry about having ugly code in your code base, you just write it and we are in charge of the 'behind the scenes' stuff.

Now you can start using pRPC in your project.

- [query$](/prpc/query)
- [mutation$](/prpc/mutation)
37 changes: 37 additions & 0 deletions docs/src/content/docs/prpc/middleware.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
title: 'middleware$'
description: 'API for the middleware$ function'
---

**API for the middleware$ function**

Functions that will run before the actual function. Useful for authentication, logging, etc. Can be used for both `mutation$` and `query$`.

## Usage

```ts
import { middleware$, query$ } from '@solid-mediakit/prpc'
import { z } from 'zod'

const middleware3 = middleware$(({ event$ }) => {
console.log('req', event$)
return Math.random() > 0.5 ? { test: true } : { test: null }
})

export const cleanSyntaxQuery = query$({
queryFn: async ({ payload, event$, ctx$ }) => {
ctx$.test
console.log('called', event$.request.headers.get('user-agent'))
return { result: payload.a + payload.b }
},
key: 'cleanSyntaxQuery',
schema: z.object({
a: z.number().max(5),
b: z.number().max(10),
}),
// middleware3 was returned from `middleware$`
middleware: [middleware3],
})
```

If you want to pipe a middleware, check out the [pipe](/pipe) function.
101 changes: 101 additions & 0 deletions docs/src/content/docs/prpc/mutation.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
---
title: 'mutation$'
description: 'API for the mutation$ function'
---

**API for the mutation$ function**

`mutation$` is basically a wrapper for createMutation, it should be used whenever createMutation should be used.

## Usage

```tsx
// server function declaration
import { z } from 'zod'
import { error$, mutation$ } from '@solid-mediakit/prpc'

const testMutation = mutation$({
mutationFn: ({ payload, event$ }) => {
const ua = event$.request.headers.get('user-agent')
console.log({ ua })
if (payload.hello === 'error') {
return error$('This is an error')
}
return `hey ${payload.hello}`
},
key: 'hello',
schema: z.object({
hello: z.string(),
}),
})

// client code
const Home: VoidComponent = () => {
const [hello, setHello] = createSignal('')
const helloMutation = testMutation(() => ({
onError(error) {
if (error.isZodError()) {
console.log('zod error:', error.cause.fieldErrors)
} else {
console.log(error.message)
}
},
}))
return (
<main class='flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#026d56] to-[#152a2c]'>
<p class='text-2xl text-white'>
{helloMutation.data ?? 'No Data yet...'}
</p>
<Show when={helloMutation.isError}>
<p class='text-2xl text-red-500'>
{helloMutation.error?.message ?? 'Unknown Error'}
</p>
</Show>
<div class='container flex flex-col items-center justify-center gap-4 px-4 py-16'>
<input
type='text'
class='p-4 text-2xl rounded-lg'
value={hello()}
onInput={(e) => setHello(e.currentTarget.value)}
/>
<button
class='p-4 text-2xl bg-white rounded-lg'
onClick={() => helloMutation.mutate({ hello: hello() })}
>
Submit
</button>
</div>
</main>
)
}
```

## API

```ts
export type Mutation$Props<
Mw extends IMiddleware[],
Fn extends ExpectedFn<ZObj, Mw>,
ZObj extends ExpectedSchema = EmptySchema
> = {
mutationFn: Fn
key: string
schema?: ZObj
middleware?: Mw
}
```
### mutationFn API
```ts
export type Fn$Input<
ZObj extends ExpectedSchema = EmptySchema,
Mw extends IMiddleware[] = []
> = {
payload: Infer$PayLoad<ZObj>
event$: PRPCEvent
ctx$: FilterOutResponse<InferFinalMiddlware<FlattenArray<Mw>>>
}
```
Read more regarding the [Middleware API](/prpc/middleware)
Loading

0 comments on commit 65bf66f

Please sign in to comment.