Express.js REST API Setup: Complete Guide
Express.js is the most popular Node.js framework for building REST APIs. In this guide, we'll set up a production-ready Express.js REST API with proper middleware, error handling, CORS configuration, and organized routing.
Express.js is the most popular Node.js framework for building REST APIs. In this guide, we'll set up a production-ready Express.js REST API with proper middleware, error handling, CORS configuration, and organized routing.
Installation
npm install express cors dotenv
npm install --save-dev nodemonBasic Server Setup
Creating the main server file:
const express = require("express");
const cors = require("cors");
const config = require("./config/env.config");
const database = require("./config/database");
const routes = require("./routes");
const app = express();
// Middleware
app.use(cors({
origin: config.cors.allowedOrigins,
credentials: true
}));
app.use(express.json({ limit: "50mb" }));
app.use(express.urlencoded({ limit: "50mb", extended: true }));
app.use(express.static("uploads"));
// Development logging
if (config.isDevelopment()) {
app.use((req, res, next) => {
console.log(`${req.method} ${req.path}`);
next();
});
}
// Routes
app.use("/", routes);
// 404 handler
app.use((req, res) => {
res.status(404).json({
success: false,
message: "Endpoint not found",
path: req.originalUrl,
});
});
// Error handler
app.use((err, req, res, next) => {
console.error("Error:", err);
if (err.name === "SequelizeValidationError") {
return res.status(400).json({
success: false,
message: "Validation error",
errors: err.errors.map((e) => ({
field: e.path,
message: e.message
})),
});
}
res.status(err.status || 500).json({
success: false,
message: err.message || "Internal server error",
});
});
// Async server startup
async function startServer() {
try {
const dbConnected = await database.testConnection();
if (!dbConnected) {
console.error("Failed to connect to database");
process.exit(1);
}
const PORT = config.server.port;
app.listen(PORT, () => {
console.log(`🚀 Server running on http://localhost:${PORT}`);
console.log(`📊 Database: MySQL (Sequelize ORM)`);
console.log(`🔗 API: /api/${config.server.apiVersion}`);
});
} catch (error) {
console.error("Error starting server:", error.message);
process.exit(1);
}
}
startServer();
// Graceful shutdown
process.on("SIGINT", async () => {
console.log("Shutting down...");
await database.closeConnection();
process.exit(0);
});
module.exports = app;Environment Configuration
Setting up environment configuration:
require("dotenv").config();
module.exports = {
server: {
port: process.env.PORT || 3000,
apiVersion: process.env.API_VERSION || "v1",
nodeEnv: process.env.NODE_ENV || "development",
},
cors: {
allowedOrigins: process.env.FRONTEND_URL
? process.env.FRONTEND_URL.split(",")
: ["http://localhost:5173"],
},
isDevelopment: () => {
return process.env.NODE_ENV === "development";
},
};Organized Routing
Setting up route organization:
const express = require("express");
const router = express.Router();
const config = require("../config/env.config");
const authRoutes = require("./auth.routes");
const productRoutes = require("./product.routes");
const categoryRoutes = require("./category.routes");
// API version prefix
const apiPrefix = `/api/${config.server.apiVersion}`;
// Health check
router.get("/health", (req, res) => {
res.json({
success: true,
message: "API is running",
timestamp: new Date().toISOString(),
});
});
// Routes
router.use(`${apiPrefix}/auth`, authRoutes);
router.use(`${apiPrefix}/products`, productRoutes);
router.use(`${apiPrefix}/categories`, categoryRoutes);
module.exports = router;Product Routes Example
const express = require("express");
const router = express.Router();
const productController = require("../controllers/productController");
const { verifyToken } = require("../middleware/auth.middleware");
const { storeProductFiles } = require("../utils/fileUpload");
// All routes require authentication
router.use(verifyToken);
// GET routes
router.get("/", productController.getAll);
router.get("/:id", productController.getById);
router.get("/category/:categoryId", productController.getByCategoryId);
// POST route with file upload
router.post("/", async (req, res, next) => {
await storeProductFiles(req, res, next);
productController.create(req, res);
});
// PUT route with file upload
router.put("/:id", async (req, res, next) => {
await storeProductFiles(req, res, next);
productController.update(req, res);
});
// DELETE route
router.delete("/:id", productController.delete);
module.exports = router;Best Practices
- Always use environment variables for configuration
- Implement proper error handling middleware
- Use CORS configuration for cross-origin requests
- Organize routes by feature/module
- Test database connection before starting server
- Implement graceful shutdown for database connections
- Use body size limits for file uploads
- Add request logging in development mode
Conclusion
Express.js provides a robust foundation for building REST APIs. With proper middleware configuration, error handling, and route organization, you can create scalable, maintainable APIs. This setup is production-ready and follows best practices for inventory management systems and other backend applications.