t3-oss / create-t3-turbo

Clean and simple starter repo using the T3 Stack along with Expo React Native
https://turbo.t3.gg
MIT License
4.62k stars 393 forks source link

Help, how do I enable CORS on the new TRPC endpoint? #437

Closed dBianchii closed 1 year ago

dBianchii commented 1 year ago

It has recently changed, and now doesn't have NextRequest or NextResponse.

import { fetchRequestHandler } from "@trpc/server/adapters/fetch";

import { appRouter, createTRPCContext } from "@kdx/api";

export const runtime = "nodejs";

// export API handler
const handler = (req: Request) =>
  fetchRequestHandler({
    endpoint: "/api/trpc",
    router: appRouter,
    req,
    createContext: () => createTRPCContext(),
  });

export { handler as GET, handler as POST };
juliusmarminge commented 1 year ago
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";

import { appRouter, createTRPCContext } from "@kdx/api";

const handler = async (req: Request) => {
  const response = await fetchRequestHandler({
    endpoint: "/api/trpc/edge",
    router: edgeRouter,
    req: req,
    createContext: () => createTRPCContext({ req }),
    onError: ({ error, path }) => {
      console.log("Error in tRPC handler (edge)", path);
      console.error(error);
      console.error(error.cause ?? "No cause");
    },
  });

  // configure cors
  response.headers.set("access-control-allow-origin", "*");
  response.headers.set("access-control-allow-headers", "*");
  response.headers.set("access-control-allow-methods", "*");
  response.headers.set("access-control-allow-credentials", "true");

  return response;
};

export { handler as GET, handler as POST };
dBianchii commented 1 year ago
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";

import { appRouter, createTRPCContext } from "@kdx/api";

const handler = async (req: Request) => {
  const response = await fetchRequestHandler({
    endpoint: "/api/trpc/edge",
    router: edgeRouter,
    req: req,
    createContext: () => createTRPCContext({ req }),
    onError: ({ error, path }) => {
      console.log("Error in tRPC handler (edge)", path);
      console.error(error);
      console.error(error.cause ?? "No cause");
    },
  });

  // configure cors
  response.headers.set("access-control-allow-origin", "*");
  response.headers.set("access-control-allow-headers", "*");
  response.headers.set("access-control-allow-methods", "*");
  response.headers.set("access-control-allow-credentials", "true");

  return response;
};

export { handler as GET, handler as POST };

That was fast! Thank you <3

dBianchii commented 1 year ago

I still keep getting this error: Access to fetch at 'http://localhost:3000/api/trpc/post.all?batch=1&input=%7B%220%22%3A%7B%22json%22%3Anull%2C%22meta%22%3A%7B%22values%22%3A%5B%22undefined%22%5D%7D%7D%7D' from origin 'http://localhost:3001' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

I tried to add

if (req.method === "OPTIONS") {
    const responseHeaders = {
      "Access-Control-Allow-Origin": "*", // Allow requests from any origin
      "Access-Control-Allow-Methods": "*", // Allow all HTTP methods
      "Access-Control-Allow-Headers": "*", // Allow all headers
      "Access-Control-Allow-Credentials": "true", // Allow credentials
    };

    return new Response(null, {
      headers: responseHeaders,
    });
  }

but no luck :/

dBianchii commented 1 year ago

UPDATE: I fixed it! I had to add

if (req.method === "OPTIONS") {
    const responseHeaders = {
      "Access-Control-Allow-Origin": "*", // Allow requests from any origin
      "Access-Control-Allow-Methods": "*", // Allow all HTTP methods
      "Access-Control-Allow-Headers": "*", // Allow all headers
      "Access-Control-Allow-Credentials": "true", // Allow credentials
    };

    return new Response(null, {
      headers: responseHeaders,
    });
  }

as I said, but then I also had to add to the exports: export { handler as GET, handler as POST, handler as OPTIONS };. Feel a little dumb for not noticing I had to export it as OPTIONS as well, if I am also doing if (req.method === "OPTIONS")

dBianchii commented 1 year ago

Here's my finished code:

