diff --git a/components/AppHeader.vue b/components/AppHeader.vue index 94c2cb4..97ef0b9 100644 --- a/components/AppHeader.vue +++ b/components/AppHeader.vue @@ -1,5 +1,4 @@ diff --git a/plugins/trpcClient.ts b/plugins/trpcClient.ts new file mode 100644 index 0000000..62c7d59 --- /dev/null +++ b/plugins/trpcClient.ts @@ -0,0 +1,22 @@ +import { createTRPCNuxtClient, httpBatchLink } from 'trpc-nuxt/client' +import type { AppRouter } from '~/server/api/trpc/[trpc]' + +export default defineNuxtPlugin(() => { + /** + * createTRPCNuxtClient adds a `useQuery` composable + * built on top of `useAsyncData`. + */ + const client = createTRPCNuxtClient({ + links: [ + httpBatchLink({ + url: '/api/trpc', + }), + ], + }) + + return { + provide: { + client, + }, + } +}) diff --git a/server/api/notes.ts b/server/api/notes.ts deleted file mode 100644 index 244994d..0000000 --- a/server/api/notes.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { PrismaClient } from '@prisma/client'; -import { serverSupabaseClient } from '#supabase/server'; - -const prisma = new PrismaClient(); - -export default defineEventHandler(async (event) => { - const client = serverSupabaseClient(event) - const user = await client.auth.getUser(); - - const dbUser = await prisma.user.findFirstOrThrow({ - where: { - supabase_uid: user.data.id // TODO - this shit is messy.. typing - }, - include: { - membership: true, // Return all fields - }, - }); - - const data = await prisma.note.findMany({ - where:{ - account_id: dbUser.membership?.account_id - } - }); - return data; -}) \ No newline at end of file diff --git a/server/api/trpc/[trpc].ts b/server/api/trpc/[trpc].ts new file mode 100644 index 0000000..0d9e5fc --- /dev/null +++ b/server/api/trpc/[trpc].ts @@ -0,0 +1,37 @@ +/** + * This is the API-handler of your app that contains all your API routes. + * On a bigger app, you will probably want to split this file up into multiple files. + */ +import { createNuxtApiHandler } from 'trpc-nuxt' +import { z } from 'zod' + +import { publicProcedure, router } from '~/server/trpc/trpc' +import { createContext } from '~~/server/trpc/context'; +import NotesService from '~~/lib/services/notes.service'; + +export const appRouter = router({ + notes: publicProcedure + .input( + z.object({ + text: z.string().nullish(), + }), + ) + .query(async ({ ctx, input }) => { + const notesService = new NotesService(ctx.prisma); + const notes = await notesService.getNotesForAccountId(ctx.dbUser.membership?.account_id); + return { + notes, + } + }), +}) + +// export only the type definition of the API +// None of the actual implementation is exposed to the client +export type AppRouter = typeof appRouter; + +// export API handler +export default createNuxtApiHandler({ + router: appRouter, + createContext: createContext, + onError({ error}) { console.error(error)}, // TODO - logging and reporting +}) diff --git a/server/trpc/context.ts b/server/trpc/context.ts new file mode 100644 index 0000000..7b3b257 --- /dev/null +++ b/server/trpc/context.ts @@ -0,0 +1,43 @@ +import { PrismaClient } from '@prisma/client'; +import type { inferAsyncReturnType } from '@trpc/server' +import { H3Event } from 'h3'; +import { serverSupabaseClient } from '#supabase/server'; +import SupabaseClient from '@supabase/supabase-js/dist/module/SupabaseClient'; +import { User } from '@supabase/supabase-js'; +import UserService from '~~/lib/services/user.service'; + +let prisma: PrismaClient | undefined +let supabase: SupabaseClient | undefined +let user: User | null = null; +let dbUser: any | undefined + +export async function createContext(event: H3Event){ + if (!supabase) { + supabase = serverSupabaseClient(event) + } + if (!user) { + ({data: { user }} = await supabase.auth.getUser()); + } + if (!prisma) { + prisma = new PrismaClient() + } + if (!dbUser && user) { + const userService = new UserService(prisma); + dbUser = await userService.getUserBySupabaseId(user.id); + + if (!dbUser && user) { + dbUser = await userService.createUser( user.id, user.user_metadata.full_name ); + console.log(`\n Created user \n ${JSON.stringify(dbUser)}\n`); + } + } + + // TODO - This seems excessive, trim context when I have figured out what I actually need + return { + supabase, + user, + prisma, + dbUser, + } +}; + +export type Context = inferAsyncReturnType \ No newline at end of file diff --git a/server/trpc/trpc.ts b/server/trpc/trpc.ts new file mode 100644 index 0000000..8fceee3 --- /dev/null +++ b/server/trpc/trpc.ts @@ -0,0 +1,21 @@ +/** + * 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 './context'; + +const t = initTRPC.context().create() + +/** + * Unprotected procedure + **/ +export const publicProcedure = t.procedure; + +export const router = t.router; +export const middleware = t.middleware;