Files
trpc-nuxt/README.md
Robert Soriano 13385d2bb6 update readme
2022-05-18 07:15:27 -07:00

3.5 KiB

tRPC-Nuxt

Version

End-to-end typesafe APIs with tRPC.io in Nuxt applications.

Install

npm i trpc-nuxt
// nuxt.config.ts
import { defineNuxtConfig } from 'nuxt'

export default defineNuxtConfig({
  modules: ['trpc-nuxt'],
})

Usage

Expose your tRPC routes and context under ~/server/trpc/index.ts:

// ~/server/trpc/index.ts
import type { inferAsyncReturnType } from '@trpc/server'
import * as trpc from '@trpc/server'

export const router = trpc
  .router<inferAsyncReturnType<typeof createContext>>()
  // queries and mutations...
  .query('hello', {
    resolve: () => 'world',
  })
  .query('bye', {
    resolve() {
      return {
        text: 'goodbye',
      }
    },
  })

// optional
export const createContext = (event: CompatibilityEvent) => {
  // ...
  return {
    /** context data */
  }
}

// optional
export const responseMeta = () => {
  // ...
  return {
    // { headers: ... }
  }
}

Use the client like so:

<script setup lang="ts">
const client = useClient()

const greeting = await client.query('hello');
console.log(greeting); // => 👈 world

const farewell = await client.query('bye');
console.log(farewell); // => 👈 goodbye
</script>

Recipes

Validation

tRPC works out-of-the-box with yup/superstruct/zod/myzod/custom validators. Learn more about input validation here.

// ~/server/trpc/index.ts
import { z } from 'zod'

export const router = trpc
  .router()
  .mutation('createUser', {
    // validate input with Zod
    input: z.object({
      name: z.string().min(5)
    }),
    async resolve(req) {
      // use your ORM of choice
      return await UserModel.create({
        data: req.input,
      })
    },
  })

Authorization

The createContext-function is called for each incoming request so here you can add contextual information about the calling user from the request object. Learn more about authorization here.

import * as trpc from '@trpc/server'
import type { CompatibilityEvent } from 'h3'
import { decodeAndVerifyJwtToken } from '~/somewhere/in/your/app/utils'

// The app's context - is generated for each incoming request
export async function createContext(event: CompatibilityEvent) {
  // Create your context based on the request object
  // Will be available as `ctx` in all your resolvers

  // This is just an example of something you'd might want to do in your ctx fn
  async function getUserFromHeader() {
    if (req.headers.authorization) {
      const user = await decodeAndVerifyJwtToken(req.headers.authorization.split(' ')[1])
      return user
    }
    return null
  }
  const user = await getUserFromHeader()

  return {
    user,
  }
}

export const router = trpc
  .router<inferAsyncReturnType<typeof createContext>>()
  // open for anyone
  .query('hello', {
    input: z.string().nullish(),
    resolve: ({ input, ctx }) => {
      return `hello ${input ?? ctx.user?.name ?? 'world'}`
    },
  })
  // checked in resolver
  .query('secret', {
    resolve: ({ ctx }) => {
      if (!ctx.user)
        throw new trpc.TRPCError({ code: 'UNAUTHORIZED' })

      return {
        secret: 'sauce',
      }
    },
  })

Learn more about tRPC.io here.

License

MIT