A production-grade REST API that serves random quotes from famous Indian scholars and scientists β built with Node.js, Express, and SQLite. Designed to be deployable anywhere in minutes with zero external dependencies.
The Quotes API is a stateless, scalable HTTP service that exposes a curated dataset of 25+ quotes from luminaries such as A.P.J. Abdul Kalam, C.V. Raman, Srinivasa Ramanujan, Jagadish Chandra Bose, and more. The architecture is deliberately simple β one file-based SQLite database, no background processes β making it trivial to run locally or deploy on a PaaS like Render.
| Feature | Detail |
|---|---|
| Random quote endpoint | Scalable offset-based selection (no ORDER BY RANDOM()) |
| Health check | /api/v1/health β uptime + timestamp |
| Rate limiting | 60 req / min / IP via express-rate-limit |
| Security headers | helmet + cors |
| Structured logging | pino with pretty-print in dev, JSON in prod |
| Graceful shutdown | SIGTERM / SIGINT handling with connection cleanup |
| Redis-ready | Drop-in store swap when you need distributed rate limiting |
| Zero external DB | SQLite file β no Postgres, no MongoDB, no setup |
- Runtime β Node.js 18+ LTS
- Framework β Express.js 4
- Database β SQLite via
better-sqlite3 - Logging β
pino+pino-pretty - Security β
helmet,cors - Rate limiting β
express-rate-limit - Config β
dotenv
- Node.js 18 or newer (
node -v) - npm 9 or newer (
npm -v)
git clone https://github.com/your-username/quotes-api.git
cd quotes-api
npm installcp .env.example .env
# Edit .env if you want to change PORT or DB_PATHnpm run seedYou should see:
β
Seed complete β 25 new row(s) inserted. Total in DB: 25
# Development (with pretty logs + auto-restart via nodemon)
npm run dev
# Production
npm startThe server will be live at http://localhost:3000.
Returns a single random quote.
Response 200 OK:
{
"success": true,
"data": {
"quote": {
"id": 4,
"quote": "Dream is not that which you see while sleeping; it is something that does not let you sleep.",
"author": "A.P.J. Abdul Kalam"
}
}
}Health check β used by load balancers and uptime monitors.
Response 200 OK:
{
"success": true,
"data": {
"status": "ok",
"uptime": 42.317,
"timestamp": "2024-06-01T10:00:00.000Z",
"service": "quotes-api"
}
}All errors follow the same shape:
{
"success": false,
"error": "Route GET /api/v1/does-not-exist not found."
}| Status | Scenario |
|---|---|
404 |
Route or resource not found |
429 |
Rate limit exceeded |
500 |
Internal server error |
# Random quote
curl http://localhost:3000/api/v1/quote
# Health check
curl http://localhost:3000/api/v1/health
# With pretty JSON output (requires jq)
curl -s http://localhost:3000/api/v1/quote | jq .
# Check rate-limit headers
curl -I http://localhost:3000/api/v1/quoteRender is a great free-tier PaaS for Node.js APIs. Here's how to deploy:
git init
git add .
git commit -m "feat: initial production API"
git remote add origin https://github.com/your-username/quotes-api.git
git push -u origin main- Log in at render.com and click New β Web Service
- Connect your GitHub repo
- Fill in the settings:
| Field | Value |
|---|---|
| Name | quotes-api |
| Region | Choose closest to your users |
| Branch | main |
| Runtime | Node |
| Build Command | npm install && npm run seed |
| Start Command | npm start |
| Instance Type | Free (or Starter for always-on) |
In Environment β Add Environment Variable:
| Key | Value |
|---|---|
NODE_ENV |
production |
PORT |
10000 (Render sets this automatically) |
DB_PATH |
./database/quotes.db |
β οΈ Persistent disk note: Render's free tier has an ephemeral filesystem β the DB resets on each deploy. To persist data across deploys, attach a Render Disk (paid) mounted at/var/dataand setDB_PATH=/var/data/quotes.db. The seed script is idempotent and safe to re-run.
Click Create Web Service. Render will build, seed, and start your API. You'll get a public URL like:
https://quotes-api-xxxx.onrender.com/api/v1/quote
curl https://quotes-api-xxxx.onrender.com/api/v1/healthquotes-api/
βββ src/
β βββ app.js # Express app factory (middlewares + routes)
β βββ server.js # HTTP server + graceful shutdown
β βββ config/
β β βββ env.js # Centralised env var access
β β βββ database.js # SQLite singleton connection
β β βββ logger.js # Pino logger instance
β βββ controllers/
β β βββ quoteController.js
β βββ routes/
β β βββ quoteRoutes.js
β βββ services/
β β βββ quoteService.js # Business logic + scalable random query
β βββ middleware/
β β βββ rateLimiter.js
β β βββ errorHandler.js
β β βββ requestLogger.js
β βββ utils/
β βββ response.js # sendSuccess / sendError helpers
βββ database/
β βββ init.sql # Raw schema (informational)
β βββ seed.js # Idempotent seed script
βββ .env.example
βββ package.json
βββ README.md
The rate limiter uses an in-memory store by default. When you need distributed rate limiting across multiple instances, swap the store in src/middleware/rateLimiter.js:
const RedisStore = require('rate-limit-redis');
const { createClient } = require('redis');
const redisClient = createClient({ url: env.REDIS_URL });
await redisClient.connect();
const rateLimiter = rateLimit({
// ... existing config ...
store: new RedisStore({ sendCommand: (...args) => redisClient.sendCommand(args) }),
});Set REDIS_URL in your .env and install the packages:
npm install rate-limit-redis redisNo other changes required β the rest of the codebase remains stateless.
MIT