Tamás Soós
Tamás Soós
Explore posts from servers
TTCTheo's Typesafe Cult
Created by quest1onmark on 10/29/2023 in #questions
TRPC server recieves... html?
You're welcome.
28 replies
TTCTheo's Typesafe Cult
Created by quest1onmark on 10/29/2023 in #questions
TRPC server recieves... html?
Oh and getting it to reproduce took longer than finding the cause. The global style import was wrong, then after getting the keys from clerk, it wasn't redirecting me to the login. I was just stuck on the home page. So I had to add a sign in / out button.
28 replies
TTCTheo's Typesafe Cult
Created by quest1onmark on 10/29/2023 in #questions
TRPC server recieves... html?
I had to play around with it a little, but it was mostly about trying to find where the html could come from. After finding the custom redirect response in the middleware I was pretty confident that's the issue.
28 replies
TTCTheo's Typesafe Cult
Created by quest1onmark on 10/29/2023 in #questions
TRPC server recieves... html?
The issue was in the middleware. Your custom redirect logic doesn't work well with tRPC. tRPC is meant to handle JSON formatted data, but the redirect will send some html down the wire. I suppose you wouldn't want to redirect in the middle of the mutation anyway, so one way to handle it would be to add one more condition to your redirect logic like this:
import { authMiddleware, redirectToSignIn } from "@clerk/nextjs";
import { redirect } from "next/navigation";
import { NextResponse } from "next/server";

// This example protects all routes including api/trpc routes
// Please edit this to allow other routes to be public as needed.
// See https://clerk.com/docs/references/nextjs/auth-middleware for more information about configuring your Middleware
export default authMiddleware({
afterAuth(auth, req, _evt) {
if (!auth.userId && !auth.isPublicRoute) {
redirectToSignIn({ returnBackUrl: req.url });
}
if (
auth.userId &&
!auth.user?.firstName &&
!auth.user?.lastName &&
!auth.user?.username &&
!auth.user?.publicMetadata.yearsAtWork &&
req.nextUrl.pathname !== "/sign-up" &&
!req.nextUrl.pathname.match("/(api|trpc)(.*)") // <-- Don't redirect if it's a tRPC API call
) {
const signUp = new URL("/sign-up", req.url);
return NextResponse.redirect(signUp);
}
},
});

export const config = {
matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
};
import { authMiddleware, redirectToSignIn } from "@clerk/nextjs";
import { redirect } from "next/navigation";
import { NextResponse } from "next/server";

// This example protects all routes including api/trpc routes
// Please edit this to allow other routes to be public as needed.
// See https://clerk.com/docs/references/nextjs/auth-middleware for more information about configuring your Middleware
export default authMiddleware({
afterAuth(auth, req, _evt) {
if (!auth.userId && !auth.isPublicRoute) {
redirectToSignIn({ returnBackUrl: req.url });
}
if (
auth.userId &&
!auth.user?.firstName &&
!auth.user?.lastName &&
!auth.user?.username &&
!auth.user?.publicMetadata.yearsAtWork &&
req.nextUrl.pathname !== "/sign-up" &&
!req.nextUrl.pathname.match("/(api|trpc)(.*)") // <-- Don't redirect if it's a tRPC API call
) {
const signUp = new URL("/sign-up", req.url);
return NextResponse.redirect(signUp);
}
},
});

