JavascriptMick
JavascriptMick
Explore posts from servers
NNuxt
Created by JavascriptMick on 8/29/2024 in #❓・help
Real cost of scaling Nuxt on netlify
I have a web app in Nuxt3 hosted on netlify. Trying to figure out how the cost will scale as I go live. The website will get some traffic but is largely brochure but it also serves a rest API that will be accessed via a mobile app. This will get smashed as I scale. Netlify Pro gives me only 125k serverless function invitations per month. If I have 1000 users (I'm b2c) that's only 4 API calls per user per day... That's not going to work. Same plan gives me 2 million edge function invocations gives me 600 API calls per user per day .. should be ok. Do I need to use the edge preset for this? SERVER_PRESET=netlify_edge I guess I'm just looking for some advice from anyone who has actually scaled out a Nuxt backend. Did you use netlify or similar or go to VPS? Did costs blow out? Did you need to refactor? Any help or refs appreciated
6 replies
NNuxt
Created by JavascriptMick on 2/17/2024 in #❓・help
Can I put multiple Favicon assets in public?
No description
2 replies
TtRPC
Created by JavascriptMick on 6/2/2023 in #❓-help
Use onError to change an application error into a TRPCError?
I want to use the onError handler to change any instance of a custom application error into a TRPCError (I want the HTTP Status code to be a 401 rather than a 500). I tried this but it doesn't work...
onError({error}) {
if (error instanceof AccountLimitError){
error = new TRPCError({ code: 'UNAUTHORIZED', message:error.message })
}
}
onError({error}) {
if (error instanceof AccountLimitError){
error = new TRPCError({ code: 'UNAUTHORIZED', message:error.message })
}
}
nor does this
onError({error}) {
if (error instanceof AccountLimitError){
const trpcError = new TRPCError({ code: 'UNAUTHORIZED', message:error.message })
error = Object.assign(error, trpcError)
}
}
onError({error}) {
if (error instanceof AccountLimitError){
const trpcError = new TRPCError({ code: 'UNAUTHORIZED', message:error.message })
error = Object.assign(error, trpcError)
}
}
I can set individual properties on the error object like error.message but thought there might be an easier way. Has anybody got a nifty hack for this?
8 replies
NNuxt
Created by JavascriptMick on 5/7/2023 in #❓・help
What are people using for Toasts?
No description
10 replies
NNuxt
Created by JavascriptMick on 4/13/2023 in #❓・help
Best Design system for SAAS app?
I'm shite at CSS but need to make my SAAS site look pretty. In the past I have used Bootstrap for everything but am open to new approaches. I guess important features are:- - works good with Nuxt 3 - lots of example snippets - possibility of theming
10 replies
NNuxt
Created by JavascriptMick on 4/7/2023 in #❓・help
Global onMounted for Pinia initialisation?
I have an initialisation method on my Pinia store that I want to invoke before any other store stuff is invoked. I am currently using onMounted on a header component but it feels like a hack and doesn't always fire. AppHeader.vue script setup...
onMounted(async () => {
await authStore.initUser();
});
onMounted(async () => {
await authStore.initUser();
});
Is there a better approach for global store initialisation? maybe onMounted for the app.vue? or in defineNuxtConfig 'ready' hook?
8 replies
TtRPC
Created by JavascriptMick on 2/25/2023 in #❓-help
is context cached?
If I put an object on the context that represents the User record from my database...
export async function createContext(event: H3Event){
if (!dbUser) {
const userService = new UserService();
dbUser = await userService.getUser(user_id);
}

return {
dbUser,
blah....
}
};
export async function createContext(event: H3Event){
if (!dbUser) {
const userService = new UserService();
dbUser = await userService.getUser(user_id);
}

return {
dbUser,
blah....
}
};
And then I have a router function which changes important details of the user....
changeAccountLevel: protectedProcedure
.input(z.object({ user_id: z.number(), level: z.number() }))
.query(async ({ ctx, input }) => {
const userService = new UserService();
const user = await userService.changeAccountLevel(input.user_id, input.level);
return {
user,
}
}),
changeAccountLevel: protectedProcedure
.input(z.object({ user_id: z.number(), level: z.number() }))
.query(async ({ ctx, input }) => {
const userService = new UserService();
const user = await userService.changeAccountLevel(input.user_id, input.level);
return {
user,
}
}),
Do I need to 'mutate' the dbUser on the context....
ctx.dbUser = user
ctx.dbUser = user
or will subsequent calls to routes, re-invoke createContext and re-load the user from the database?
7 replies
TtRPC
Created by JavascriptMick on 2/23/2023 in #❓-help
Best way to implement input based validation on a router procedure
Hi guys, bit of a noob. I have already created a 'protectedProcedure', ensuring the user is logged in, but for some of my procedures, I also want to ensure the user is an ADMIN for the account specified on the input. This is my first try with validation just added at the top of the procedure implementation...
changeUserAccessWithinAccount: protectedProcedure
.input(z.object({ user_id: z.number(), account_id: z.number(), access: z.enum([ACCOUNT_ACCESS.ADMIN, ACCOUNT_ACCESS.OWNER, ACCOUNT_ACCESS.READ_ONLY, ACCOUNT_ACCESS.READ_WRITE]) }))
.query(async ({ ctx, input }) => {
// validate that the context user is an admin within the account specified as an input param.... where should this go.. an additional input parser?
const test_membership = ctx.dbUser.memberships.find(membership => membership.account_id == input.account_id);
if(!test_membership || (test_membership?.access !== ACCOUNT_ACCESS.ADMIN && test_membership?.access !== ACCOUNT_ACCESS.OWNER)) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
//....do the thing...
changeUserAccessWithinAccount: protectedProcedure
.input(z.object({ user_id: z.number(), account_id: z.number(), access: z.enum([ACCOUNT_ACCESS.ADMIN, ACCOUNT_ACCESS.OWNER, ACCOUNT_ACCESS.READ_ONLY, ACCOUNT_ACCESS.READ_WRITE]) }))
.query(async ({ ctx, input }) => {
// validate that the context user is an admin within the account specified as an input param.... where should this go.. an additional input parser?
const test_membership = ctx.dbUser.memberships.find(membership => membership.account_id == input.account_id);
if(!test_membership || (test_membership?.access !== ACCOUNT_ACCESS.ADMIN && test_membership?.access !== ACCOUNT_ACCESS.OWNER)) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
//....do the thing...
Is there a way to implement this with multiple input parsers (https://trpc.io/docs/procedures#multiple-input-parsers) ?? what would that look like?
15 replies
NNuxt
Created by JavascriptMick on 2/13/2023 in #❓・help
useState + watch + async fetch... should I just use Pinia at this point?
Hi All, New to Nuxt 3. I am using useState to hold my application state. I want to respond to a change in a state property (activeMembership) by doing a fetch (trpc/Prisma) and then using the fetch to update another bit of the state (notes). The following works ok but feels pretty clumsy.
// composables/states.ts
import { Membership, Note } from ".prisma/client"

export type AppState = {
activeMembership?: Membership
notes: Note[]
}

export const appState = () => useState<AppState>('appState', () => ({
notes: []
}));

// pages/dashboard.vue
const theAppState = appState();
watch(theAppState.value, (newAppState) => {
if(newAppState.activeMembership){
const { data: foundNotes } = $client.notes.getForCurrentUser.useQuery({account_id: newAppState.activeMembership.account_id});
if(foundNotes.value?.notes){
theAppState.value.notes = foundNotes.value.notes;
}
}
});
// composables/states.ts
import { Membership, Note } from ".prisma/client"

export type AppState = {
activeMembership?: Membership
notes: Note[]
}

export const appState = () => useState<AppState>('appState', () => ({
notes: []
}));

// pages/dashboard.vue
const theAppState = appState();
watch(theAppState.value, (newAppState) => {
if(newAppState.activeMembership){
const { data: foundNotes } = $client.notes.getForCurrentUser.useQuery({account_id: newAppState.activeMembership.account_id});
if(foundNotes.value?.notes){
theAppState.value.notes = foundNotes.value.notes;
}
}
});
Specifically, it feels strange to use a watch, it feels even stranger to put the watch + fetch in the setup script for the dashboard. I'm more familiar with Vuex where this would be an Action inside which I would dispatch a mutation to change the property (activeMembership) , then do the fetch, then dispatch another mutation to update the notes. No watches, central store, neat and tidy. Is there a neater way to utilise useState or do I just quit and move to Vuex/Pinia at this point?
3 replies