T
TanStack11mo ago
metropolitan-bronze

Not sure if this belongs in #start or #query, but type inference when using server functions

Hello, not sure where this belongs, but I'm using Start and Query and my type inference seems to be wrong when my objects contain dates. See images and code related.
import { queryOptions } from "@tanstack/react-query";
import { redirect } from "@tanstack/react-router";
import { createServerFn, json } from "@tanstack/start";
import { asc, eq } from 'drizzle-orm';
import { getEvent } from "vinxi/http";
import { db } from "~/server/db";
import { transaction } from "~/server/db/schema";


const fetchUserTransactions = createServerFn('GET', async (_, ctx) => {
const event = getEvent();
const auth = event.context.auth;

if (!auth.isAuthenticated) {
throw redirect({
to: '/signin',
code: 400
})
}

const transactions = await db.select().from(transaction).where(eq(transaction.userId, auth.user?.id)).orderBy(asc(transaction.date))


return json(transactions);
})

export const transactionQueries = {
getUserTransactions: () => queryOptions({
queryKey: ['transactions', 'all'],
queryFn: () => fetchUserTransactions(),
}),
} as const;
import { queryOptions } from "@tanstack/react-query";
import { redirect } from "@tanstack/react-router";
import { createServerFn, json } from "@tanstack/start";
import { asc, eq } from 'drizzle-orm';
import { getEvent } from "vinxi/http";
import { db } from "~/server/db";
import { transaction } from "~/server/db/schema";


const fetchUserTransactions = createServerFn('GET', async (_, ctx) => {
const event = getEvent();
const auth = event.context.auth;

if (!auth.isAuthenticated) {
throw redirect({
to: '/signin',
code: 400
})
}

const transactions = await db.select().from(transaction).where(eq(transaction.userId, auth.user?.id)).orderBy(asc(transaction.date))


return json(transactions);
})

export const transactionQueries = {
getUserTransactions: () => queryOptions({
queryKey: ['transactions', 'all'],
queryFn: () => fetchUserTransactions(),
}),
} as const;
No description
No description
16 Replies
metropolitan-bronze
metropolitan-bronzeOP11mo ago
import { QueryClient } from "@tanstack/react-query";
import { createRouter as createTanStackRouter } from "@tanstack/react-router";
import { routerWithQueryClient } from "@tanstack/react-router-with-query";

import { DefaultCatchBoundary } from "./components/DefaultCatchBoundary";
import { NotFound } from "./components/NotFound";
import { routeTree } from "./routeTree.gen";

import { SuperJSON } from "superjson";

export function createRouter() {
const queryClient = new QueryClient({});

return routerWithQueryClient(
createTanStackRouter({
routeTree,
context: { queryClient },
defaultPreload: "intent",
defaultErrorComponent: DefaultCatchBoundary,
defaultNotFoundComponent: NotFound,
transformer: SuperJSON,
}),
queryClient
);
}

declare module "@tanstack/react-router" {
interface Register {
router: ReturnType<typeof createRouter>;
}
}
import { QueryClient } from "@tanstack/react-query";
import { createRouter as createTanStackRouter } from "@tanstack/react-router";
import { routerWithQueryClient } from "@tanstack/react-router-with-query";

import { DefaultCatchBoundary } from "./components/DefaultCatchBoundary";
import { NotFound } from "./components/NotFound";
import { routeTree } from "./routeTree.gen";

import { SuperJSON } from "superjson";

export function createRouter() {
const queryClient = new QueryClient({});

return routerWithQueryClient(
createTanStackRouter({
routeTree,
context: { queryClient },
defaultPreload: "intent",
defaultErrorComponent: DefaultCatchBoundary,
defaultNotFoundComponent: NotFound,
transformer: SuperJSON,
}),
queryClient
);
}

declare module "@tanstack/react-router" {
interface Register {
router: ReturnType<typeof createRouter>;
}
}
I forgot to mention that I am using superjson
like-gold
like-gold11mo ago
no, this one will not apply to server function
metropolitan-bronze
metropolitan-bronzeOP11mo ago
is there a way to wrap it?
like-gold
like-gold11mo ago
I think there’s no way to do this rn
metropolitan-bronze
metropolitan-bronzeOP11mo ago
Gotcha. What did you do to resolve your issue? Or did you just wrap your dates with new Date(stringValue) to get the date object back?
like-gold
like-gold11mo ago
I didn’t resolve the issue I just called the superjson api manually to test the result on client I think we need to wait this PR first
like-gold
like-gold11mo ago
GitHub
Chained Server Fn Syntax, ServerFn Middleware by tannerlinsley · Pu...
Todo: Remove any &quot;search&quot; param specific terms/functionality from the validations utilities (including types and adapters) Publish new validation packages (without the router- ...
like-gold
like-gold11mo ago
before they support superjson
metropolitan-bronze
metropolitan-bronzeOP11mo ago
Gotcha. I guess for now I can just do
{data?.map((transaction) => {
const date = new Date((transaction.date as any).toString());
return (
<TableRow key={transaction.id}>
<TableCell>{date.toUTCString()}</TableCell>
</TableRow>
);
})}
{data?.map((transaction) => {
const date = new Date((transaction.date as any).toString());
return (
<TableRow key={transaction.id}>
<TableCell>{date.toUTCString()}</TableCell>
</TableRow>
);
})}
like-gold
like-gold11mo ago
Yeah, return a normal json object is a good solution for now
like-gold
like-gold11mo ago
Twitter
vxTwitter / fixvx 💖 492 🔁 22
Tanner Linsley (@tannerlinsley)
Type-safe Server Function Middleware and upgraded Server Functions have been achieved internally @Tan_Stack Start. Shipping to alpha (maybe beta) ASAP
like-gold
like-gold11mo ago
The input will also support zod schema or other validation libs Just like tRPC Oh, u transforming the string to date object on client code I think u can transform the date to string on server And transform the date string to date object on client This way, the types and data will match
metropolitan-bronze
metropolitan-bronzeOP11mo ago
return json(transactions.map(t => ({
...t,
date: t.date.toString()
})));
return json(transactions.map(t => ({
...t,
date: t.date.toString()
})));
something like that I guess?
like-gold
like-gold11mo ago
yeah and, when using server function, you don’t need to wrap the data with json() api just return the data like tRPC the json() api is for api route
metropolitan-bronze
metropolitan-bronzeOP11mo ago
oops, thought you did, thank you I ended up using this:
type Transform<T> = {
[K in keyof T]: T[K] extends Date | null ? string | null : T[K] extends Date ? string : T[K];
};

export function transform<T extends Record<string, any>>(data: T): Transform<T> {
return Object.fromEntries(
Object.entries(data).map(([key, value]) => [
key,
value instanceof Date ? value.toString() : value
])
) as Transform<T>;
}
type Transform<T> = {
[K in keyof T]: T[K] extends Date | null ? string | null : T[K] extends Date ? string : T[K];
};

export function transform<T extends Record<string, any>>(data: T): Transform<T> {
return Object.fromEntries(
Object.entries(data).map(([key, value]) => [
key,
value instanceof Date ? value.toString() : value
])
) as Transform<T>;
}
so
const transactions = await db.select().from(transaction).where(eq(transaction.userId, auth.user?.id)).orderBy(asc(transaction.date))
return transactions.map(transform);
const transactions = await db.select().from(transaction).where(eq(transaction.userId, auth.user?.id)).orderBy(asc(transaction.date))
return transactions.map(transform);

Did you find this page helpful?