introduce TRPC and service layer

This commit is contained in:
Michael Dausmann
2023-02-05 11:55:49 +11:00
parent 791192a1ae
commit bba070d985
13 changed files with 307 additions and 58 deletions

View File

@@ -1,5 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
console.log('AppHeader.vuew - setup script');
const supabase = useSupabaseAuthClient(); const supabase = useSupabaseAuthClient();
const user = useSupabaseUser(); const user = useSupabaseUser();

View File

@@ -0,0 +1,29 @@
import { PrismaClient } from '@prisma/client';
export default class NotesService {
private prisma: PrismaClient;
constructor( prisma: PrismaClient) {
this.prisma = prisma;
}
async getAllNotes() {
return this.prisma.note.findMany();
}
async getNotesForAccountId(account_id: number) {
return this.prisma.note.findMany({ where: { account_id } });
}
async createNote( account_id: number, note_text: string ) {
return this.prisma.note.create({ data: { account_id, note_text }});
}
async updateNote(id: number, note_text: string) {
return this.prisma.note.update({ where: { id }, data: { note_text } });
}
async deleteNote(id: number) {
return this.prisma.note.delete({ where: { id } });
}
}

View File

@@ -0,0 +1,47 @@
import { PrismaClient } from '@prisma/client';
import { UtilService } from './util.service';
const TRIAL_PLAN_NAME = '3 Month Trial'; // TODO - some sort of config.. this will change for every use of the boilerplate
export default class UserService {
private prisma: PrismaClient;
constructor( prisma: PrismaClient) {
this.prisma = prisma;
}
async getUserBySupabaseId(supabase_uid: string) {
return this.prisma.user.findFirst({ where: { supabase_uid }, include: { membership: true } });
}
async getUserById(id: number) {
return this.prisma.user.findFirstOrThrow({ where: { id }, include: { membership: true } });
}
async createUser( supabase_uid: string, display_name: string ) {
const trialPlan = await this.prisma.plan.findFirstOrThrow({ where: { name: TRIAL_PLAN_NAME}});
return this.prisma.user.create({
data:{
supabase_uid: supabase_uid,
display_name: display_name,
membership: {
create: {
account: {
create: {
plan_id: trialPlan.id,
name: display_name,
features: trialPlan.features, //copy in features from the plan, plan_id is a loose association and settings can change independently
current_period_ends: UtilService.addMonths(new Date(),3),
}
}
}
}
},
include: { membership: true },
});
}
async deleteUser(id: number) {
return this.prisma.user.delete({ where: { id } });
}
}

View File

@@ -0,0 +1,10 @@
export class UtilService {
public static addMonths(date: Date, months: number): Date {
const d = date.getDate();
date.setMonth(date.getMonth() + +months);
if (date.getDate() != d) {
date.setDate(0);
}
return date;
}
}

View File

@@ -1,5 +1,8 @@
// https://nuxt.com/docs/api/configuration/nuxt-config // https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({ export default defineNuxtConfig({
build: {
transpile: ['trpc-nuxt']
},
typescript: { typescript: {
shim: false shim: false
}, },

112
package-lock.json generated
View File