export const config = {
matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
};
28 replies
TTCTheo's Typesafe Cult
Created by quest1onmark on 10/29/2023 in #questions
TRPC server recieves... html?
Yeah I have to agree. Which page where you on? What did you do to get the error, etc...?
28 replies
TTCTheo's Typesafe Cult
Created by Tamás Soós on 10/27/2023 in #questions
Next.js state management and dynamic routes
Here's what I ended up with. The example is using slightly different entities.
6 replies
TTCTheo's Typesafe Cult
Created by quest1onmark on 10/29/2023 in #questions
TRPC server recieves... html?
Could you link to some code?
28 replies
TTCTheo's Typesafe Cult
Created by Tamás Soós on 10/27/2023 in #questions
Next.js app router with tRPC context and NextAuth
My issue is with the createContext call. The opts type isn't quite right. getSession is giving me:
Type 'IncomingMessage | Request' is not assignable to type '(Partial<IncomingMessage> & { body?: any; }) | undefined'.
Type 'Request' is not assignable to type 'Partial<IncomingMessage> & { body?: any; }'.
Type 'Request' is not assignable to type 'Partial<IncomingMessage>'.
Types of property 'headers' are incompatible.
Type 'Headers' is not assignable to type 'IncomingHttpHeaders'.
Index signature for type 'string' is missing in type 'Headers'.ts(2322)
(property) CtxOrReq.req?: (Partial<IncomingMessage> & {
body?: any;
}) | undefined
Type 'IncomingMessage | Request' is not assignable to type '(Partial<IncomingMessage> & { body?: any; }) | undefined'.
Type 'Request' is not assignable to type 'Partial<IncomingMessage> & { body?: any; }'.
Type 'Request' is not assignable to type 'Partial<IncomingMessage>'.
Types of property 'headers' are incompatible.
Type 'Headers' is not assignable to type 'IncomingHttpHeaders'.
Index signature for type 'string' is missing in type 'Headers'.ts(2322)
(property) CtxOrReq.req?: (Partial<IncomingMessage> & {
body?: any;
}) | undefined
even though it happens to be working for now. Btw getSession creates infinite reconnects on the ws client with any other inputs and getServerSession does the same except I couldn't find any arguments that work at all.
6 replies
TTCTheo's Typesafe Cult
Created by Tamás Soós on 10/27/2023 in #questions
Next.js app router with tRPC context and NextAuth
6 replies
TTCTheo's Typesafe Cult
Created by Tamás Soós on 10/27/2023 in #questions
Next.js app router with tRPC context and NextAuth
and a provider component
6 replies
TTCTheo's Typesafe Cult
Created by Tamás Soós on 10/27/2023 in #questions
Next.js state management and dynamic routes
The problem with this is the initialisation. I can't figure out how to properly initialise the global store according to the route. I made an attempt with a custom initialiser component:
'use client';

import { useEffect, useLayoutEffect } from 'react';
import { AppState, useAppStore } from './store';

export function AppStoreInitialiser({
children,
...props
}: React.PropsWithChildren<Partial<AppState>>) {
useEffect(() => {
console.log(`Setting device state: ${JSON.stringify(props)}`);
useAppStore.setState({ ...props });
console.log(useAppStore.getState());
}, []);

return children;
}
'use client';

import { useEffect, useLayoutEffect } from 'react';
import { AppState, useAppStore } from './store';

export function AppStoreInitialiser({
children,
...props
}: React.PropsWithChildren<Partial<AppState>>) {
useEffect(() => {
console.log(`Setting device state: ${JSON.stringify(props)}`);
useAppStore.setState({ ...props });
console.log(useAppStore.getState());
}, []);

return children;
}
I tried to wrap the sub routes with this in template.tsx files so that they set the state properly on every navigation, but useEffect runs first for the inner templates. So given this setup:
...
- layout.tsx
- template.tsx <- AppStoreInitialiser sets deviceId to null
- navBar.tsx
- deviceList.tsx
- page.tsx
- device
- [deviceId]
- layout.tsx <- Get device from db
- template.tsx <- AppStoreInitialiser sets deviceId to dynamic param
- firmwareVersionList.tsx
- page.tsx
...
...
...
- layout.tsx
- template.tsx <- AppStoreInitialiser sets deviceId to null
- navBar.tsx
- deviceList.tsx
- page.tsx
- device
- [deviceId]
- layout.tsx <- Get device from db
- template.tsx <- AppStoreInitialiser sets deviceId to dynamic param
- firmwareVersionList.tsx
- page.tsx
...
...
navigating to /device/2 first sets the store's deviceId to 2 then to null.
6 replies
TTCTheo's Typesafe Cult
Created by Tamás Soós on 10/27/2023 in #questions
Next.js state management and dynamic routes
I had a little more luck with Zustand. I could get rid of the nested contexts. I was able to access the entities as the required type (optional or not) in the right contexts via hooks. Here's my store:
import { Device } from '@prisma/client';
import { create } from 'zustand';

