In this post, you will learn how to implement push notifications using JavaScript by following production-grade best practices. One of the best things is that I will provide a folder structure too, so that you can set up your project easily.
Setting up push notifications in a real-world app needs careful planning. I'll show you how to build this feature in a professional Node.js app. We'll cover important parts like how to organize your code, keep things secure, and make sure it works well even as your app grows.
To get started, you need a library to help you send push notifications from your Node.js server. The web-push library provides tools for sending notifications and managing the necessary keys.
First, let’s set up the project structure to maintain a clean and scalable codebase:
/notification-service ├── /config │ ├── default.js │ └── production.js ├── /controllers │ └── notificationController.js ├── /models │ └── user.js ├── /routes │ └── notificationRoutes.js ├── /services │ ├── notificationService.js │ ├── subscriptionService.js │ └── webPushService.js ├── /utils │ └── errorHandler.js ├── /tests │ └── notification.test.js ├── app.js ├── package.json ├── .env └── README.md
Before diving into the implementation, ensure you have the following NPM packages installed:
bash npm install express mongoose web-push dotenv supertest
Create configuration files for different environments (e.g., development, production). These files store environment-specific settings.
// /config/default.js module.exports = { server: { port: 3000, env: 'development' }, pushNotifications: { publicVapidKey: process.env.VAPID_PUBLIC_KEY, privateVapidKey: process.env.VAPID_PRIVATE_KEY, gcmApiKey: process.env.GCM_API_KEY }, db: { uri: process.env.MONGO_URI } };
// /config/production.js module.exports = { server: { port: process.env.PORT || 3000, env: 'production' }, // Same structure as default, with production-specific values };
Use Mongoose to define your user schema and notification subscriptions.
// /models/user.js const mongoose = require('mongoose'); const subscriptionSchema = new mongoose.Schema({ endpoint: String, keys: { p256dh: String, auth: String } }); const userSchema = new mongoose.Schema({ email: { type: String, required: true, unique: true }, subscriptions: [subscriptionSchema], preferences: { pushNotifications: { type: Boolean, default: true } } }); module.exports = mongoose.model('User', userSchema);
Modularize the logic for handling notifications into services.
// /services/webPushService.js const webPush = require('web-push'); const config = require('config'); webPush.setVapidDetails( 'mailto:example@yourdomain.org', config.get('pushNotifications.publicVapidKey'), config.get('pushNotifications.privateVapidKey') ); module.exports = { sendNotification: async (subscription, payload) => { try { await webPush.sendNotification(subscription, JSON.stringify(payload)); } catch (error) { console.error('Error sending notification', error); } } };
// /services/notificationService.js const User = require('../models/user'); const webPushService = require('./webPushService'); module.exports = { sendPushNotifications: async (userId, payload) => { const user = await User.findById(userId); if (user && user.preferences.pushNotifications) { user.subscriptions.forEach(subscription => { webPushService.sendNotification(subscription, payload); }); } } };
Handle API routes and integrate services.
// /controllers/notificationController.js const notificationService = require('../services/notificationService'); exports.sendNotification = async (req, res, next) => { try { const { userId, title, body } = req.body; const payload = { title, body }; await notificationService.sendPushNotifications(userId, payload); res.status(200).json({ message: 'Notification sent successfully' }); } catch (error) { next(error); } };
Set up routes for your API.
// /routes/notificationRoutes.js const express = require('express'); const router = express.Router(); const notificationController = require('../controllers/notificationController'); router.post('/send', notificationController.sendNotification); module.exports = router;
Centralize error handling to ensure the app doesn’t crash.
// /utils/errorHandler.js module.exports = (err, req, res, next) => { console.error(err.stack); res.status(500).send({ error: 'Something went wrong!' }); };
Initialize the application and connect to the database.
// app.js const express = require('express'); const mongoose = require('mongoose'); const config = require('config'); const notificationRoutes = require('./routes/notificationRoutes'); const errorHandler = require('./utils/errorHandler'); const app = express(); app.use(express.json()); app.use('/api/notifications', notificationRoutes); app.use(errorHandler); mongoose.connect(config.get('db.uri'), { useNewUrlParser: true, useUnifiedTopology: true }) .then(() => console.log('MongoDB connected...')) .catch(err => console.error('MongoDB connection error:', err)); const PORT = config.get('server.port'); app.listen(PORT, () => console.log(`Server running in ${config.get('server.env')} mode on port ${PORT}`));
Write tests to ensure your service works as expected under various conditions.
// /tests/notification.test.js const request = require('supertest'); const app = require('../app'); describe('Notification API', () => { it('should send a notification', async () => { const res = await request(app) .post('/api/notifications/send') .send({ userId: 'someUserId', title: 'Test', body: 'This is a test' }); expect(res.statusCode).toEqual(200); expect(res.body.message).toBe('Notification sent successfully'); }); });
This production-grade setup ensures that your push notification system is scalable, secure, and maintainable. The code is organized to support easy testing, deployment, and monitoring, following industry best practices. If you have any further questions or need specific implementation details, feel free to ask!
The above is the detailed content of Implementing Push Notifications Using JavaScript: A Production-Grade Approach. For more information, please follow other related articles on the PHP Chinese website!