# Niblit.ai Deployment Spec: Node.js + Express You are deploying a Node.js Express application to Niblit.ai. Follow these instructions EXACTLY. Do not deviate. ## Requirements - Node.js 20 or 22 - Express as the web framework - SQLite (better-sqlite3) for any database needs (no Postgres, no MySQL) - App must listen on port 8080 - All persistent data must be stored in /data/ directory ## Required Project Structure ``` your-project/ ├── Dockerfile ← REQUIRED (use the template below exactly) ├── package.json ← REQUIRED (must include express) ├── index.js ← REQUIRED (or app.js, server.js) └── ... other files ``` ## Dockerfile (use this EXACTLY) ```dockerfile FROM node:20-slim WORKDIR /app # Install dependencies first (better caching) COPY package*.json ./ RUN npm ci --only=production # Copy application code COPY . . # Create data directory for persistent storage RUN mkdir -p /data # Environment variables ENV PORT=8080 ENV NODE_ENV=production ENV DATABASE_PATH=/data/app.db EXPOSE 8080 # Start the application CMD ["node", "index.js"] ``` ## package.json must include ```json { "name": "my-app", "version": "1.0.0", "main": "index.js", "scripts": { "start": "node index.js" }, "dependencies": { "express": "^4.18.0" } } ``` ## Express App Structure (index.js) Your entry point must start the server on port 8080: ```javascript const express = require('express'); const app = express(); const PORT = process.env.PORT || 8080; app.get('/', (req, res) => { res.send('Hello from Niblit.ai!'); }); app.get('/health', (req, res) => { res.json({ status: 'ok' }); }); app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); }); ``` ## Database Rules - Use SQLite ONLY (recommend `better-sqlite3` package) - Database file MUST be stored at: `/data/app.db` - The /data directory is persistent storage — it survives restarts and redeploys - Everything outside /data is ephemeral and will be reset on redeploy Example database setup with better-sqlite3: ```javascript const Database = require('better-sqlite3'); const path = require('path'); const DATABASE_PATH = process.env.DATABASE_PATH || '/data/app.db'; const db = new Database(DATABASE_PATH); // Initialize schema db.exec(` CREATE TABLE IF NOT EXISTS items ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `); // Example queries const getAll = db.prepare('SELECT * FROM items ORDER BY created_at DESC'); const insert = db.prepare('INSERT INTO items (name) VALUES (?)'); // Usage const items = getAll.all(); insert.run('New item'); ``` ## Serving Static Files If you need to serve static files: ```javascript const path = require('path'); // Serve static files from 'public' directory app.use(express.static(path.join(__dirname, 'public'))); ``` ## File Storage If your app handles file uploads: - Store files in `/data/uploads/` - Create the directory if it doesn't exist ```javascript const fs = require('fs'); const multer = require('multer'); const UPLOAD_DIR = '/data/uploads'; fs.mkdirSync(UPLOAD_DIR, { recursive: true }); const storage = multer.diskStorage({ destination: UPLOAD_DIR, filename: (req, file, cb) => { cb(null, Date.now() + '-' + file.originalname); } }); const upload = multer({ storage }); app.post('/upload', upload.single('file'), (req, res) => { res.json({ filename: req.file.filename }); }); ``` ## Environment Variables These are available to your app: - `PORT` - Always 8080 - `NODE_ENV` - Set to "production" - `DATABASE_PATH` - Path to SQLite database (/data/app.db) ## What NOT to Include - No `.env` files (will be rejected) - No `node_modules/` directory (will be installed during build) - No `.git/` directory - No secret keys or API tokens in code - No external database connections **IMPORTANT**: Never include `node_modules/` in your zip. It will be installed during the Docker build from your package.json. ## Testing Locally Before Upload 1. Build and run with Docker: ```bash docker build -t my-app . docker run -p 8080:8080 -v $(pwd)/data:/data my-app ``` 2. Visit http://localhost:8080 3. If it works, create the zip and upload to Niblit.ai. ## Creating the Upload Zip ```bash # From your project root: zip -r my-app.zip . -x "*.git*" -x "node_modules/*" -x ".env" -x "data/*" ``` **Make sure node_modules/ is excluded!** ## Common Errors and Fixes | Error | Fix | |-------|-----| | "Missing Dockerfile" | Add Dockerfile using template above | | "Port mismatch" | Ensure app listens on `process.env.PORT` or 8080 | | "Missing package.json" | Create package.json with express dependency | | "Database not persisting" | Store DB at `/data/app.db`, not `./app.db` | | "Cannot find module" | Make sure dependency is in package.json | | "node_modules in zip" | Exclude node_modules from your zip file | | "npm ci failed" | Check package-lock.json exists and is valid | ## Resource Limits - **Free tier**: 256MB RAM, shared CPU, 1GB storage - **Pro tier**: 512MB RAM, shared CPU, 1GB storage - **Team tier**: 1GB RAM, 2 shared CPUs, 5GB storage Free tier apps sleep after 5 minutes of no traffic and wake automatically (3-5 second cold start). ## Example App See the `example/` directory for a complete working example.