export interface AppState {
deviceId: number | null;
devices: Device[];
}

interface AppActions {
addDevice: (device: Device) => void;
setDevice: (device: Device) => void;
removeDevice: (deviceId: number) => void;
}

export const useAppStore = create<AppState & AppActions>()((set, get) => ({
deviceId: null,
devices: [],
addDevice: (device) =>
set((state) => ({ devices: [...state.devices, device] })),
setDevice: (device) =>
set((state) => ({
devices: state.devices.map((d) => (d.id === device.id ? device : d)),
})),
removeDevice: (deviceId) =>
set((state) => ({
devices: state.devices.filter((d) => d.id !== deviceId),
})),
}));

export function useDeviceOptional() {
return useAppStore((state) => {
// console.log(`Store: ${JSON.stringify(state)}`);
const deviceId = state.deviceId;
if (typeof deviceId === 'undefined') return undefined;

return state.devices.find((device) => device.id === deviceId);
});
}

export function useDevice() {
const device = useDeviceOptional();
if (typeof device === 'undefined')
throw new Error("Device hasn't been initialised!");

return device;
}

export type AppStore = ReturnType<typeof useAppStore>;
import { Device } from '@prisma/client';
import { create } from 'zustand';

export interface AppState {
deviceId: number | null;
devices: Device[];
}

interface AppActions {
addDevice: (device: Device) => void;
setDevice: (device: Device) => void;
removeDevice: (deviceId: number) => void;
}

export const useAppStore = create<AppState & AppActions>()((set, get) => ({
deviceId: null,
devices: [],
addDevice: (device) =>
set((state) => ({ devices: [...state.devices, device] })),
setDevice: (device) =>
set((state) => ({
devices: state.devices.map((d) => (d.id === device.id ? device : d)),
})),
removeDevice: (deviceId) =>
set((state) => ({
devices: state.devices.filter((d) => d.id !== deviceId),
})),
}));

export function useDeviceOptional() {
return useAppStore((state) => {
// console.log(`Store: ${JSON.stringify(state)}`);
const deviceId = state.deviceId;
if (typeof deviceId === 'undefined') return undefined;

return state.devices.find((device) => device.id === deviceId);
});
}

export function useDevice() {
const device = useDeviceOptional();
if (typeof device === 'undefined')
throw new Error("Device hasn't been initialised!");

return device;
}

export type AppStore = ReturnType<typeof useAppStore>;
6 replies
TTCTheo's Typesafe Cult
Created by Tamás Soós on 10/27/2023 in #questions
Next.js state management and dynamic routes
No description
6 replies
TTCTheo's Typesafe Cult
Created by Tamás Soós on 10/27/2023 in #questions
Next.js state management and dynamic routes
AFAIK we can't read the dynamic IDs in the layouts above a specific route. Right now there's a context provider wrapping the nav bar and a nested provider wrapping the relevant sub route that are kept somewhat in sync via useEffects. Here's the summary of what I'd like to achieve: - Share these entities globally so that all dependent components have easy access to them. - Initialise the entities in particular sub routes outside which they are undefined by default - The value the entities are getting initialised with should come from the server and be fetched in a server component - Components outside the sub route providing the entity should be able to access the global value as for example Device | undefined - Components inside the sub route providing the entity should be able to access the global value as simply Device - State updates should rerender dependent components everywhere in the tree.
6 replies