I'm developing a backend server with Node.js/Express and I want to create an external API that everyone can access. So I need to disable CORS policy for the external API, but keep the policy for all other APIs except the external API.
This is my current server.js.
import path from 'path';
import cors from 'cors';
import nocache from 'nocache';
import express from 'express';
import mongoose from 'mongoose';
import { fileURLToPath } from 'url';
import bodyParser from 'body-parser';
import Router from './routes/index.js';
import PaymentController from './controllers/client/payment.controller.js';
// Get the directory name of the current module
const __dirname = path.dirname(fileURLToPath(import.meta.url));
mongoose.connect(process.env.DB)
.then(() => {
console.log(`Database connected successfully ${process.env.DB}`);
})
.catch((error) => console.log(error));
const app = express();
app.use(nocache());
// Combine to frontend for production
if (process.env.MODE == 'production') {
app.use(express.static(path.join(__dirname, '../production/build')));
} else if (process.env.MODE == 'staging') {
app.use(express.static(path.join(__dirname, '../staging/build')));
}
const allowedOrigins = [
'http://localhost:3000'
];
const corsOptions = {
origin: function (origin, callback) {
if (!origin) return callback(null, true);
if (allowedOrigins.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
methods: 'GET, OPTIONS',
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
};
// Apply CORS to most routes
app.use(cors(corsOptions));
app.post('/v1/api/webhook', express.raw({ type: 'application/json' }), PaymentController.handleSubscriptionEvent);
app.use(
bodyParser.json({
limit: '15360mb',
type: 'application/json',
})
);
app.use(
bodyParser.urlencoded({
limit: '15360mb',
extended: true,
parameterLimit: 5000000,
type: 'application/json',
})
);
app.use('/v1/', Router);
// Combile to frontend for production
if (process.env.MODE == 'production') {
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, '../production/build', 'index.html'));
});
} else if (process.env.MODE == 'staging') {
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, '../staging/build', 'index.html'));
});
}
const port = process.env.MODE === 'production' || process.env.MODE === 'staging' ? 3000 : 9200;
const runningMessage = 'Server is running on port ' + port;
app.listen(port, () => {
console.log(runningMessage);
});
This is Router
import express from 'express';
import AdminRouter from './admin.router.js';
import ClientRouter from './client.router.js';
import ServiceRouter from './service.router.js';
const router = express.Router();
router.use('/api/admin', AdminRouter);
router.use('/api', ClientRouter);
router.use('/api/service', ServiceRouter);
export default router;
As you can see in my code, I have an array of allowedOrigins and I want to disable CORS policy for origins included in allowedOrigins. But for external APIs, I want to disable CORS policy for all origins.
To do this, I tried setting up cors middleware only for external APIs, but it didn't work.
I found the reason. The reason is that client route includes service route. client route is /api and service route is /api/service so /api/* routes will be pass middleware of client route first. It was the reason so I added subprefix so client route is updated to /api/client. And then, I updated corsOptions like below.
import cors from 'cors';
import express from 'express';
import role from '../services/role.js';
import auth from '../services/auth.js';
import AdminRouter from './admin.router.js';
import ClientRouter from './client.router.js';
import ServiceRouter from './service.router.js';
const router = express.Router();
const allowedOrigins = ['https://gallerai.ai', 'https://gallerai-staging-v1.gallerai.ai', 'http://localhost:3000', 'http://localhost:3001'];
const corsOptions = {
origin: function (origin, callback) {
if (!origin) return callback(null, true);
if (allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
methods: 'GET, POST, PUT, DELETE, OPTIONS',
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
};
// Apply CORS to specific routes with configured options
router.use('/api/admin', cors(corsOptions), auth, role, AdminRouter);
router.use('/api/client', cors(corsOptions), ClientRouter);
// Explicitly allow all CORS for /api/service/*
router.options('/api/service/', cors()); // Handle pre-flight OPTIONS requests
router.use('/api/service/', cors(), (req, res, next) => {
res.header('Access-Control-Allow-Origin', '*'); // Allow all origins explicitly
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization, X-Api-Key');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
next();
}, ServiceRouter);
export default router;