seven
seven
TtRPC
Created by seven on 8/21/2024 in #❓-help
Is it ok to have 2 clients in one app
Hi, as for the topic, is it okay to have a standalone client and a react-query integration? I have a trpc standalone backend in a monorepo with nextjs frontend, so far I've been calling my backend only from server components, now i think i will need react query functionalities in the client components, is it ok to have both standlone client and react query intefgration or is there a better approach?
4 replies
TtRPC
Created by seven on 7/7/2024 in #❓-help
client server counication with webSockets
Hi, I would like to be able to send data via ws api from my frontend. Still, I don't seem to be able to send any data from the frontend to the trpc backend, I tried to do it in a way that I make mutation and I open ws connection in this procedure. Still, it reconnects me on every request and I need this connection to stay open and just send the data, is there a way to send data from trpc client - frontend to the subscription precedure? On the client, apart from onData there are only onComplete, onError, onSarted, and onStopped. ws API offers ws.send() and it seems that I can only receive data from the server, is trpc client lacking this functionality or am i missing something? Many Thanks
12 replies
TtRPC
Created by seven on 7/2/2024 in #❓-help
sending messages via WebSockets
Hi, do I have the possibility to send messages via wsLink? I want to connect to Binance and be able to place orders via WebSockets, so far I've only been receiving market data like so:
useEffect(() => {
const socket = client.binance.marketDataStream.subscribe(
{ symbol },
{
onData(data) {
if (data.type === priceLadderUpdates.Trade) {
setTradedLevel(data.lastTrade);
}
if (data.type === priceLadderUpdates.Depth) {
setUpdatedLevels([...data.priceLevels]);
}
},
},
);

return socket.unsubscribe;
}, [symbol, setTradedLevel, setUpdatedLevels]);
useEffect(() => {
const socket = client.binance.marketDataStream.subscribe(
{ symbol },
{
onData(data) {
if (data.type === priceLadderUpdates.Trade) {
setTradedLevel(data.lastTrade);
}
if (data.type === priceLadderUpdates.Depth) {
setUpdatedLevels([...data.priceLevels]);
}
},
},
);

return socket.unsubscribe;
}, [symbol, setTradedLevel, setUpdatedLevels]);
apart onData there are only onComplete, onError, onSarted, and onStopped. My mental model is that I would like to broadcast messages with my order params to receive them in my trpc backend and from there transmit this to Binance, is this possible ? I use standalone client
import { createWSClient, splitLink, wsLink, createTRPCClient, httpLink } from '@trpc/client';
import type { AppRouter } from '@api/app';
import superjson from 'superjson';

export const client = createTRPCClient<AppRouter>({
links: [
splitLink({
condition: (op) => op.type === 'subscription',
true: wsLink({
client: createWSClient({
url: `ws://localhost:7000`,
}),
transformer: superjson,
}),
false: httpLink({
url: `http://localhost:7000`,
fetch(url, options) {
return fetch(url, {
...options,
credentials: 'include',
cache: 'no-store',
});
},
transformer: superjson,
}),
}),
],
});
import { createWSClient, splitLink, wsLink, createTRPCClient, httpLink } from '@trpc/client';
import type { AppRouter } from '@api/app';
import superjson from 'superjson';

export const client = createTRPCClient<AppRouter>({
links: [
splitLink({
condition: (op) => op.type === 'subscription',
true: wsLink({
client: createWSClient({
url: `ws://localhost:7000`,
}),
transformer: superjson,
}),
false: httpLink({
url: `http://localhost:7000`,
fetch(url, options) {
return fetch(url, {
...options,
credentials: 'include',
cache: 'no-store',
});
},
transformer: superjson,
}),
}),
],
});
Thanks
2 replies
TtRPC
Created by seven on 4/20/2024 in #❓-help
Calling procedures from Next server actions
Hi, is using trpc client allowed in server actions? I have two use cases, in one i call procedure from server component with the 'use server'
'use server';

export async function getCandlesticsData(symbol: string, interval: KlineInterval) {
const candlesticksPromise = client.binance.candlestick.query({ symbol, interval }, { context: { revalidate: 0 } });
const lastTradePromise = client.binance.lastTrade.query({ symbol }, { context: { revalidate: 0 } });
const [candlesticksResult, recentTrade] = await Promise.all([candlesticksPromise, lastTradePromise]);

return {
candlesticks: candlesticksResult?.candlesticks ?? [],
recentTrade: parseFloat(recentTrade ?? '0'),
};
}