import { fetchRequestHandler } from "@trpc/server/adapters/fetch";

import { appRouter, createTRPCContext } from "@kdx/api";

export const runtime = "nodejs";

// export API handler
const handler = async (req: Request) => {
  if (req.method === "OPTIONS") {
    const responseHeaders = {
      "Access-Control-Allow-Origin": "*", // Allow requests from any origin
      "Access-Control-Allow-Methods": "*", // Allow all HTTP methods
      "Access-Control-Allow-Headers": "*", // Allow all headers
      "Access-Control-Allow-Credentials": "true", // Allow credentials
    };

    return new Response(null, {
      headers: responseHeaders,
      status: 204,
    });
  }

  const response = await fetchRequestHandler({
    endpoint: "/api/trpc",
    router: appRouter,
    req,
    createContext: () => createTRPCContext(),
  });
  // configure cors
  response.headers.set("access-control-allow-origin", "*");
  response.headers.set("access-control-allow-headers", "*");
  response.headers.set("access-control-allow-methods", "*");
  response.headers.set("access-control-allow-credentials", "true");

  return response;
};

export { handler as GET, handler as POST, handler as OPTIONS };
juliusmarminge commented 1 year ago

Ah sorry, forgot about the OPTIONS request, here's what I did:

import { fetchRequestHandler } from "@trpc/server/adapters/fetch";

import { appRouter, createTRPCContext } from "@acme/api";

export const runtime = "nodejs";

function setCorsHeaders(res: Response) {
  res.headers.set("Access-Control-Allow-Origin", "*");
  res.headers.set("Access-Control-Request-Method", "*");
  res.headers.set("Access-Control-Allow-Methods", "OPTIONS, GET, POST");
  res.headers.set("Access-Control-Allow-Headers", "*");
}

export function OPTIONS() {
  const response = new Response(null, {
    status: 204,
  });
  setCorsHeaders(response);

  return response;
}

async function handler(req: Request) {
  const response = await fetchRequestHandler({
    endpoint: "/api/trpc",
    router: appRouter,
    req,
    createContext: () => createTRPCContext(),
  });

  // set CORS headers
  setCorsHeaders(response);

  return response;
}

export { handler as GET, handler as POST };
dBianchii commented 1 year ago

Ah sorry, forgot about the OPTIONS request, here's what I did:


import { fetchRequestHandler } from "@trpc/server/adapters/fetch";

import { appRouter, createTRPCContext } from "@acme/api";

export const runtime = "nodejs";

function setCorsHeaders(res: Response) {

  res.headers.set("Access-Control-Allow-Origin", "*");

  res.headers.set("Access-Control-Request-Method", "*");

  res.headers.set("Access-Control-Allow-Methods", "OPTIONS, GET, POST");

  res.headers.set("Access-Control-Allow-Headers", "*");

}

export function OPTIONS() {

  const response = new Response(null, {

    status: 204,

  });

  setCorsHeaders(response);

  return response;

}

async function handler(req: Request) {

  const response = await fetchRequestHandler({

    endpoint: "/api/trpc",

    router: appRouter,

    req,

    createContext: () => createTRPCContext(),

  });

  // set CORS headers

  setCorsHeaders(response);

  return response;

}

export { handler as GET, handler as POST };

Also don't forget export { handler as GET, handler as POST, handler as OPTIONS };

juliusmarminge commented 1 year ago

Also don't forget export { handler as GET, handler as POST, handler as OPTIONS };

I am exporting a standalone function OPTIONS instead. No need to run the entire tRPC handler for the OPTIONS request

dBianchii commented 1 year ago

Oh sorry, I didn't realize it was inside another function. This is better.

marine-mb commented 2 months ago

Hi, It is a bad practice to allow access to your backend with a wildcard *. It exposes you to attacks such as CSRF. image Your users' cookies could be sent (and accepted) from another website, potentially from a hacker.

You must specify which origin you allow specifically.

You can find more information in this article.

@juliusmarminge It would be better to remove the CORS headers by default. In this case, the Same-Origin will be applied. It means only the same origin will be able to call the backend.

For example: