{"slug": "how-i-built-an-in-memory-explicit-content-filter-in-node-js-200ms-latency-zero", "title": "How I built an in-memory explicit content filter in Node.js (200ms latency, zero images saved)", "summary": "A developer built an in-memory explicit content filter for Node.js that classifies images without ever writing them to disk, achieving roughly 200ms latency. The solution uses multer's memoryStorage and the nsfwjs library with TensorFlow.js to keep images only in RAM, addressing privacy and liability concerns. The developer also launched an API called Tabu based on this logic.", "body_md": "If you allow user-generated content in your app, you eventually run into a massive liability problem. Users will upload NSFW images.\n\nI ran into this exact problem when Apple rejected my previous app under Guideline 1.2 (User Generated Content). I needed a filter, but the standard way most tutorials teach you to handle this is flawed.\n\nUsually, the process goes like this:\n\nThe problem? The explicit image actually hits your hard drive before you know it is explicit. If the background job fails or is delayed, you are temporarily hosting illegal or policy-violating content on your servers.\n\nTo solve this, I built a single Node.js endpoint that runs the entire classification in memory.\n\nInstead of saving the file to disk, the server receives the image as a buffer, passes that buffer directly to a lightweight machine learning model, gets the score, and immediately destroys the buffer. The image never touches a hard drive.\n\nHere is the core logic using the open-source `nsfwjs`\n\nlibrary and TensorFlow.js:\n\n``` js\nconst express = require('express');\nconst multer = require('multer');\nconst tf = require('@tensorflow/tfjs-node');\nconst nsfwjs = require('nsfwjs');\n\nconst app = express();\n// Keep the file in memory, do not write to disk\nconst upload = multer({ storage: multer.memoryStorage() });\n\nlet _model;\nconst loadModel = async () => {\n    _model = await nsfwjs.load();\n};\n\napp.post('/moderate', upload.single('image'), async (req, res) => {\n    if (!req.file) {\n        return res.status(400).json({ error: 'No image provided' });\n    }\n\n    try {\n        // 1. Decode the image buffer directly from memory\n        const imageTensor = tf.node.decodeImage(req.file.buffer, 3);\n\n        // 2. Pass the tensor to the model\n        const predictions = await _model.classify(imageTensor);\n\n        // 3. Destroy the tensor to free memory immediately\n        imageTensor.dispose();\n\n        // 4. Return the scores\n        return res.json(predictions);\n\n    } catch (error) {\n        return res.status(500).json({ error: 'Processing failed' });\n    }\n});\n\nloadModel().then(() => app.listen(3000));\n```\n\n**1. Total Privacy.** Because we use `multer.memoryStorage()`\n\n, the image exists only in RAM for the fraction of a second it takes to run the classification. Once the request ends, Node.js garbage collects the buffer. You can legally guarantee your users that you are not storing their private photos.\n\n**2. Speed.** Bypassing the file system completely keeps the classification extremely fast. In my testing, it drops to roughly 200ms per image on a standard VPS.\n\n**3. Simplicity.** It is a synchronous API call. You can run this check before your database transaction even commits, keeping your backend architecture clean.\n\nIf you want to run this yourself, the code snippet above is pretty much all you need to get started. Just watch your server memory, as TensorFlow tensors will cause a memory leak if you forget to call `.dispose()`\n\n.\n\nIf you don't want to bother hosting the ML models or managing the RAM yourself, I actually wrapped this exact logic into an API called [Tabu](https://tabushield.com) that I launched recently. It has a free tier that is plenty for testing, so feel free to use it if you want to skip the server setup.\n\nLet me know how you guys handle image moderation in your own side projects!", "url": "https://wpnews.pro/news/how-i-built-an-in-memory-explicit-content-filter-in-node-js-200ms-latency-zero", "canonical_source": "https://dev.to/tabushield/how-i-built-an-in-memory-explicit-content-filter-in-nodejs-200ms-latency-zero-images-saved-jl4", "published_at": "2026-08-26 19:03:54+00:00", "updated_at": "2026-08-26 19:15:00.763870+00:00", "lang": "en", "topics": ["machine-learning", "developer-tools"], "entities": ["Node.js", "nsfwjs", "TensorFlow.js", "Tabu", "Apple"], "alternates": {"html": "https://wpnews.pro/news/how-i-built-an-in-memory-explicit-content-filter-in-node-js-200ms-latency-zero", "markdown": "https://wpnews.pro/news/how-i-built-an-in-memory-explicit-content-filter-in-node-js-200ms-latency-zero.md", "text": "https://wpnews.pro/news/how-i-built-an-in-memory-explicit-content-filter-in-node-js-200ms-latency-zero.txt", "jsonld": "https://wpnews.pro/news/how-i-built-an-in-memory-explicit-content-filter-in-node-js-200ms-latency-zero.jsonld"}}