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...