Build A Full-Stack Typescript Application with Nuxt and tRPC

Build A Full-Stack Typescript Application with Nuxt and tRPC

ยท

4 min read

tRpc - 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 application up and running with tRpc using the trpc-nuxt module.

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.

tPRC Nuxt Module - wobsoriano/trpc-nuxt Documentation

Installation

Create a Nuxt 3 App using the cli

npx nuxi@latest init trpc-nuxt-app

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

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

Configuration

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

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

Add Boilerplate Code

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

// 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

// 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, 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

// 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 your nuxt app client.

// 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 theappRouter we create and we pass in the function to create the context

// 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

// 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

Social Media

ย