export async function TradingChartsManager({ symbol }: D3ChartProps) {
const { candlesticks, recentTrade } = await getCandlesticsData(symbol, interval);

return (
<section className={styles.container}>
<IntervalButtons />
<CandlestickChart {...{ symbol, interval, candlesticks, recentTrade }} key={`${symbol}-${interval}`} />;
</section>
);
}
'use server';

export async function getCandlesticsData(symbol: string, interval: KlineInterval) {
const candlesticksPromise = client.binance.candlestick.query({ symbol, interval }, { context: { revalidate: 0 } });
const lastTradePromise = client.binance.lastTrade.query({ symbol }, { context: { revalidate: 0 } });
const [candlesticksResult, recentTrade] = await Promise.all([candlesticksPromise, lastTradePromise]);

return {
candlesticks: candlesticksResult?.candlesticks ?? [],
recentTrade: parseFloat(recentTrade ?? '0'),
};
}

export async function TradingChartsManager({ symbol }: D3ChartProps) {
const { candlesticks, recentTrade } = await getCandlesticsData(symbol, interval);

return (
<section className={styles.container}>
<IntervalButtons />
<CandlestickChart {...{ symbol, interval, candlesticks, recentTrade }} key={`${symbol}-${interval}`} />;
</section>
);
}
and it works well every time and in my login action the prcedure is never called unless i remove 'use server' from the top of the file
'use server';

export async function loginAction<State extends FormState, Payload extends FormData>(
_: State,
payload: Payload,
): Promise<FormState> {
const parsedData = await LoginFormSchema.safeParseAsync(Object.fromEntries(payload));
if (!parsedData.success) {
return {
success: false,
errors: parsedData.error.flatten().fieldErrors,
};
}

await client.auth.login.mutate({ email: parsedData.data.email, password: parsedData.data.password });

return { success: true, data: parsedData.data };
}
'use server';

export async function loginAction<State extends FormState, Payload extends FormData>(
_: State,
payload: Payload,
): Promise<FormState> {
const parsedData = await LoginFormSchema.safeParseAsync(Object.fromEntries(payload));
if (!parsedData.success) {
return {
success: false,
errors: parsedData.error.flatten().fieldErrors,
};
}

await client.auth.login.mutate({ email: parsedData.data.email, password: parsedData.data.password });

return { success: true, data: parsedData.data };
}
I dont understand this behavior, can someone please explain me what is going on ? In my login page i have
export const dynamic = 'force-dynamic';
export const dynamic = 'force-dynamic';
so it's not cache
2 replies
TtRPC
Created by seven on 4/5/2024 in #❓-help
Cookie authentication flow
Hello can i get a bit of a help and explanation please? Im frontend learning backend on trpc, I have monorepo with trpc backend and next 14 frontend, I use wesockets, and my backend context accorgingly to the docs has type CreateHTTPContextOptions | CreateWSSContextFnOptions The problem is that in procedures I can’t use eg setHeader method from CreateHTTPContextOptions unless I type cast it so should I use a type assertion function or is there a way to infer proper type from createcontext so queries and mutation procedures have different context type than subscriptions? And when i log Object.keys(ctx) i get keys that are not available in CreateHTTPContextOptions ['router','createContext','middleware','req','res','path','info'] how can I get full type for that? I want to authorize my users so in registration route i want to set the authorization cookie, to do that to do that i do res.setHeader('Set-Cookie', 'authToken=exampleToken'); and i have this header set in my network tab, but in application tab in cookies section there is no such entry, should it be there? Then the other way round i want to authorize with my server from the frontend to do that in my trpc client in experimental_nextHttpLink i add headers function, here i have two issues one is that when i want to read my cookies from next/headers i get error that one of parents is a client component and indeed i get error from components that uses subscription inside useEffect, so how should i go about this? And the other issue is how can i authenticate in my subscriptions? Could you please give me a bit of explanation how these things should work or perhaps some mental model, i mean should i restructure it to have subscriptions in a separate client? Is it ok to have multiple clients? And how could i authorize in subscriptions? Some guidance would help me tremendously
4 replies