React Post + Plumber API resulting in CORS Error:

35 views Asked by At

I am working on my very first React project (as a frontend) combined with R (Backend) + Plumber (API).

My Get HTTP request works fine but as soon as I make a POST request I get the following error message in Chrome's inspect section: Access to XMLHttpRequest at 'http://localhost:8000/addMappingItem' from origin 'http://localhost:5173' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status. Important to note here is that my React app runs on 5173 and the Plumber API on 8000.

In my React-app I am using the following:

// Update function for the mapping:
    const addMappingItem = async () => {
        try {
            const bodyData = {
                CarModel: newMappingItemModel,
                CarBrand: newMappingItemTyp,
            };
            console.log(bodyData);

            console.log(JSON.stringify(bodyData));

            const response = await axios.post('http://localhost:8000/addMappingItem', bodyData, {
                headers: {
                    'Content-Type': 'application/json',
                },
            });
            console.log(response);
            if (response.status !== 200) {
                throw new Error('Failed to add mapping item');
            }
        } catch (error) {
            console.error("Could not add mapping item: ", error);
        }
    };

Where as an example for my bodyData one could take: bodyData = {"CarModel":"Test","CarBrand":"Test1"}

My R/Plumber API is divided currently into two scripts: run_local.R:


library(plumber)
library(pool)
library(DBI)

# Function:
# Function that creates the connection to the DB and creates a global variable:
createDbPool <- function() {
  dbPool <- pool::dbPool(
    drv = RMySQL::MySQL(),
    dbname = Sys.getenv("MY_SQL_DBNAME"),
    host = Sys.getenv("MY_SQL_HOST", "localhost"),
    username = Sys.getenv("MY_SQL_USER"),
    password = Sys.getenv("MY_SQL_PASS"),
    port = 3306, # Default MySQL port, change if necessary
    idleTimeout = 3600 # closes connections after being idle for 1 hour
  )
  
  # Assign dbPool to the global environment
  assign("dbPool", dbPool, envir = .GlobalEnv)
}



### Connection to the DB
# Load your environment variables or however you choose to secure your credentials
## Todo: 
readRenviron(paste0(getwd(),"/Access.Renviron"))
# Call the function to create and assign dbPool globally
createDbPool()

### CORS POLICY MIDDLEWARE:
# Define CORS middleware function
corsMiddleware <- function(req, res) {
  res$setHeader('Access-Control-Allow-Origin', '*')
  res$setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
  res$setHeader("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With")
  
  # Handle preflight requests
  if (req$REQUEST_METHOD == "OPTIONS") {
    res$status <- 204
    return(res)
  }
  
  plumber::forward()
}

# Run the Plumber API
pr <- plumb("plumber_api.R")
pr$run(port=8000)

plumber_api.R

# Load packages if they are not given:
if (!requireNamespace("DBI", quietly = TRUE)) install.packages("DBI")
if (!requireNamespace("jsonlite", quietly = TRUE)) install.packages("jsonlite")
if (!requireNamespace("pool", quietly = TRUE)) install.packages("pool")
if (!requireNamespace("plumber", quietly = TRUE)) install.packages("plumber")
if (!requireNamespace("swagger", quietly = TRUE)) install.packages("swagger")
if (!requireNamespace("data.table", quietly = TRUE)) install.packages("data.table")

# Library-call of packages:
library(plumber)
library(DBI)
library(jsonlite)
library(swagger)
library(data.table)


### SCRIPT SOURCING:
### ----------------
source("plotly_graph.R")




### PLUMBER FUNCTIONS:
### ------------------

# Plumber API definition with CORS middleware registration
#* @plumber
function(pr) {
  pr$registerHooks(list(
    preroute = corsMiddleware
  ))
  return(pr)
}

# Register a hook to close the database connection pool when the API exits
# Note: Actual hook registration needs to be done programmatically as shown in the previous example
#* @plumber
function(pr) {
  pr$registerHook("exit", function() {
    pool::poolClose(dbPool)
  })
  return(pr)
}


### This generates the Plotly Graph JSON
#* @get /plotlyGraph
function(req, res){
  
  # Your plotly graph creation code
  df <- data.frame(x = c(1, 2, 3, 4, 5), y = c(2, 3, 2, 3, 4))
  p <- plot_ly(df, x = ~x, y = ~y, type = 'scatter', mode = 'lines+markers')
  
  # Correctly reference plotly_graph within the function
  plotly_json_format <- convert_plotly_to_json(plotly_graph = p)
  
  # Set Content-Type header to application/json
  res$setHeader("Content-Type", "application/json")
  
  # Return the JSON string
  return(plotly_json_format)
}



##### --------------------------------------------------------------------------
### This generates the Plotly Graph JSON
#* @get /getMappingData
function(req, res){
  # Assuming `myPool` is your connection pool object
  mappingDataTable <- data.table::as.data.table(DBI::dbGetQuery(dbPool, "SELECT * FROM Mapping_cars"))[, c(1,2)]
  return(jsonlite::toJSON(mappingDataTable))
}

#* Adds a new element to the Mapping Data Table
#* @post /addMappingItem
#* @serializer json
function(req){
  data <- req$postBody
  parsedData <- jsonlite::fromJSON(data)
  
  # Convert parsed data to a data table:
  parsedDataTable <- as.data.table(parsedData)
  print(parsedDataTable)
  names(parsedDataTable) <- c("CarModel", "CarBrand")
  finalDataTable <- parsedDataTable[, ':=' ( 
      CarName_beginning = paste0(CarModel, " ", CarBrand),
      CarModel_prep = tolower(CarModel),
      LetterNumberCarName = nchar(CarModel)
    )]
  print(finalDataTable)
  
  dbWriteTable(conn = dbPool, name = "Mapping_cars", value = finalDataTable, 
               append = TRUE, row.names = FALSE)
  
  
  return(list(message = "Mapping item added successfully!"))
}

Can anybody help me with this problem? I am not sure what is wrong with my middleware.. Happy to provide more information if needed. :) Thanks a lot in advance!

I did use chatGPT, which wasn't a help but just gave me some hints but none solved the problem...

0

There are 0 answers