@@ -5,6 +5,12 @@
"packages": { "packages": {
"": { "": {
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": {
"@trpc/client": "^10.9.0",
"@trpc/server": "^10.9.0",
"trpc-nuxt": "^0.5.0",
"zod": "^3.20.2"
},
"devDependencies": { "devDependencies": {
"@nuxtjs/supabase": "^0.3.0", "@nuxtjs/supabase": "^0.3.0",
"@prisma/client": "^4.9.0", "@prisma/client": "^4.9.0",
@@ -1581,6 +1587,19 @@
"cross-fetch": "^3.1.5" "cross-fetch": "^3.1.5"
} }
}, },
"node_modules/@trpc/client": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/@trpc/client/-/client-10.9.0.tgz",
"integrity": "sha512-id6318qpgqllNOuBp7nuciXFPXCe+zae5d4r1hze6Eyp5fFFNO58TqA+4Q44KIcHgpfWyW2egs6iPeql3PrdKA==",
"peerDependencies": {
"@trpc/server": "10.9.0"
}
},
"node_modules/@trpc/server": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.9.0.tgz",
"integrity": "sha512-5psyZgbvy29pJND32hwkWTMbv6s86IbsPOeDopsgNF0VegZT6Dsijmb7Ub/TDhuJVQVq5u4u5briMXi3SxmBkw=="
},
"node_modules/@trysound/sax": { "node_modules/@trysound/sax": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
@@ -2851,8 +2870,7 @@
"node_modules/cookie-es": { "node_modules/cookie-es": {
"version": "0.5.0", "version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-0.5.0.tgz", "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-0.5.0.tgz",
"integrity": "sha512-RyZrFi6PNpBFbIaQjXDlFIhFVqV42QeKSZX1yQIl6ihImq6vcHNGMtqQ/QzY3RMPuYSkvsRwtnt5M9NeYxKt0g==", "integrity": "sha512-RyZrFi6PNpBFbIaQjXDlFIhFVqV42QeKSZX1yQIl6ihImq6vcHNGMtqQ/QzY3RMPuYSkvsRwtnt5M9NeYxKt0g=="
"dev": true
}, },
"node_modules/core-util-is": { "node_modules/core-util-is": {
"version": "1.0.3", "version": "1.0.3",
@@ -3207,8 +3225,7 @@
"node_modules/destr": { "node_modules/destr": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/destr/-/destr-1.2.2.tgz", "resolved": "https://registry.npmjs.org/destr/-/destr-1.2.2.tgz",
"integrity": "sha512-lrbCJwD9saUQrqUfXvl6qoM+QN3W7tLV5pAOs+OqOmopCCz/JkE05MHedJR1xfk4IAnZuJXPVuN5+7jNA2ZCiA==", "integrity": "sha512-lrbCJwD9saUQrqUfXvl6qoM+QN3W7tLV5pAOs+OqOmopCCz/JkE05MHedJR1xfk4IAnZuJXPVuN5+7jNA2ZCiA=="
"dev": true
}, },
"node_modules/destroy": { "node_modules/destroy": {
"version": "1.2.0", "version": "1.2.0",
@@ -4050,7 +4067,6 @@
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/h3/-/h3-1.1.0.tgz", "resolved": "https://registry.npmjs.org/h3/-/h3-1.1.0.tgz",
"integrity": "sha512-kx3u+RMzY963fU8NNT2ePWgsryAn9DNztPqbHia/M7HgA+rtXKjHjED9/uidcYPmImNwAfJsCachCzh2T3QH2A==", "integrity": "sha512-kx3u+RMzY963fU8NNT2ePWgsryAn9DNztPqbHia/M7HgA+rtXKjHjED9/uidcYPmImNwAfJsCachCzh2T3QH2A==",
"dev": true,
"dependencies": { "dependencies": {
"cookie-es": "^0.5.0", "cookie-es": "^0.5.0",
"destr": "^1.2.2", "destr": "^1.2.2",
@@ -5156,8 +5172,7 @@
"node_modules/node-fetch-native": { "node_modules/node-fetch-native": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.0.1.tgz", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.0.1.tgz",
"integrity": "sha512-VzW+TAk2wE4X9maiKMlT+GsPU4OMmR1U9CrHSmd3DFLn2IcZ9VJ6M6BBugGfYUnPCLSYxXdZy17M0BEJyhUTwg==", "integrity": "sha512-VzW+TAk2wE4X9maiKMlT+GsPU4OMmR1U9CrHSmd3DFLn2IcZ9VJ6M6BBugGfYUnPCLSYxXdZy17M0BEJyhUTwg=="
"dev": true
}, },
"node_modules/node-forge": { "node_modules/node-forge": {
"version": "1.3.1", "version": "1.3.1",
@@ -5366,7 +5381,6 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.0.0.tgz", "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.0.0.tgz",
"integrity": "sha512-d40aof8czZFSQKJa4+F7Ch3UC5D631cK1TTUoK+iNEut9NoiCL+u0vykl/puYVUS2df4tIQl5upQcolIcEzQjQ==", "integrity": "sha512-d40aof8czZFSQKJa4+F7Ch3UC5D631cK1TTUoK+iNEut9NoiCL+u0vykl/puYVUS2df4tIQl5upQcolIcEzQjQ==",
"dev": true,
"dependencies": { "dependencies": {
"destr": "^1.2.1", "destr": "^1.2.1",
"node-fetch-native": "^1.0.1", "node-fetch-native": "^1.0.1",
@@ -5376,8 +5390,7 @@
"node_modules/ohash": { "node_modules/ohash": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/ohash/-/ohash-1.0.0.tgz", "resolved": "https://registry.npmjs.org/ohash/-/ohash-1.0.0.tgz",
"integrity": "sha512-kxSyzq6tt+6EE/xCnD1XaFhCCjUNUaz3X30rJp6mnjGLXAAvuPFqohMdv0aScWzajR45C29HyBaXZ8jXBwnh9A==", "integrity": "sha512-kxSyzq6tt+6EE/xCnD1XaFhCCjUNUaz3X30rJp6mnjGLXAAvuPFqohMdv0aScWzajR45C29HyBaXZ8jXBwnh9A=="
"dev": true
}, },
"node_modules/on-finished": { "node_modules/on-finished": {
"version": "2.4.1", "version": "2.4.1",
@@ -6233,8 +6246,7 @@
"node_modules/radix3": { "node_modules/radix3": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/radix3/-/radix3-1.0.0.tgz", "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.0.0.tgz",
"integrity": "sha512-6n3AEXth91ASapMVKiEh2wrbFJmI+NBilrWE0AbiGgfm0xet0QXC8+a3K19r1UVYjUjctUgB053c3V/J6V0kCQ==", "integrity": "sha512-6n3AEXth91ASapMVKiEh2wrbFJmI+NBilrWE0AbiGgfm0xet0QXC8+a3K19r1UVYjUjctUgB053c3V/J6V0kCQ=="
"dev": true
}, },
"node_modules/randombytes": { "node_modules/randombytes": {
"version": "2.1.0", "version": "2.1.0",
@@ -7117,6 +7129,24 @@
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"dev": true "dev": true
}, },
"node_modules/trpc-nuxt": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/trpc-nuxt/-/trpc-nuxt-0.5.0.tgz",
"integrity": "sha512-PZpmwc2QYkC1CWxGo3brFwou88zoP6cB9+GmlIffJJLKCmTY7XzIfarNxSbCocdeymJjMpcL/94t9kJm85/eTQ==",
"dependencies": {
"h3": "^1.0.2",
"ofetch": "^1.0.0",
"ohash": "^1.0.0",
"ufo": "^1.0.1"
},
"engines": {
"node": "^16.13.0 || ^18.12.0"
},
"peerDependencies": {
"@trpc/client": "^10.8.0",
"@trpc/server": "^10.8.0"
}
},
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.4.1", "version": "2.4.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
@@ -7153,8 +7183,7 @@
"node_modules/ufo": { "node_modules/ufo": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.0.1.tgz", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.0.1.tgz",
"integrity": "sha512-boAm74ubXHY7KJQZLlXrtMz52qFvpsbOxDcZOnw/Wf+LS4Mmyu7JxmzD4tDLtUQtmZECypJ0FrCz4QIe6dvKRA==", "integrity": "sha512-boAm74ubXHY7KJQZLlXrtMz52qFvpsbOxDcZOnw/Wf+LS4Mmyu7JxmzD4tDLtUQtmZECypJ0FrCz4QIe6dvKRA=="
"dev": true
}, },
"node_modules/ultrahtml": { "node_modules/ultrahtml": {
"version": "1.2.0", "version": "1.2.0",
@@ -8431,6 +8460,14 @@
"engines": { "engines": {
"node": ">= 10" "node": ">= 10"
} }
},
"node_modules/zod": {
"version": "3.20.2",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.20.2.tgz",
"integrity": "sha512-1MzNQdAvO+54H+EaK5YpyEy0T+Ejo/7YLHS93G3RnYWh5gaotGHwGeN/ZO687qEDU2y4CdStQYXVHIgrUl5UVQ==",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
} }
}, },
"dependencies": { "dependencies": {
@@ -9511,6 +9548,17 @@
"cross-fetch": "^3.1.5" "cross-fetch": "^3.1.5"
} }
}, },
"@trpc/client": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/@trpc/client/-/client-10.9.0.tgz",
"integrity": "sha512-id6318qpgqllNOuBp7nuciXFPXCe+zae5d4r1hze6Eyp5fFFNO58TqA+4Q44KIcHgpfWyW2egs6iPeql3PrdKA==",
"requires": {}
},
"@trpc/server": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.9.0.tgz",
"integrity": "sha512-5psyZgbvy29pJND32hwkWTMbv6s86IbsPOeDopsgNF0VegZT6Dsijmb7Ub/TDhuJVQVq5u4u5briMXi3SxmBkw=="
},
"@trysound/sax": { "@trysound/sax": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
@@ -10474,8 +10522,7 @@
"cookie-es": { "cookie-es": {
"version": "0.5.0", "version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-0.5.0.tgz", "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-0.5.0.tgz",
"integrity": "sha512-RyZrFi6PNpBFbIaQjXDlFIhFVqV42QeKSZX1yQIl6ihImq6vcHNGMtqQ/QzY3RMPuYSkvsRwtnt5M9NeYxKt0g==", "integrity": "sha512-RyZrFi6PNpBFbIaQjXDlFIhFVqV42QeKSZX1yQIl6ihImq6vcHNGMtqQ/QzY3RMPuYSkvsRwtnt5M9NeYxKt0g=="
"dev": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.3", "version": "1.0.3",
@@ -10735,8 +10782,7 @@
"destr": { "destr": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/destr/-/destr-1.2.2.tgz", "resolved": "https://registry.npmjs.org/destr/-/destr-1.2.2.tgz",
"integrity": "sha512-lrbCJwD9saUQrqUfXvl6qoM+QN3W7tLV5pAOs+OqOmopCCz/JkE05MHedJR1xfk4IAnZuJXPVuN5+7jNA2ZCiA==", "integrity": "sha512-lrbCJwD9saUQrqUfXvl6qoM+QN3W7tLV5pAOs+OqOmopCCz/JkE05MHedJR1xfk4IAnZuJXPVuN5+7jNA2ZCiA=="
"dev": true
}, },
"destroy": { "destroy": {
"version": "1.2.0", "version": "1.2.0",
@@ -11374,7 +11420,6 @@
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/h3/-/h3-1.1.0.tgz", "resolved": "https://registry.npmjs.org/h3/-/h3-1.1.0.tgz",
"integrity": "sha512-kx3u+RMzY963fU8NNT2ePWgsryAn9DNztPqbHia/M7HgA+rtXKjHjED9/uidcYPmImNwAfJsCachCzh2T3QH2A==", "integrity": "sha512-kx3u+RMzY963fU8NNT2ePWgsryAn9DNztPqbHia/M7HgA+rtXKjHjED9/uidcYPmImNwAfJsCachCzh2T3QH2A==",
"dev": true,
"requires": { "requires": {
"cookie-es": "^0.5.0", "cookie-es": "^0.5.0",
"destr": "^1.2.2", "destr": "^1.2.2",
@@ -12237,8 +12282,7 @@
"node-fetch-native": { "node-fetch-native": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.0.1.tgz", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.0.1.tgz",
"integrity": "sha512-VzW+TAk2wE4X9maiKMlT+GsPU4OMmR1U9CrHSmd3DFLn2IcZ9VJ6M6BBugGfYUnPCLSYxXdZy17M0BEJyhUTwg==", "integrity": "sha512-VzW+TAk2wE4X9maiKMlT+GsPU4OMmR1U9CrHSmd3DFLn2IcZ9VJ6M6BBugGfYUnPCLSYxXdZy17M0BEJyhUTwg=="
"dev": true
}, },
"node-forge": { "node-forge": {
"version": "1.3.1", "version": "1.3.1",
@@ -12398,7 +12442,6 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.0.0.tgz", "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.0.0.tgz",
"integrity": "sha512-d40aof8czZFSQKJa4+F7Ch3UC5D631cK1TTUoK+iNEut9NoiCL+u0vykl/puYVUS2df4tIQl5upQcolIcEzQjQ==", "integrity": "sha512-d40aof8czZFSQKJa4+F7Ch3UC5D631cK1TTUoK+iNEut9NoiCL+u0vykl/puYVUS2df4tIQl5upQcolIcEzQjQ==",
"dev": true,
"requires": { "requires": {
"destr": "^1.2.1", "destr": "^1.2.1",
"node-fetch-native": "^1.0.1", "node-fetch-native": "^1.0.1",
@@ -12408,8 +12451,7 @@
"ohash": { "ohash": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/ohash/-/ohash-1.0.0.tgz", "resolved": "https://registry.npmjs.org/ohash/-/ohash-1.0.0.tgz",
"integrity": "sha512-kxSyzq6tt+6EE/xCnD1XaFhCCjUNUaz3X30rJp6mnjGLXAAvuPFqohMdv0aScWzajR45C29HyBaXZ8jXBwnh9A==", "integrity": "sha512-kxSyzq6tt+6EE/xCnD1XaFhCCjUNUaz3X30rJp6mnjGLXAAvuPFqohMdv0aScWzajR45C29HyBaXZ8jXBwnh9A=="
"dev": true
}, },
"on-finished": { "on-finished": {
"version": "2.4.1", "version": "2.4.1",
@@ -12985,8 +13027,7 @@
"radix3": { "radix3": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/radix3/-/radix3-1.0.0.tgz", "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.0.0.tgz",
"integrity": "sha512-6n3AEXth91ASapMVKiEh2wrbFJmI+NBilrWE0AbiGgfm0xet0QXC8+a3K19r1UVYjUjctUgB053c3V/J6V0kCQ==", "integrity": "sha512-6n3AEXth91ASapMVKiEh2wrbFJmI+NBilrWE0AbiGgfm0xet0QXC8+a3K19r1UVYjUjctUgB053c3V/J6V0kCQ=="
"dev": true
}, },
"randombytes": { "randombytes": {
"version": "2.1.0", "version": "2.1.0",
@@ -13654,6 +13695,17 @@
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"dev": true "dev": true
}, },
"trpc-nuxt": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/trpc-nuxt/-/trpc-nuxt-0.5.0.tgz",
"integrity": "sha512-PZpmwc2QYkC1CWxGo3brFwou88zoP6cB9+GmlIffJJLKCmTY7XzIfarNxSbCocdeymJjMpcL/94t9kJm85/eTQ==",
"requires": {
"h3": "^1.0.2",
"ofetch": "^1.0.0",
"ohash": "^1.0.0",
"ufo": "^1.0.1"
}
},
"tslib": { "tslib": {
"version": "2.4.1", "version": "2.4.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
@@ -13684,8 +13736,7 @@
"ufo": { "ufo": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.0.1.tgz", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.0.1.tgz",
"integrity": "sha512-boAm74ubXHY7KJQZLlXrtMz52qFvpsbOxDcZOnw/Wf+LS4Mmyu7JxmzD4tDLtUQtmZECypJ0FrCz4QIe6dvKRA==", "integrity": "sha512-boAm74ubXHY7KJQZLlXrtMz52qFvpsbOxDcZOnw/Wf+LS4Mmyu7JxmzD4tDLtUQtmZECypJ0FrCz4QIe6dvKRA=="
"dev": true
}, },
"ultrahtml": { "ultrahtml": {
"version": "1.2.0", "version": "1.2.0",
@@ -14525,6 +14576,11 @@
"compress-commons": "^4.1.0", "compress-commons": "^4.1.0",
"readable-stream": "^3.6.0" "readable-stream": "^3.6.0"
} }
},
"zod": {
"version": "3.20.2",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.20.2.tgz",
"integrity": "sha512-1MzNQdAvO+54H+EaK5YpyEy0T+Ejo/7YLHS93G3RnYWh5gaotGHwGeN/ZO687qEDU2y4CdStQYXVHIgrUl5UVQ=="
} }
} }
} }

View File

@@ -12,5 +12,11 @@
"@prisma/client": "^4.9.0", "@prisma/client": "^4.9.0",
"nuxt": "^3.1.1", "nuxt": "^3.1.1",
"prisma": "^4.9.0" "prisma": "^4.9.0"
},
"dependencies": {
"@trpc/client": "^10.9.0",
"@trpc/server": "^10.9.0",
"trpc-nuxt": "^0.5.0",
"zod": "^3.20.2"
} }
} }

