@bodheesh/create-bodhi-node-app
Version:
Create a production-ready Node.js REST API with zero configuration
372 lines (307 loc) • 9.36 kB
JavaScript
const fs = require('fs').promises;
const path = require('path');
async function generateMongoDBAPI(schema, outputDir, type) {
if (type === 'rest') {
await generateRESTAPI(schema, outputDir);
} else if (type === 'graphql') {
await generateGraphQLAPI(schema, outputDir);
}
}
async function generateRESTAPI(schema, outputDir) {
// Create directory structure
const dirs = ['models', 'controllers', 'routes', 'middleware'];
for (const dir of dirs) {
await fs.mkdir(path.join(outputDir, dir), { recursive: true });
}
// Generate model files
await generateModels(schema, outputDir);
// Generate controller files
await generateControllers(schema, outputDir);
// Generate route files
await generateRoutes(schema, outputDir);
// Generate main index.js
await generateMainFile(schema, outputDir);
}
async function generateModels(schema, outputDir) {
// Generate User model for auth
const userModelContent = `
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
}
}, {
timestamps: true
});
userSchema.pre('save', async function(next) {
if (this.isModified('password')) {
this.password = await bcrypt.hash(this.password, 10);
}
next();
});
userSchema.methods.comparePassword = async function(candidatePassword) {
return bcrypt.compare(candidatePassword, this.password);
};
module.exports = mongoose.model('User', userSchema);
`;
// Generate schema model
const modelContent = `
const mongoose = require('mongoose');
const ${schema.name}Schema = new mongoose.Schema(${JSON.stringify(schema.fields, null, 2)}, {
timestamps: true
});
module.exports = mongoose.model('${schema.name}', ${schema.name}Schema);
`;
await fs.writeFile(
path.join(outputDir, 'models', 'user.model.js'),
userModelContent
);
await fs.writeFile(
path.join(outputDir, 'models', `${schema.name.toLowerCase()}.model.js`),
modelContent
);
}
async function generateControllers(schema, outputDir) {
// Generate auth controller
const authControllerContent = `
const User = require('../models/user.model');
const jwt = require('jsonwebtoken');
exports.signup = async (req, res) => {
try {
const { username, email, password } = req.body;
// Check if user already exists
const existingUser = await User.findOne({ $or: [{ email }, { username }] });
if (existingUser) {
return res.status(400).json({ message: 'User already exists' });
}
// Create new user
const user = new User({ username, email, password });
await user.save();
// Generate token
const token = jwt.sign(
{ userId: user._id },
process.env.JWT_SECRET || 'your-secret-key',
{ expiresIn: '24h' }
);
res.status(201).json({ token });
} catch (error) {
res.status(500).json({ message: error.message });
}
};
exports.login = async (req, res) => {
try {
const { email, password } = req.body;
// Find user
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({ message: 'Invalid credentials' });
}
// Check password
const isValidPassword = await user.comparePassword(password);
if (!isValidPassword) {
return res.status(401).json({ message: 'Invalid credentials' });
}
// Generate token
const token = jwt.sign(
{ userId: user._id },
process.env.JWT_SECRET || 'your-secret-key',
{ expiresIn: '24h' }
);
res.json({ token });
} catch (error) {
res.status(500).json({ message: error.message });
}
};
`;
// Generate CRUD controller
const controllerContent = `
const ${schema.name} = require('../models/${schema.name.toLowerCase()}.model');
// Create
exports.create = async (req, res) => {
try {
const new${schema.name} = new ${schema.name}(req.body);
const saved${schema.name} = await new${schema.name}.save();
res.status(201).json(saved${schema.name});
} catch (error) {
res.status(400).json({ message: error.message });
}
};
// Read all
exports.findAll = async (req, res) => {
try {
const { page = 1, limit = 10 } = req.query;
const skip = (page - 1) * limit;
const items = await ${schema.name}
.find()
.limit(limit * 1)
.skip(skip)
.exec();
const count = await ${schema.name}.countDocuments();
res.json({
items,
totalPages: Math.ceil(count / limit),
currentPage: page
});
} catch (error) {
res.status(500).json({ message: error.message });
}
};
// Read one
exports.findOne = async (req, res) => {
try {
const item = await ${schema.name}.findById(req.params.id);
if (!item) {
return res.status(404).json({ message: 'Item not found' });
}
res.json(item);
} catch (error) {
res.status(500).json({ message: error.message });
}
};
// Update
exports.update = async (req, res) => {
try {
const updated${schema.name} = await ${schema.name}.findByIdAndUpdate(
req.params.id,
req.body,
{ new: true }
);
if (!updated${schema.name}) {
return res.status(404).json({ message: 'Item not found' });
}
res.json(updated${schema.name});
} catch (error) {
res.status(400).json({ message: error.message });
}
};
// Delete
exports.delete = async (req, res) => {
try {
const deleted${schema.name} = await ${schema.name}.findByIdAndDelete(req.params.id);
if (!deleted${schema.name}) {
return res.status(404).json({ message: 'Item not found' });
}
res.json({ message: 'Item deleted successfully' });
} catch (error) {
res.status(500).json({ message: error.message });
}
};
`;
await fs.writeFile(
path.join(outputDir, 'controllers', 'auth.controller.js'),
authControllerContent
);
await fs.writeFile(
path.join(outputDir, 'controllers', `${schema.name.toLowerCase()}.controller.js`),
controllerContent
);
}
async function generateRoutes(schema, outputDir) {
// Generate auth middleware
const authMiddlewareContent = `
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
try {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ message: 'Authentication required' });
}
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'your-secret-key');
req.userData = decoded;
next();
} catch (error) {
return res.status(401).json({ message: 'Authentication failed' });
}
};
`;
// Generate auth routes
const authRoutesContent = `
const express = require('express');
const router = express.Router();
const authController = require('../controllers/auth.controller');
router.post('/signup', authController.signup);
router.post('/login', authController.login);
module.exports = router;
`;
// Generate CRUD routes
const routesContent = `
const express = require('express');
const router = express.Router();
const ${schema.name.toLowerCase()}Controller = require('../controllers/${schema.name.toLowerCase()}.controller');
const auth = require('../middleware/auth');
// Create
router.post('/', auth, ${schema.name.toLowerCase()}Controller.create);
// Read all
router.get('/', ${schema.name.toLowerCase()}Controller.findAll);
// Read one
router.get('/:id', ${schema.name.toLowerCase()}Controller.findOne);
// Update
router.put('/:id', auth, ${schema.name.toLowerCase()}Controller.update);
// Delete
router.delete('/:id', auth, ${schema.name.toLowerCase()}Controller.delete);
module.exports = router;
`;
await fs.writeFile(
path.join(outputDir, 'middleware', 'auth.js'),
authMiddlewareContent
);
await fs.writeFile(
path.join(outputDir, 'routes', 'auth.routes.js'),
authRoutesContent
);
await fs.writeFile(
path.join(outputDir, 'routes', `${schema.name.toLowerCase()}.routes.js`),
routesContent
);
}
async function generateMainFile(schema, outputDir) {
const mainFileContent = `
require('dotenv').config();
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const app = express();
// Middleware
app.use(cors());
app.use(express.json());
// Connect to MongoDB
mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/${schema.name.toLowerCase()}-api')
.then(() => console.log('Connected to MongoDB'))
.catch(err => console.error('MongoDB connection error:', err));
// Import routes
const authRoutes = require('./routes/auth.routes');
const ${schema.name.toLowerCase()}Routes = require('./routes/${schema.name.toLowerCase()}.routes');
// Use routes
app.use('/api/auth', authRoutes);
app.use('/api/${schema.name.toLowerCase()}s', ${schema.name.toLowerCase()}Routes);
// Error handling middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ message: 'Something went wrong!' });
});
// Start server
const PORT = process.env.PORT || 3003;
app.listen(PORT, () => {
console.log(\`Server is running on port \${PORT}\`);
});
`;
await fs.writeFile(
path.join(outputDir, 'index.js'),
mainFileContent
);
}
module.exports = {
generateMongoDBAPI
};