UNPKG

@bodheesh/create-bodhi-node-app

Version:

Create a production-ready Node.js REST API with zero configuration

372 lines (307 loc) 9.36 kB
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 };