pupo
pupo10mo ago

Missing content type header for mutations leads to 405

Im migrating my app from Next Pages Router to App Router. I implemented the new trpc wrapper as explained here and everything seems to be working as I expected, except mutations. Mutations fail on preflight with a 405 (local development, same server). The only difference between the "before" and "after" of the migration (As well as the only difference to queries) is that mutations are missing a content-type: "json" (See attached). Any idea how I can fix this?
No description
No description
Solution:
Ok .... so for prosterity the issue was in the server API route. it was missing a POST handler. Fixed version: ```tsx import { fetchRequestHandler } from "@trpc/server/adapters/fetch";...
Jump to solution
2 Replies
pupo
pupoOP10mo ago
export const trpc = createTRPCReact<AppRouter>({
unstable_overrides: {
useMutation: {
async onSuccess(opts) {
await opts.originalFn();
await opts.queryClient.invalidateQueries();
},
},
},
});

const getBaseUrl = (): string => {
const env = getEnvVariableOrDefault("NODE_ENV", "");
const VERCEL_URL = getEnvVariableOrDefault("VERCEL_URL", "");
const PORT = getEnvVariableOrDefault("PORT", "3000");

if (env === "test") return `http://localhost:${PORT}`;

if (typeof window !== "undefined") return "";

if (VERCEL_URL) return `https://${VERCEL_URL}`;

return `http://localhost:${PORT}`;
};

export type TRPCProviderProps = {
children: ReactNode;
};

export const TrpcProvider: FunctionComponent<TRPCProviderProps> = ({
children,
}) => {
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
loggerLink({
enabled: () => true,
}),
httpBatchLink({
url: `${getBaseUrl()}/api/trpc2`,
}),
],
transformer: superJson,
}),
);

return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</trpc.Provider>
);
};
export const trpc = createTRPCReact<AppRouter>({
unstable_overrides: {
useMutation: {
async onSuccess(opts) {
await opts.originalFn();
await opts.queryClient.invalidateQueries();
},
},
},
});

const getBaseUrl = (): string => {
const env = getEnvVariableOrDefault("NODE_ENV", "");
const VERCEL_URL = getEnvVariableOrDefault("VERCEL_URL", "");
const PORT = getEnvVariableOrDefault("PORT", "3000");

if (env === "test") return `http://localhost:${PORT}`;

if (typeof window !== "undefined") return "";

if (VERCEL_URL) return `https://${VERCEL_URL}`;

return `http://localhost:${PORT}`;
};

export type TRPCProviderProps = {
children: ReactNode;
};

export const TrpcProvider: FunctionComponent<TRPCProviderProps> = ({
children,
}) => {
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
loggerLink({
enabled: () => true,
}),
httpBatchLink({
url: `${getBaseUrl()}/api/trpc2`,
}),
],
transformer: superJson,
}),
);

return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</trpc.Provider>
);
};
Since Im not 100% sure if this is an issue with the setup of the client, or the server endpoint, here is the code for the server endpoint:
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { appRouter } from "@cf/providers/trpc";

export const GET = (req: Request) =>
fetchRequestHandler({
endpoint: "/api/trpc2",
router: appRouter,
req,
createContext: async () => ({}),
});
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { appRouter } from "@cf/providers/trpc";

export const GET = (req: Request) =>
fetchRequestHandler({
endpoint: "/api/trpc2",
router: appRouter,
req,
createContext: async () => ({}),
});
Solution
pupo
pupo10mo ago
Ok .... so for prosterity the issue was in the server API route. it was missing a POST handler. Fixed version:
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { appRouter } from "@cf/providers/trpc";

const handler = (req: Request) =>
fetchRequestHandler({
endpoint: "/api/trpc2",
router: appRouter,
req,
createContext: async () => ({}),
});

export const POST = handler;
export const GET = handler;
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { appRouter } from "@cf/providers/trpc";

const handler = (req: Request) =>
fetchRequestHandler({
endpoint: "/api/trpc2",
router: appRouter,
req,
createContext: async () => ({}),
});

export const POST = handler;
export const GET = handler;