DEV Community

Cover image for Let's talk about CORS & frontend with vite
Nicolás Danelón
Nicolás Danelón

Posted on • Edited on

Let's talk about CORS & frontend with vite

I often find myself developing websites with React or Preact.
Lately, Preact has become my favorite, as it's much faster and lighter.

Sometimes I don't need to use a real server—for example, when I'm teaching a class, making a demo, or working without internet. It's also helpful when I need to simulate requests to a server, even if it's local.

To test my frontend, I create a simple PHP file that reads the data sent via POST and returns a JSON object similar to the one that the real server would provide.

(At the end of the article, there is a Node.js version of the PHP server.)

php -S 127.0.0.1 -t server.php
Enter fullscreen mode Exit fullscreen mode
<?php

$json = file_get_contents('php://input');
$data = json_decode($json, true);

if (!isset($data['username']) || !isset($data['password'])) {
    http_response_code(400);
    echo json_encode([
        'status' => 'error',
        'message' => 'Username or password not provided'
    ]);
    exit;
}

if ($data['username'] === 'nicolas' && $data['password'] === 'Correct-h0rse-Battery-staple') {
    echo json_encode([
        'status' => 'success',
        'token' => 'fake_token'
    ]);
} else {
    http_response_code(401);
    echo json_encode([
        'status' => 'error',
        'message' => 'Invalid username or password'
    ]);
}
Enter fullscreen mode Exit fullscreen mode

I like to use PHP because it has no dependencies, which is great for a demo. But this code doesn't work. The error is:

Response body is not available to scripts (Reason: CORS Failed)
Enter fullscreen mode Exit fullscreen mode

After reading the title, you might have guessed that the error comes from CORS. But what is CORS?

CORS stands for Cross-Origin Resource Sharing. It is a security mechanism that allows you to control who can access the server's resources.

So, all I have to add to my PHP code is a couple of HTTP headers, right?

<?php

header('Content-Type: application/json');
header('Access-Control-Allow-Origin: http://localhost:5173');
header('Access-Control-Allow-Methods: POST');
header('Access-Control-Allow-Headers: Content-Type');
Enter fullscreen mode Exit fullscreen mode

My code still doesn't work! My request doesn't reach the server. But why?

Upon inspection, Axios is sending a request before the POST request I'm making. This first request is known as a "preflight request." It's an OPTIONS request; it only asks the server for the types of requests that are allowed. Knowing that, let's modify our headers and add a small if-statement:

// headers
header('Access-Control-Allow-Methods: POST, OPTIONS');

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(200);
    exit;
}
// fake login

Enter fullscreen mode Exit fullscreen mode

Now it works! Generally, browsers try to enforce the "Same Origin" policy, which limits how JavaScript code that's running in one origin can access resources in another origin. (Origins can be domains, protocols, or ports.)

Finally, our PHP code looks like this:

<?php

header('Content-Type: application/json');
header('Access-Control-Allow-Origin: http://localhost:5173'); // CORS
header('Access-Control-Allow-Methods: POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(200);
    exit;
}

$json = file_get_contents('php://input');
$data = json_decode($json, true);

if (!isset($data['username']) || !isset($data['password'])) {
  http_response_code(400);
  echo json_encode([
    'status' => 'error',
    'message' => 'Username or password not provided'
  ]);
  exit;
}

if ($data['username'] === 'nicolas' && $data['password'] === 'Correct-h0rse-Battery-staple') {
  echo json_encode([
    'status' => 'success',
    'token' => 'fake_token'
  ]);
} else {
  http_response_code(401);
  echo json_encode([
    'status' => 'error',
    'message' => 'Invalid username or password'
  ]);
}

Enter fullscreen mode Exit fullscreen mode

In case you don't have the PHP interpreter on your system, here's a version of the same program in Node.js with Express:

pnpm init && pnpm add express body-parser cors
Enter fullscreen mode Exit fullscreen mode
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");

const app = express();
const port = process.env.PORT || 3000;

app.use(bodyParser.json());
app.use(cors());

app.post("/login", (req, res) => {
  const { username, password } = req.body;

  if (!username || !password) {
    res
      .status(400)
      .json({ status: "error", message: "Username or password not provided" });
    return;
  }

  if (username === "nicolas" && password === "Correct-h0rse-Battery-staple") {
    res.json({ status: "success", token: "fake_token" });
  } else {
    res
      .status(401)
      .json({ status: "error", message: "Invalid username or password" });
  }
});

app.listen(port, () => {
  console.log(`Node.js server running at http://localhost:${port}`);
});
Enter fullscreen mode Exit fullscreen mode

Cover credits:

Top comments (0)