Compare commits

...

24 Commits

Author SHA1 Message Date
wobsoriano
4289dcbfab chore(release): v0.10.8 2023-08-20 22:30:24 -07:00
wobsoriano
dc317c2ded build: bump local deps 2023-08-20 21:57:59 -07:00
wobsoriano
3db0b31d31 refactor: use stand-alone getErrorShape 2023-08-20 21:55:25 -07:00
wobsoriano
f86ebcd6d6 docs: add handler param description 2023-08-20 21:48:39 -07:00
wobsoriano
252f6108cf refactor: use built-in getRequestURL from h3 instead of ufo 2023-08-20 21:41:57 -07:00
wobsoriano
3a395a7d4a build(deps): bump h3 to 1.8.0 2023-08-20 21:36:23 -07:00
wobsoriano
ec7e0255c2 chore(release): v0.10.7 2023-08-10 21:01:35 -07:00
Robert Soriano
1386832b4a Merge pull request #115 from Blechlawine/fix/usequery-nullable
fix: make useQuery return data nullable (#113)
2023-08-10 08:15:24 -07:00
Blechlawine
120ebc5760 fix: make useQuery return data nullable 2023-08-10 15:16:46 +02:00
Robert Soriano
adeb5a5afd Merge pull request #84 from Colonel-Sandvich/patch-1
docs: Update Server Side Calls docs
2023-08-03 13:07:20 -07:00
wobsoriano
6e47431f35 bump local deps 2023-07-20 22:16:57 -07:00
wobsoriano
7c9392b209 add examples to workspace 2023-07-12 10:47:53 -07:00
wobsoriano
117f7b791f bump local deps 2023-07-12 10:46:39 -07:00
wobsoriano
735e362958 remove prettier config 2023-07-12 10:46:20 -07:00
Robert Soriano
0738a133ca Merge pull request #108 from RyanPaulGannon/next
Example: Added a basic example with a product call
2023-07-12 10:44:56 -07:00
Robert Soriano
97ef8c90b8 Update CHANGELOG.md 2023-07-09 17:41:35 -07:00
wobsoriano
bec300b750 chore(release): v0.10.6 2023-07-09 17:38:40 -07:00
wobsoriano
7bbe767495 bump local deps 2023-07-09 17:38:07 -07:00
wobsoriano
811a634010 chore(deps): update ofetch to 1.1.1 2023-07-09 17:36:54 -07:00
wobsoriano
12d8c97f71 chore(deps): update h3 to 1.7.1 2023-07-09 17:36:23 -07:00
Ryan Gannon
3fc261d5e7 Example: Added a basic example with a product call 2023-06-13 15:00:38 +01:00
wobsoriano
ad85a78350 chore(release): v0.10.5 2023-05-27 06:13:49 -07:00
wobsoriano
892d167ff1 feat: pass input to watched sources if it's a ref 2023-05-27 06:13:45 -07:00
Colonel-Sandvich
1c638a8489 docs: Update Server Side Calls docs 2023-04-08 22:56:57 +01:00
25 changed files with 2378 additions and 1630 deletions

View File

@@ -1,6 +1,68 @@
# Changelog # Changelog
## v0.10.8
[compare changes](https://github.com/wobsoriano/trpc-nuxt/compare/v0.10.7...v0.10.8)
### 💅 Refactors
- Use built-in getRequestURL from h3 instead of ufo ([252f610](https://github.com/wobsoriano/trpc-nuxt/commit/252f610))
- Use stand-alone getErrorShape ([3db0b31](https://github.com/wobsoriano/trpc-nuxt/commit/3db0b31))
### 📖 Documentation
- Add handler param description ([f86ebcd](https://github.com/wobsoriano/trpc-nuxt/commit/f86ebcd))
### 📦 Build
- **deps:** Bump h3 to 1.8.0 ([3a395a7](https://github.com/wobsoriano/trpc-nuxt/commit/3a395a7))
- Bump local deps ([dc317c2](https://github.com/wobsoriano/trpc-nuxt/commit/dc317c2))
### ❤️ Contributors
- Wobsoriano ([@wobsoriano](http://github.com/wobsoriano))
## v0.10.7
[compare changes](https://github.com/wobsoriano/trpc-nuxt/compare/v0.10.6...v0.10.7)
### 🩹 Fixes
- Make useQuery return data nullable ([120ebc5](https://github.com/wobsoriano/trpc-nuxt/commit/120ebc5))
### 📖 Documentation
- Update Server Side Calls docs ([1c638a8](https://github.com/wobsoriano/trpc-nuxt/commit/1c638a8))
### ❤️ Contributors
- Blechlawine <marczinser@gmx.de>
- Colonel-Sandvich
## v0.10.6
[compare changes](https://github.com/wobsoriano/trpc-nuxt/compare/v0.10.5...v0.10.6)
### 🏡 Chore
- **deps**:
- Bump h3 to 1.7.1 &nbsp;-&nbsp; by @wobsoriano [<samp>(12d8c)</samp>](https://github.com/wobsoriano/trpc-nuxt/commit/12d8c97f71bedb025b2e7914db13e69e5f62c8a2)
- Bump ofetch to 1.1.1 &nbsp;-&nbsp; by @wobsoriano [<samp>(811a6)</samp>](https://github.com/wobsoriano/trpc-nuxt/commit/811a6340107a2bea53f2cf488416bcec915f88b0)
## v0.10.5
[compare changes](https://github.com/wobsoriano/trpc-nuxt/compare/v0.10.4...v0.10.5)
### 🚀 Enhancements
- Pass input to watched sources if it's a ref ([892d167](https://github.com/wobsoriano/trpc-nuxt/commit/892d167))
### ❤️ Contributors
- Wobsoriano ([@wobsoriano](http://github.com/wobsoriano))
## v0.10.4 ## v0.10.4
[compare changes](https://github.com/wobsoriano/trpc-nuxt/compare/v0.10.3...v0.10.4) [compare changes](https://github.com/wobsoriano/trpc-nuxt/compare/v0.10.3...v0.10.4)

View File

@@ -27,11 +27,11 @@ export const router = t.router({
``` ```
```ts [server/api/greeting.ts] ```ts [server/api/greeting.ts]
import { router } from '@/server/trpc/trpc' import { appRouter } from '@/server/trpc/routers'
export default eventHandler(async (event) => { export default defineEventHandler(async (event) => {
const { name } = getQuery(event) const { name } = getQuery(event)
const caller = router.createCaller({}) const caller = appRouter.createCaller({})
const greeting = await caller.greeting({ name }) const greeting = await caller.greeting({ name })
@@ -67,11 +67,11 @@ export const router = t.router({
``` ```
```ts [server/api/post.ts] ```ts [server/api/post.ts]
import { router } from '@/server/trpc/trpc' import { appRouter } from '@/server/trpc/routers'
export default eventHandler(async (event) => { export default defineEventHandler(async (event) => {
const body = await getBody(event) const body = await getBody(event)
const caller = router.createCaller({}) const caller = appRouter.createCaller({})
const post = await caller.post.add(body.post) const post = await caller.post.add(body.post)

View File

@@ -8,8 +8,8 @@
"preview": "nuxi preview" "preview": "nuxi preview"
}, },
"devDependencies": { "devDependencies": {
"@nuxt-themes/docus": "^1.12.0", "@nuxt-themes/docus": "^1.14.3",
"@nuxtlabs/github-module": "^1.6.3", "@nuxtlabs/github-module": "^1.6.3",
"nuxt": "3.4.3" "nuxt": "3.6.5"
} }
} }

23
examples/basic-example/.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
# Nuxt dev/build outputs
.output
.nuxt
.nitro
.cache
dist
# Node dependencies
node_modules
# Logs
logs
*.log
# Misc
.DS_Store
.fleet
.idea
# Local env files
.env
.env.*
!.env.example

View File

@@ -0,0 +1,2 @@
shamefully-hoist=true
strict-peer-dependencies=false

View File

@@ -0,0 +1,63 @@
# Nuxt 3 Minimal Starter
Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
## Setup
Make sure to install the dependencies:
```bash
# npm
npm install
# pnpm
pnpm install
# yarn
yarn install
```
## Development Server
Start the development server on `http://localhost:3000`:
```bash
# npm
npm run dev
# pnpm
pnpm run dev
# yarn
yarn dev
```
## Production
Build the application for production:
```bash
# npm
npm run build
# pnpm
pnpm run build
# yarn
yarn build
```
Locally preview production build:
```bash
# npm
npm run preview
# pnpm
pnpm run preview
# yarn
yarn preview
```
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.

View File

@@ -0,0 +1,127 @@
<script setup lang="ts">
const { $client } = useNuxtApp()
const { data: product } = await $client.products.useQuery()
function formatPrice(num: number) {
return `£ ${num.toFixed(2)}`
}
</script>
<template>
<div class="product-page">
<header class="header">
<h1 class="header-title">
TRPC-Nuxt Basic Example
</h1>
</header>
<div class="card">
<div class="card-content">
<h3 class="card-title">
{{ product.title }}
</h3>
<p class="card-description">
{{ product.description }}
</p>
<p class="card-price">
{{ formatPrice(product.price) }}
</p>
<button class="card-button">
Add to Cart
</button>
</div>
</div>
</div>
</template>
<style>
body {
margin: 0;
}
.product-page {
display: flex;
flex-direction: column;
align-items: center;
height: 100vh;
}
.header {
width: 100%;
background-color: #41b883;
padding: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.header-title {
margin: 0;
font-size: 24px;
font-weight: bold;
color: #fff;
text-align: center;
font-family: 'Roboto', sans-serif;
}
.card {
width: 300px;
border: 1px solid #eaeaea;
border-radius: 4px;
overflow: hidden;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
margin-top: 16px;
}
.card-image {
width: 100%;
height: auto;
}
.card-content {
padding: 16px;
background-color: #f9f9f9;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.card-title {
margin: 0;
font-size: 20px;
color: #333;
font-family: 'Roboto', sans-serif;
}
.card-description {
margin: 8px 0;
font-size: 14px;
color: #666;
font-family: 'Roboto', sans-serif;
}
.card-price {
margin: 8px 0;
font-size: 18px;
font-weight: bold;
color: #333;
font-family: 'Roboto', sans-serif;
}
.card-button {
display: block;
width: 100%;
padding: 8px;
margin-top: 16px;
font-size: 16px;
font-weight: bold;
text-align: center;
color: #fff;
background-color: #41b883;
border: none;
border-radius: 4px;
cursor: pointer;
}
.card-button:hover {
background-color: #399d7d;
}
</style>

View File

@@ -0,0 +1,7 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devtools: { enabled: true },
build: {
transpile: ['trpc-nuxt'],
},
})

View File

@@ -0,0 +1,22 @@
{
"name": "nuxt-app",
"private": true,
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"devDependencies": {
"@nuxt/devtools": "latest",
"@types/node": "^20.4.2",
"nuxt": "^3.6.5"
},
"dependencies": {
"@trpc/client": "^10.35.0",
"@trpc/server": "^10.35.0",
"trpc-nuxt": "workspace:*",
"zod": "^3.21.4"
}
}

View File

@@ -0,0 +1,22 @@
import { createTRPCNuxtClient, httpBatchLink } from 'trpc-nuxt/client'
import type { AppRouter } from '~/server/trpc/routers'
export default defineNuxtPlugin(() => {
/**
* createTRPCNuxtClient adds a `useQuery` composable
* built on top of `useAsyncData`.
*/
const client = createTRPCNuxtClient<AppRouter>({
links: [
httpBatchLink({
url: '/api/trpc',
}),
],
})
return {
provide: {
client,
},
}
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,9 @@
import { createNuxtApiHandler } from 'trpc-nuxt'
import { appRouter } from '../../trpc/routers'
import { createContext } from '../../trpc/context'
// export API handler
export default createNuxtApiHandler({
router: appRouter,
createContext,
})

View File

@@ -0,0 +1,9 @@
import { inferAsyncReturnType } from '@trpc/server'
/**
* Creates context for an incoming request
* @link https://trpc.io/docs/context
*/
export const createContext = () => ({})
export type Context = inferAsyncReturnType<typeof createContext>

View File

@@ -0,0 +1,27 @@
import { z } from 'zod'
import { publicProcedure, router } from '../trpc'
export const appRouter = router({
hello: publicProcedure
.input(
z.object({
text: z.string().nullish(),
})
)
.query(({ input }) => {
console.log(input)
return {
greeting: `hello ${input?.text ?? 'world'}`,
}
}),
products: publicProcedure.query(async () => {
return {
title: 'TRPC Nuxt',
price: 12.0,
description: 'Check out the trpc-nuxt repo',
}
}),
})
// export type definition of API
export type AppRouter = typeof appRouter

View File

@@ -0,0 +1,20 @@
/**
* This is your entry point to setup the root configuration for tRPC on the server.
* - `initTRPC` should only be used once per app.
* - We export only the functionality that we use so we can enforce which base procedures should be used
*
* Learn how to create protected base procedures and other things below:
* @see https://trpc.io/docs/v10/router
* @see https://trpc.io/docs/v10/procedures
*/
import { initTRPC } from '@trpc/server'
import { Context } from '../trpc/context'
const t = initTRPC.context<Context>().create()
/**
* Unprotected procedure
**/
export const publicProcedure = t.procedure
export const router = t.router
export const middleware = t.middleware

View File

@@ -0,0 +1,3 @@
{
"extends": "../.nuxt/tsconfig.server.json"
}

View File

@@ -0,0 +1,4 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json"
}

View File

@@ -2,8 +2,8 @@
"name": "trpc-nuxt", "name": "trpc-nuxt",
"description": "End-to-end typesafe APIs in Nuxt applications.", "description": "End-to-end typesafe APIs in Nuxt applications.",
"type": "module", "type": "module",
"packageManager": "pnpm@8.5.1", "packageManager": "pnpm@8.6.9",
"version": "0.10.4", "version": "0.10.8",
"license": "MIT", "license": "MIT",
"sideEffects": false, "sideEffects": false,
"exports": { "exports": {
@@ -39,20 +39,19 @@
"@trpc/server": "^10.26.0" "@trpc/server": "^10.26.0"
}, },
"dependencies": { "dependencies": {
"h3": "^1.6.6", "h3": "^1.8.0",
"ofetch": "^1.0.1", "ofetch": "^1.1.1",
"ohash": "^1.1.2", "ohash": "^1.1.2"
"ufo": "^1.1.2"
}, },
"devDependencies": { "devDependencies": {
"@nuxt/eslint-config": "^0.1.1", "@nuxt/eslint-config": "^0.1.1",
"@trpc/client": "^10.28.0", "@trpc/client": "^10.37.1",
"@trpc/server": "^10.28.0", "@trpc/server": "^10.37.1",
"changelogen": "^0.5.3", "changelogen": "^0.5.4",
"eslint": "^8.41.0", "eslint": "^8.45.0",
"taze": "^0.10.1", "taze": "^0.11.2",
"tsup": "6.7.0", "tsup": "7.2.0",
"typescript": "^5.0.4" "typescript": "^5.1.6"
}, },
"eslintConfig": { "eslintConfig": {
"extends": [ "extends": [
@@ -75,7 +74,9 @@
], ],
"pnpm": { "pnpm": {
"overrides": { "overrides": {
"nuxt": "3.4.3" "nuxt": "3.6.5",
"@trpc/client": "10.37.1",
"@trpc/server": "10.37.1"
} }
} }
} }

View File

@@ -9,14 +9,14 @@
"postinstall": "nuxt prepare" "postinstall": "nuxt prepare"
}, },
"dependencies": { "dependencies": {
"@trpc/client": "^10.28.0", "@trpc/client": "^10.37.1",
"@trpc/server": "^10.28.0", "@trpc/server": "^10.37.1",
"superjson": "^1.12.3", "superjson": "^1.13.1",
"trpc-nuxt": "workspace:*", "trpc-nuxt": "workspace:*",
"zod": "^3.21.4" "zod": "^3.21.4"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^18.16.15", "@types/node": "^20.4.2",
"nuxt": "3.4.3" "nuxt": "3.6.5"
} }
} }

View File

@@ -6,9 +6,7 @@ const id = ref(1)
// watch: [id] // watch: [id]
// }) // })
const { data: todo, pending, error, refresh } = await $client.todo.getTodo.useQuery(id, { const { data: todo, pending, error, refresh } = await $client.todo.getTodo.useQuery(id)
watch: [id],
})
</script> </script>
<template> <template>

3512
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,4 @@
packages: packages:
- playground - playground
- examples/*
- docs - docs

View File

@@ -4,7 +4,7 @@ import { createFlatProxy, createRecursiveProxy } from '@trpc/server/shared'
import { hash } from 'ohash' import { hash } from 'ohash'
import { type DecoratedProcedureRecord } from './types' import { type DecoratedProcedureRecord } from './types'
// @ts-expect-error: Nuxt auto-imports // @ts-expect-error: Nuxt auto-imports
import { getCurrentInstance, onScopeDispose, useAsyncData, unref } from '#imports' import { getCurrentInstance, onScopeDispose, useAsyncData, unref, isRef } from '#imports'
/** /**
* Calculates the key used for `useAsyncData` call. * Calculates the key used for `useAsyncData` call.
@@ -58,7 +58,10 @@ export function createNuxtProxyDecoration<TRouter extends AnyRouter> (name: stri
return useAsyncData(queryKey, () => (client as any)[path].query(unref(input), { return useAsyncData(queryKey, () => (client as any)[path].query(unref(input), {
signal: controller?.signal, signal: controller?.signal,
...trpc ...trpc
}), asyncDataOptions) }), {
...asyncDataOptions,
watch: isRef(input) ? [...(asyncDataOptions.watch || []), input] : asyncDataOptions.watch
})
} }
return (client as any)[path][lastArg](...args) return (client as any)[path][lastArg](...args)

View File

@@ -60,7 +60,7 @@ type DecorateProcedure<
>( >(
input: MaybeRef<inferProcedureInput<TProcedure>>, input: MaybeRef<inferProcedureInput<TProcedure>>,
opts?: AsyncDataOptions<ResT, DataT, PickKeys> & { trpc?: TRPCRequestOptions }, opts?: AsyncDataOptions<ResT, DataT, PickKeys> & { trpc?: TRPCRequestOptions },
) => AsyncData<PickFrom<DataT, PickKeys>, DataE>, ) => AsyncData<PickFrom<DataT, PickKeys> | null, DataE>,
query: Resolver<TProcedure> query: Resolver<TProcedure>
} : TProcedure extends AnyMutationProcedure ? { } : TProcedure extends AnyMutationProcedure ? {
mutate: Resolver<TProcedure> mutate: Resolver<TProcedure>

View File

@@ -9,10 +9,10 @@ import type {
import { import {
TRPCError TRPCError
} from '@trpc/server' } from '@trpc/server'
import { createURL } from 'ufo'
import type { H3Event } from 'h3' import type { H3Event } from 'h3'
import { createError, defineEventHandler, isMethod, readBody } from 'h3' import { createError, defineEventHandler, getRequestURL, isMethod, readBody } from 'h3'
import type { TRPCResponse } from '@trpc/server/rpc' import type { TRPCResponse } from '@trpc/server/rpc'
import { getErrorShape } from '@trpc/server/shared'
type MaybePromise<T> = T | Promise<T> type MaybePromise<T> = T | Promise<T>
@@ -40,9 +40,25 @@ export interface OnErrorPayload<TRouter extends AnyRouter> {
export type OnErrorFn<TRouter extends AnyRouter> = (opts: OnErrorPayload<TRouter>) => void export type OnErrorFn<TRouter extends AnyRouter> = (opts: OnErrorPayload<TRouter>) => void
export interface ResolveHTTPRequestOptions<TRouter extends AnyRouter> { export interface ResolveHTTPRequestOptions<TRouter extends AnyRouter> {
/**
* The tRPC router to use.
* @see https://trpc.io/docs/router
*/
router: TRouter router: TRouter
/**
* An async function that returns the tRPC context.
* @see https://trpc.io/docs/context
*/
createContext?: CreateContextFn<TRouter> createContext?: CreateContextFn<TRouter>
/**
* A function that returns the response meta.
* @see https://trpc.io/docs/caching#using-responsemeta-to-cache-responses
*/
responseMeta?: ResponseMetaFn<TRouter> responseMeta?: ResponseMetaFn<TRouter>
/**
* A function that is called when an error occurs.
* @see https://trpc.io/docs/error-handling#handling-errors
*/
onError?: OnErrorFn<TRouter> onError?: OnErrorFn<TRouter>
batching?: { batching?: {
enabled: boolean enabled: boolean
@@ -74,15 +90,16 @@ export function createNuxtApiHandler<TRouter extends AnyRouter> ({
res res
} = event.node } = event.node
const $url = createURL(req.url!) const $url = getRequestURL(event)
const path = getPath(event) const path = getPath(event)
if (path === null) { if (path === null) {
const error = router.getErrorShape({ const error = getErrorShape({
config: router._def._config,
error: new TRPCError({ error: new TRPCError({
message: message:
'Param "trpc" not found - is the file named `[trpc]`.ts or `[...trpc].ts`?', 'Query "trpc" not found - is the file named `[trpc]`.ts or `[...trpc].ts`?',
code: 'INTERNAL_SERVER_ERROR' code: 'INTERNAL_SERVER_ERROR'
}), }),
type: 'unknown', type: 'unknown',