← All Stacks
💚

Node / Express

Raw Text

✨ How to use: Copy this entire spec and paste it into your AI assistant (Claude, ChatGPT, etc.) when asking it to prepare your project for Niblit.ai deployment.

# 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.