T
TanStack•3w ago
fair-rose

How do I stop server code getting leaked to the client?

Hi, I have an util that should only be used by server code in orpc that leverages the crypto module, but based on my browser logs I am getting errors such as:
Uncaught (in promise) Error: Module "crypto" has been externalized for browser compatibility. Cannot access "crypto.createCipheriv" in client code
---
Uncaught (in promise) ReferenceError: Buffer is not defined
Uncaught (in promise) Error: Module "crypto" has been externalized for browser compatibility. Cannot access "crypto.createCipheriv" in client code
---
Uncaught (in promise) ReferenceError: Buffer is not defined
My router is currently set up this way:
import { os } from "@orpc/server";
import { portalRouter } from "./routes/portal";

const createRpcRouter = {
healthCheck: os.handler(() => {
return "OK";
}),
portal: portalRouter,
};

export const rpcRouter = createRpcRouter;

export type RpcRouter = typeof rpcRouter;
import { os } from "@orpc/server";
import { portalRouter } from "./routes/portal";

const createRpcRouter = {
healthCheck: os.handler(() => {
return "OK";
}),
portal: portalRouter,
};

export const rpcRouter = createRpcRouter;

export type RpcRouter = typeof rpcRouter;
and my client:
const getORPCClient = createIsomorphicFn()
.server(() =>
createRouterClient(rpcRouter, {
context: () => {
const { headers } = getWebRequest();
return {
headers,
};
},
}),
)
.client((): RouterClient<RpcRouter> => {
const link = new RPCLink({
url: `${window.location.origin}/api/rpc`,
});
return createORPCClient(link);
});

export const client: RouterClient<RpcRouter> = getORPCClient();

export const orpc = createTanstackQueryUtils(client);
const getORPCClient = createIsomorphicFn()
.server(() =>
createRouterClient(rpcRouter, {
context: () => {
const { headers } = getWebRequest();
return {
headers,
};
},
}),
)
.client((): RouterClient<RpcRouter> => {
const link = new RPCLink({
url: `${window.location.origin}/api/rpc`,
});
return createORPCClient(link);
});

export const client: RouterClient<RpcRouter> = getORPCClient();

export const orpc = createTanstackQueryUtils(client);
I saw online that you could use the serverOnly() function to wrap the router to solve this but it provides an error:
import { serverOnly } from "@tanstack/react-start";
const createRpcRouter = serverOnly(() => ({
healthCheck: os.handler(() => {
return "OK";
}),
portal: portalRouter,
}));
import { serverOnly } from "@tanstack/react-start";
const createRpcRouter = serverOnly(() => ({
healthCheck: os.handler(() => {
return "OK";
}),
portal: portalRouter,
}));
Error: serverOnly() functions can only be called on the server!
5 Replies
fair-rose
fair-roseOP•3w ago
GitHub
GitHub - kevmok/tanstackstart-server-orpc
Contribute to kevmok/tanstackstart-server-orpc development by creating an account on GitHub.
deep-jade
deep-jade•3w ago
if that function is called on the client as well you should use createIsomorphicFn as well and don't supply a client impl so it will be a noop on the client
deep-jade
deep-jade•3w ago
If you want to use this code ONLY in server side you should call these utils inside server functions or else, transform them in server functions.
deep-jade
deep-jade•3w ago
yep
fair-rose
fair-roseOP•3w ago
Got it, that makes sense That could work, but moving all the orpc routes as server functions doesn't seem right in my head Or should the orpc handler just call the server fn Ok I found the root cause of everything. So the crypto utils were not really that big of a deal, but I have an orpc middleware in this context file and main issue is that it's importing db and auth (also imports db) so it was producing errors in my portalRoute since it uses the protectedProcedure... I would assume the createIsomorphicFn would handle this no? since the server part is what's using the rpcRouter and the client is just using an RPCLink? /src/server/api/utils/context.ts
import { ORPCError, os } from "@orpc/server";
import { auth } from "@/lib/auth";

import { db } from "@/lib/db";

const authMiddleware = os
.$context<{ headers: Headers }>()
.middleware(async ({ context, next }) => {
const session = await auth.api.getSession({
headers: context.headers,
});

if (!session) {
throw new ORPCError("UNAUTHORIZED", {
message: "Authentication required",
});
}

return next({ context: { ...context, session } });
});

const dbProvider = os
.$context<{ headers: Headers }>()
.middleware(async ({ context, next }) => {
return next({ context: { ...context, db } });
});

export const publicProcedure = os
.$context<{ headers: Headers }>()
.use(dbProvider);

export const protectedProcedure = os
.$context<{ headers: Headers }>()
.use(authMiddleware)
.use(dbProvider);
import { ORPCError, os } from "@orpc/server";
import { auth } from "@/lib/auth";

import { db } from "@/lib/db";

const authMiddleware = os
.$context<{ headers: Headers }>()
.middleware(async ({ context, next }) => {
const session = await auth.api.getSession({
headers: context.headers,
});

if (!session) {
throw new ORPCError("UNAUTHORIZED", {
message: "Authentication required",
});
}

return next({ context: { ...context, session } });
});

const dbProvider = os
.$context<{ headers: Headers }>()
.middleware(async ({ context, next }) => {
return next({ context: { ...context, db } });
});

export const publicProcedure = os
.$context<{ headers: Headers }>()
.use(dbProvider);

export const protectedProcedure = os
.$context<{ headers: Headers }>()
.use(authMiddleware)
.use(dbProvider);
i think just separating the imports in the orpc file worked 😭
import type { RpcRouter } from "@/server/api/router";
import { rpcRouter } from "@/server/api/router";
import type { RpcRouter } from "@/server/api/router";
import { rpcRouter } from "@/server/api/router";

Did you find this page helpful?