Build A Full-Stack Typescript Application with Nuxt and tRPC
Tags: nuxt, trpc, typescript, vue
Published: 2023-08-31 14:45:04

>[tRpc](https://trpc.io/) - Move Fast and Break Nothing. End-to-end typesafe APIs made easy. Experience the full power of TypeScript inference to boost productivity for your NUXT full-stack application.

This blog is a companion to the video walkthrough of getting a [Nuxt 3](https://nuxt.com/) application up and running with tRpc using the [trpc-nuxt module](https://github.com/wobsoriano/trpc-nuxt).

This is the first video in the series, additional content will be created for you to follow along and build a Full-Stack Typescript Application with Nuxt and tRPC. 
- [Series Video Playlist](https://www.youtube.com/playlist?list=PL2PY2-9rsgl3-8_-DAHs7T4Pi9XwaLj9i)


{% youtube XX-4A4xkRB8 %}

>tPRC Nuxt Module - [wobsoriano/trpc-nuxt Documentation ](https://github.com/wobsoriano/trpc-nuxt)


## Installation

Create a Nuxt 3 App using the cli

```tsx
npx nuxi@latest init trpc-nuxt-app
```

Install the tRPC Libraries and Zod for schema and parameter validation.

```jsx
npm install @trpc/server @trpc/client trpc-nuxt zod
```

## Configuration

modify `nuxt.config.ts` to transpile that into an ES5 bundle. 

```jsx
export default defineNuxtConfig({
  build: {
    transpile: ['trpc-nuxt']
  }
})
```

## Add Boilerplate Code

Entrypoint into trpc server; the embedded documentation explains what is going on.

```tsx
// server/trpc/trpc.ts

/**
 * 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;
```

The `context`, this is where you can put things that you want all routes to have access to. We will be putting out database client here so we can access it through the context in our routes

```tsx
// server/trpc/context.ts

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>;
```

The routes, the can be broken up into separate files so we will place an index file in the `/trpc/routers` directory to support that in the future, but for now we will just add the routes/endpoints directly to the router

```tsx
// server/trpc/routers/index.ts

import { z } from 'zod';
import { publicProcedure, router } from '../trpc';

export const appRouter = router({
  hello: publicProcedure
    .input(
      z.object({
        text: z.string().nullish(),
      })
    )
    .query(({ input }) => {
      return {
        greeting: `hello ${input?.text ?? 'world'}`,
      };
    }),
});

// export type definition of API
export type AppRouter = typeof appRouter;
```
The `hello` route has an optional string parameter `text` which is used in the `query` to return the greeting with the `text` parameter value or the static string `hello`.


Next create the tRPC Plugin so you can access the trpc client throughout your application. 

You will be able to access the client using `const { $trpcClient } = useNuxtApp();` anywhere in you nuxt app client.

```tsx
// plugins/trpc-client.ts

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 trpcClient = createTRPCNuxtClient<AppRouter>({
    links: [
      httpBatchLink({
        url: '/api/trpc',
      }),
    ],
  });

  return {
    provide: {
      trpcClient,
    },
  };
});
```

Next we create and initialize the API Handler using the`appRouter` we create and we pass in the function to create the context 

```tsx
// server/api/trpc/[trpc].ts

import { createNuxtApiHandler } from 'trpc-nuxt';
import { appRouter } from '../../trpc/routers';
import { createContext } from '../../trpc/context';

// export API handler
export default createNuxtApiHandler({
  router: appRouter,
  createContext,
});
```

## Test Code In Application
Now that everything is in place we can test the api call in the `app.vue`

Code to call api from client

```tsx
// app.vue

<script setup lang="ts">
const { $trpcClient } = useNuxtApp();
console.log($trpcClient);

const { data } = await $trpcClient.hello.useQuery({text : "smith"});
</script>

<template>
  <p>app</p>
  <div>
    <p>{{ data?.greeting }}</p>
  </div>
</template>
```
---

### Source Code
{% github aaronksaunders/trpc-nuxt-video-app %}


### Links
- trpc-nuxt documentation - https://trpc-nuxt.vercel.app/
- trpc documentation - https://trpc.io/

### Social Media 
- Twitter - https://twitter.com/aaronksaunders
- Facebook - https://www.facebook.com/ClearlyInnovativeInc
- Instagram - https://www.instagram.com/aaronksaunders/
- Clearly Innovative Inc - https://www.clearlyinnovative.com