Difficulty in Setting Cookies on Server Side with Apollo Server v4, Express, and Next.js

50 Views Asked by At

I'm encountering an issue with setting cookies on the server side of my project, which involves Apollo Server, Express, and Next.js. Despite the server-side code executing without errors, the cookies are not appearing in the browser's cookie section, and there are no reported errors in the console.

Server-side Code (excerpt):

// index.ts

import { v2 as cloudinary } from "cloudinary";
import server from "./server";
import mongoose from "mongoose";
import dotenv from "dotenv";

/* MONGOOSE SETUP */
dotenv.config();

cloudinary.config({
  secure: true,
  api_key: process.env.API_KEY,
  api_secret: process.env.API_SECRET,
  cloud_name: process.env.CLOUD_NAME,
});

const port = Number(process.env.PORT) || 8080;
const MONGO_URL = String(process.env.MONGO_URL);
mongoose.set("strictQuery", true);
mongoose
  .connect(MONGO_URL)
  .then(async () => {
    server({ port });
  })
  .catch((error) => console.error(`${error} did not connect`));
// server.ts

import { ApolloServerPluginDrainHttpServer } from "@apollo/server/plugin/drainHttpServer";
import { makeExecutableSchema } from "@graphql-tools/schema";
import { expressMiddleware } from "@apollo/server/express4";
import { useServer } from "graphql-ws/lib/use/ws";
import { PubSub } from "graphql-subscriptions";
import { ApolloServer } from "@apollo/server";
import resolvers from "./graphql/resolvers";
import typeDefs from "./graphql/typeDefs";
import cookieParser from "cookie-parser";
import { WebSocketServer } from "ws";
import bodyParser from "body-parser";
import { createServer } from "http";
import express from "express";
import cors from "cors";

export default async function server(props: { port: any }) {
  // Server code in here!
  const pubsub = new PubSub();
  const app = express();
  const httpServer = createServer(app);

  const schema = makeExecutableSchema({
    typeDefs,
    resolvers,
  });

  // ws Server
  const wsServer = new WebSocketServer({
    server: httpServer,
    path: "/graphql", // localhost:3000/graphql
  });

  const serverCleanup = useServer({ schema }, wsServer); // dispose

  // apollo server
  const server = new ApolloServer({
    schema,
    plugins: [
      ApolloServerPluginDrainHttpServer({ httpServer }),
      {
        async serverWillStart() {
          return {
            async drainServer() {
              await serverCleanup.dispose();
            },
          };
        },
      },
    ],
  });

  // start our server
  await server.start();

  // apply middlewares (cors, expressMiddleware)
  app.use(
    "/graphql",
    cors<cors.CorsRequest>({
      origin: "http://localhost:3000",
      credentials: true,
    }),
    bodyParser.json({ limit: "10mb" }),
    cookieParser(),
    expressMiddleware(server, {
      context: async ({ req, res }) => {
        return { req, res, pubsub };
      },
    })
  );

  // http server start

  httpServer.listen(props.port, () => {
    console.log(
      "Server running on http://localhost:" + props.port + "/graphql"
    );
  });
}
// Code to set cookie in server

console.log("Middleware executed!");

try {
    // Set your authentication token as a cookie
    context.res.cookie("your_cookie_name", "your_token_value", {
       // secure: true,
       // httpOnly: true,
       maxAge: 1000 \* 60 \* 30,
       sameSite: "none",
    });

    // Log a message indicating the cookie was set
    console.log("Cookie set in response.");
    
    // Log the cookies in the request
    console.log("Cookies in request:", context.req.cookies);

} catch (error) {
    console.log("context: error:", error);
}

Client-side Code (excerpt):

// Code on the client side to connect to GraphQL with subscriptions

import { GraphQLWsLink } from "@apollo/client/link/subscriptions";
import { getMainDefinition } from "@apollo/client/utilities";
import { setContext } from "@apollo/client/link/context";
import { createClient } from "graphql-ws";
import {
  createHttpLink,
  InMemoryCache,
  ApolloClient,
  split,
} from "@apollo/client";

const httpLink = createHttpLink({
  uri: "http://localhost:8080/graphql",
});

const wsLink = new GraphQLWsLink(
  createClient({
    url: "ws://localhost:8080/graphql",
  })
);

const splitLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === "OperationDefinition" &&
      definition.operation === "subscription"
    );
  },
  wsLink,
  httpLink
);

const createApolloClient = () => {

  const authLink = setContext((_, { headers }) => {
    return {
      headers: {
        ...headers,
        authorization: `Bearer token`,
      },
    };
  });

  return new ApolloClient({
    link: authLink.concat(splitLink),
    cache: new InMemoryCache(),
  });
};

export default createApolloClient;

Observations:

The server-side code appears to execute correctly without errors. The cookies set in the response are not visible in the browser's cookie section. GraphQL and WebSocket connections seem to be functioning well, as no errors are reported in the console. Environment:

Server: Apollo Server, Express, Next.js Client: Apollo Client, GraphQL WebSocket subscriptions Relevant packages and their versions (package.json)

// server package.json
{
  "main": "server.ts",
  "scripts": {
    "compile": "tsc",
    "start": "nodemon ./src/index.ts",
    "start1": "npm run compile && node ./dist/index.js",
    "dev": "concurrently \"npx tsc --watch\" \"nodemon -q dist/index.js\""
  },
  "dependencies": {
    "@apollo/server": "^4.1.1",
    "@graphql-tools/schema": "^9.0.19",
    "bcrypt": "^5.1.1",
    "body-parser": "^1.20.1",
    "cloudinary": "^1.41.2",
    "concurrently": "^8.2.2",
    "cookie-parser": "^1.4.6",
    "cors": "^2.8.5",
    "dotenv": "^16.3.1",
    "express": "^4.18.2",
    "graphql-subscriptions": "^2.0.0",
    "graphql-tag": "^2.12.6",
    "graphql-ws": "^5.11.2",
    "jsonwebtoken": "^9.0.2",
    "mongoose": "^8.0.3",
    "nodemon": "^2.0.20",
    "ts-node": "^10.9.1",
    "ws": "^8.11.0"
  },
  "devDependencies": {
    "@types/bcrypt": "^5.0.2",
    "@types/cookie-parser": "^1.4.6",
    "@types/cors": "^2.8.12",
    "@types/jsonwebtoken": "^9.0.5",
    "@types/ws": "^8.5.3"
  }
}
// claint package.json
{
  "name": "web",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "graphql": "^16.8.1",
    "graphql-ws": "^5.14.3",
    "next": "14.0.4",
    "react": "^18",
    "react-blurhash": "^0.3.0",
    "react-dom": "^18",
    "react-hook-form": "^7.49.3",
  },
  "devDependencies": {
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "autoprefixer": "^10.0.1",
    "eslint": "^8",
    "eslint-config-next": "14.0.4",
    "postcss": "^8",
    "typescript": "^5"
  }
}

I have thoroughly reviewed my code and configurations but couldn't identify the root cause of this issue. Any assistance or insights into why the cookies might not be appearing in the browser would be highly appreciated.

0

There are 0 best solutions below