Node.js & Express – Dhisidda Barnaamijyo Backend oo Maamuli karo
Node.js ayaa si weyn u beddelay horumarinta JavaScript-ka ee dhinaca server-ka, halka Express.js ay ka dhigto mid aad u fudud in laga dhiso API-yo iyo barnaamijyo shabakad awood badan. Aan u dhex galno dhisidda backend diyaar u ah wax-soo-saar!
🌟 Muxay Node.js & Express muhiim u yihiin ?
Node.js iyo Express waxay hoggaansan yihiin deegaanka backend ee JavaScript sababo dhowr ah dartood:
- ⚡ Non-blocking I/O: Waxay maareeyaan kumannaan isku xirka is barbar socda
- 🔄 JavaScript meel walba: Hal luqad ka shaqaysa frontend iyo backend
- 📦 Deegaanka NPM: In ka badan 2 milyan oo xidhmooyin ah
- 🚀 Horumar degdeg ah: Dhis oo qaadid si dhaqso ah
- 💪 La kordhin karo: Ku habboon microservices iyo abka waqtiga-dhabta ah
- 👥 Bulsho firfircoon: Taageero iyo agab fara badan
Goorma ayaa Node.js la isticmaalaa?
✅ Ku habboon:
- Barnaamijyo waqtiga-dhabta ah (chat, ciyaaro, wada-shaqeyn)
- RESTful APIs iyo microservices
- Single-page applications
- Barnaamijyo qulqul (streaming)
- IoT applications
❌ Lama doorbido:
- Hawlo CPU culus ah
- Xisaab adag oo qalafsan
📦 Sida loo bilaabo
Shuruudaha Hore
# Hubi nooca Node.js (ugu yaraan 18)
node --version
# Hubi nooca npm
npm --version
# Haddii aan la rakibin, ka soo dejiso nodejs.org
Dejinta Mashruuca
# Abuur galka mashruuca
mkdir express-ecommerce-api
cd express-ecommerce-api
# Bilow npm project
npm init -y
# Ku rakib ku-tiirsanaanta
npm install express mongoose dotenv cors helmet morgan
npm install --save-dev nodemon
# Ku rakib agab horumarineed
npm install --save-dev @types/node @types/express
🏗️ Qaab-dhismeedka Mashruuca
Dhiso qaab sax ah oo la kordhin karo:
express-ecommerce-api/
├── src/
│ ├── config/
│ │ └── database.js
│ ├── controllers/
│ │ ├── productController.js
│ │ └── orderController.js
│ ├── models/
│ │ ├── Product.js
│ │ └── Order.js
│ ├── routes/
│ │ ├── productRoutes.js
│ │ └── orderRoutes.js
│ ├── middleware/
│ │ ├── errorHandler.js
│ │ └── validateRequest.js
│ ├── utils/
│ │ └── logger.js
│ └── app.js
├── .env
├── .gitignore
├── package.json
└── server.js
🎯 Dhisidda API E-Commerce ah
Aan dhisno API e-commerce oo dhamaystiran oo haya badeecooyin iyo amarro.
Tallaabada 1: Dejinta Deegaanka
Abuur .env:
# Dejinta server-ka
PORT=5000
NODE_ENV=development
# Kaydka xogta
MONGODB_URI=mongodb://localhost:27017/ecommerce
# Ama isticmaal MongoDB Atlas:
# MONGODB_URI=mongodb+srv://username:password@cluster.mongodb.net/ecommerce
# Sirta JWT
JWT_SECRET=your_super_secret_key_change_in_production
# Dejinta API
API_VERSION=v1
Abuur .gitignore:
node_modules/
.env
*.log
dist/
build/
Tallaabada 2: Dejinta Xiriirka Kaydka Xogta
Abuur src/config/database.js:
const mongoose = require('mongoose');
const connectDB = async () => {
try {
const conn = await mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
console.log(`✅ MongoDB Connected: ${conn.connection.host}`);
} catch (error) {
console.error(`❌ Error: ${error.message}`);
process.exit(1);
}
};
module.exports = connectDB;
Tallaabada 3: Abuur Model-yada
Abuur src/models/Product.js:
const mongoose = require('mongoose');
const productSchema = new mongoose.Schema(
{
name: {
type: String,
required: [true, 'Product name is required'],
trim: true,
maxlength: [100, 'Product name cannot exceed 100 characters'],
},
description: {
type: String,
required: [true, 'Product description is required'],
maxlength: [2000, 'Description cannot exceed 2000 characters'],
},
price: {
type: Number,
required: [true, 'Product price is required'],
min: [0, 'Price cannot be negative'],
},
category: {
type: String,
required: [true, 'Product category is required'],
enum: ['Electronics', 'Clothing', 'Books', 'Home', 'Sports', 'Other'],
},
stock: {
type: Number,
required: true,
min: [0, 'Stock cannot be negative'],
default: 0,
},
images: [{
type: String,
}],
rating: {
type: Number,
default: 0,
min: 0,
max: 5,
},
numReviews: {
type: Number,
default: 0,
},
featured: {
type: Boolean,
default: false,
},
},
{
timestamps: true,
}
);
// Index for better search performance
productSchema.index({ name: 'text', description: 'text' });
module.exports = mongoose.model('Product', productSchema);
Abuur src/models/Order.js:
const mongoose = require('mongoose');
const orderSchema = new mongoose.Schema(
{
orderNumber: {
type: String,
required: true,
unique: true,
},
customer: {
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
phone: {
type: String,
required: true,
},
},
items: [{
product: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Product',
required: true,
},
quantity: {
type: Number,
required: true,
min: 1,
},
price: {
type: Number,
required: true,
},
}],
shippingAddress: {
street: String,
city: String,
state: String,
zipCode: String,
country: String,
},
totalAmount: {
type: Number,
required: true,
},
status: {
type: String,
enum: ['pending', 'processing', 'shipped', 'delivered', 'cancelled'],
default: 'pending',
},
paymentStatus: {
type: String,
enum: ['pending', 'completed', 'failed'],
default: 'pending',
},
},
{
timestamps: true,
}
);
// Generate order number before saving
orderSchema.pre('save', async function(next) {
if (!this.orderNumber) {
this.orderNumber = `ORD-${Date.now()}-${Math.floor(Math.random() * 1000)}`;
}
next();
});
module.exports = mongoose.model('Order', orderSchema);
Tallaabada 4: Abuur Controllers
Abuur src/controllers/productController.js:
const Product = require('../models/Product');
// @desc Get all products
// @route GET /api/v1/products
// @access Public
exports.getProducts = async (req, res, next) => {
try {
const { page = 1, limit = 10, category, search, sort } = req.query;
// Build query
const query = {};
if (category) query.category = category;
if (search) query.$text = { $search: search };
// Build sort
let sortOption = {};
if (sort === 'price_asc') sortOption.price = 1;
else if (sort === 'price_desc') sortOption.price = -1;
else if (sort === 'rating') sortOption.rating = -1;
else sortOption.createdAt = -1;
// Execute query with pagination
const products = await Product.find(query)
.sort(sortOption)
.limit(limit * 1)
.skip((page - 1) * limit)
.exec();
const count = await Product.countDocuments(query);
res.status(200).json({
success: true,
count: products.length,
total: count,
page: Number(page),
pages: Math.ceil(count / limit),
data: products,
});
} catch (error) {
next(error);
}
};
// @desc Get single product
// @route GET /api/v1/products/:id
// @access Public
exports.getProduct = async (req, res, next) => {
try {
const product = await Product.findById(req.params.id);
if (!product) {
return res.status(404).json({
success: false,
message: 'Product not found',
});
}
res.status(200).json({
success: true,
data: product,
});
} catch (error) {
next(error);
}
};
// @desc Create product
// @route POST /api/v1/products
// @access Private/Admin
exports.createProduct = async (req, res, next) => {
try {
const product = await Product.create(req.body);
res.status(201).json({
success: true,
data: product,
});
} catch (error) {
next(error);
}
};
// @desc Update product
// @route PUT /api/v1/products/:id
// @access Private/Admin
exports.updateProduct = async (req, res, next) => {
try {
const product = await Product.findByIdAndUpdate(
req.params.id,
req.body,
{
new: true,
runValidators: true,
}
);
if (!product) {
return res.status(404).json({
success: false,
message: 'Product not found',
});
}
res.status(200).json({
success: true,
data: product,
});
} catch (error) {
next(error);
}
};
// @desc Delete product
// @route DELETE /api/v1/products/:id
// @access Private/Admin
exports.deleteProduct = async (req, res, next) => {
try {
const product = await Product.findByIdAndDelete(req.params.id);
if (!product) {
return res.status(404).json({
success: false,
message: 'Product not found',
});
}
res.status(200).json({
success: true,
message: 'Product deleted successfully',
});
} catch (error) {
next(error);
}
};
// @desc Get featured products
// @route GET /api/v1/products/featured
// @access Public
exports.getFeaturedProducts = async (req, res, next) => {
try {
const products = await Product.find({ featured: true })
.limit(10)
.sort('-rating');
res.status(200).json({
success: true,
count: products.length,
data: products,
});
} catch (error) {
next(error);
}
};
Tallaabada 5: Abuur Routes
Abuur src/routes/productRoutes.js:
const express = require('express');
const {
getProducts,
getProduct,
createProduct,
updateProduct,
deleteProduct,
getFeaturedProducts,
} = require('../controllers/productController');
const router = express.Router();
router.get('/featured', getFeaturedProducts);
router.route('/')
.get(getProducts)
.post(createProduct);
router.route('/:id')
.get(getProduct)
.put(updateProduct)
.delete(deleteProduct);
module.exports = router;
Tallaabada 6: Middleware
Abuur src/middleware/errorHandler.js:
const errorHandler = (err, req, res, next) => {
let error = { ...err };
error.message = err.message;
// Log to console for dev
console.error(err);
// Mongoose bad ObjectId
if (err.name === 'CastError') {
const message = 'Resource not found';
error = { message, statusCode: 404 };
}
// Mongoose duplicate key
if (err.code === 11000) {
const message = 'Duplicate field value entered';
error = { message, statusCode: 400 };
}
// Mongoose validation error
if (err.name === 'ValidationError') {
const message = Object.values(err.errors).map(val => val.message);
error = { message, statusCode: 400 };
}
res.status(error.statusCode || 500).json({
success: false,
error: error.message || 'Server Error',
});
};
module.exports = errorHandler;
Tallaabada 7: Codsiga Ugu Weyn
Abuur src/app.js:
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const errorHandler = require('./middleware/errorHandler');
// Import routes
const productRoutes = require('./routes/productRoutes');
const app = express();
// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());
app.use(helmet());
// Logging
if (process.env.NODE_ENV === 'development') {
app.use(morgan('dev'));
}
// Health check
app.get('/health', (req, res) => {
res.status(200).json({
success: true,
message: 'Server is healthy',
timestamp: new Date().toISOString(),
});
});
// API routes
app.use('/api/v1/products', productRoutes);
// Root route
app.get('/', (req, res) => {
res.json({
message: 'Welcome to E-Commerce API',
version: '1.0.0',
endpoints: {
products: '/api/v1/products',
health: '/health',
},
});
});
// Error handler (must be last)
app.use(errorHandler);
// 404 handler
app.use((req, res) => {
res.status(404).json({
success: false,
message: 'Route not found',
});
});
module.exports = app;
Abuur server.js:
require('dotenv').config();
const app = require('./src/app');
const connectDB = require('./src/config/database');
const PORT = process.env.PORT || 5000;
// Connect to database
connectDB();
// Start server
const server = app.listen(PORT, () => {
console.log(`🚀 Server running in ${process.env.NODE_ENV} mode on port ${PORT}`);
});
// Handle unhandled promise rejections
process.on('unhandledRejection', (err) => {
console.log(`❌ Error: ${err.message}`);
server.close(() => process.exit(1));
});
Cusboonaysii package.json:
{
"name": "express-ecommerce-api",
"version": "1.0.0",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
}
}
🚀 Socodsiinta Codsiga
# Habka horumarinta oo leh auto-reload
npm run dev
# Habka wax-soo-saarka
npm start
API-gaaga waxa laga heli doonaa http://localhost:5000
🧪 Tijaabinta API-ga
Abuurista Badeecad
curl -X POST http://localhost:5000/api/v1/products \
-H "Content-Type: application/json" \
-d '{
"name": "MacBook Pro 16-inch",
"description": "Powerful laptop for developers",
"price": 2499,
"category": "Electronics",
"stock": 50,
"featured": true
}'
Hel dhammaan Badeecooyinka
# Codsi aasaasi ah
curl http://localhost:5000/api/v1/products
# Iyadoo pagination ah
curl "http://localhost:5000/api/v1/products?page=1&limit=10"
# Ku shaandhee qayb
curl "http://localhost:5000/api/v1/products?category=Electronics"
# Raadi badeecooyin
curl "http://localhost:5000/api/v1/products?search=laptop"
# Ku kala saar qiimo
curl "http://localhost:5000/api/v1/products?sort=price_asc"
⚡ Astaamo Hormarsan
1. Hubinta Codsiyada (Request Validation)
Ku rakib validator:
npm install express-validator
Abuur middleware validation:
const { body, validationResult } = require('express-validator');
exports.validateProduct = [
body('name').trim().notEmpty().withMessage('Name is required'),
body('price').isNumeric().withMessage('Price must be a number'),
body('stock').isInt({ min: 0 }).withMessage('Stock must be non-negative'),
(req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
next();
},
];
2. Rate Limiting
npm install express-rate-limit
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 daqiiqo
max: 100, // Xad 100 codsi IP kasta
message: 'Codsiyo badan, fadlan dib isku day goor dambe',
});
app.use('/api/', limiter);
3. Async Handler Wrapper
const asyncHandler = fn => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
// Isticmaal
exports.getProducts = asyncHandler(async (req, res) => {
const products = await Product.find();
res.json({ data: products });
});
4. File Upload
npm install multer
const multer = require('multer');
const storage = multer.diskStorage({
destination: './uploads/',
filename: (req, file, cb) => {
cb(null, `${Date.now()}-${file.originalname}`);
},
});
const upload = multer({ storage });
app.post('/upload', upload.single('image'), (req, res) => {
res.json({ filename: req.file.filename });
});
🔒 Hab-raacyada Amniga
1. Environment Variables
Waligaa ha commit-garin faylasha .env. Samee configs kala duwan deegaan kasta.
2. Nadiifinta Gelinta (Input Sanitization)
npm install express-mongo-sanitize
const mongoSanitize = require('express-mongo-sanitize');
app.use(mongoSanitize());
3. Ka-hortagga XSS
npm install xss-clean
const xss = require('xss-clean');
app.use(xss());
4. Cinwaannada Amniga HTTP
Waxaan hore u isticmaalnay helmet:
app.use(helmet());
📊 Hagaajinta Waxqabadka
1. Kaydinta (Caching) Redis
npm install redis
const redis = require('redis');
const client = redis.createClient();
// Cache middleware
const cache = (req, res, next) => {
const key = req.originalUrl;
client.get(key, (err, data) => {
if (data) {
return res.json(JSON.parse(data));
}
next();
});
};
2. Indexing ee kaydka xogta
// Gudaha model-kaaga
productSchema.index({ name: 1, category: 1 });
3. Cadaadinta jawaabaha
npm install compression
const compression = require('compression');
app.use(compression());
🚀 Daabacaadda (Deployment)
Adeegsiga PM2
npm install -g pm2
# Bilow app
pm2 start server.js --name "ecommerce-api"
# La soco
pm2 monit
# Dib u bilow
pm2 restart ecommerce-api
Docker
Abuur Dockerfile:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 5000
CMD ["node", "server.js"]
🎓 Qodobbo Muhiim ah
- Express waa yar: Adigaa leh xakamaynta dhammaystiran ee qaab-dhismeedka
- Qaabka Middleware: Awood badan oo dabacsanaan ah marka la farsameynayo codsiyada
- Async/await: JavaScript casri ah oo loogu talagalay hawlo asynchronous ah
- Mongoose: Hab hirgelin oo nadiif ah oo MongoDB ah
- Amniga: Had iyo jeer hirgeli hab-raacyo amni
- La kordhin karo: Node.js wuxuu ku fiican yahay maareynta isku xiryo badan oo isku mar ah
📚 Maxaa Xiga?
- Ku dar xaqiijinta JWT
- Kudar WebSocket si aad u hesho astaamo waqtiga-dhabta ah
- Ku dar taageerada GraphQL
- Ku xir albaabada lacag-bixinta (Stripe, PayPal)
- Deji tijaabooyin Jest ah
- Ku daabac AWS, Heroku, ama DigitalOcean
Node.js iyo Express waxay bixiyaan aasaas adag oo lagu dhiso APIs casri ah oo la kordhin karo. Bilow dhisidda API-yadaada maanta!
Ma rabtaa inaad wax badan ka barato? Ka eeg manhajka horumarinta backend ama nagala xiriir Twitter!