View File

@@ -4,12 +4,13 @@
middleware: ['auth'], middleware: ['auth'],
}); });
const notes = await $fetch('/api/notes') const { $client } = useNuxtApp()
const { data: notes } = await $client.notes.useQuery({ text: 'client' })
</script> </script>
<template> <template>
<div> <div>
<h3>{{ user?.user_metadata.full_name }}'s Dashboard</h3> <h3>{{ user?.user_metadata.full_name }}'s Notes Dashboard</h3>
<p v-for="note in notes">{{ note.note_text }}</p><!-- TODO - wtf.. typing--> <p v-for="note in notes?.notes">{{ note.note_text }}</p>
<div>Stuff</div>
</div> </div>
</template> </template>

22
plugins/trpcClient.ts Normal file
View File

@@ -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<AppRouter>({
links: [
httpBatchLink({
url: '/api/trpc',
}),
],
})
return {
provide: {
client,
},
}
})

View File

@@ -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;
})

37
server/api/trpc/[trpc].ts Normal file
View File

@@ -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
})

43
server/trpc/context.ts Normal file
View File

@@ -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<typeof createContext>

21
server/trpc/trpc.ts Normal file
View File

@@ -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<Context>().create()
/**
* Unprotected procedure
**/
export const publicProcedure = t.procedure;
export const router = t.router;
export const middleware = t.middleware;