A production-ready Node.js REST API template integrating with the Anthropic Claude AI for emotion-based endpoints. This template demonstrates best practices for building secure, maintainable, and well-documented APIs.
- Features
- Quick Start
- Getting Your Anthropic API Key
- API Authentication
- API Endpoints
- Example Requests
- Configuration Reference
- Security Features
- Hosting Options
- Project Structure
- Extending the Template
- Troubleshooting
- Emotion-Based Endpoints: Positive affirmations, humorous negative affirmations, mood support, motivational quotes, wellness tips, and emotion analysis
- GET & POST Examples: Both HTTP methods demonstrated for flexibility
- Production Security: API key authentication, rate limiting, input validation, security headers
- Cost Protection: Hard token limits, dual rate limiting, usage tracking
- Resilience: Automatic retries with exponential backoff, circuit breaker pattern
- Monitoring: Health checks, readiness probes, token usage statistics
- Heavily Commented: Every file includes detailed comments explaining what the code does and why
npm install# Copy the example environment file
cp .env.example .env
# Edit .env and add your Anthropic API key
# Get your API key from: https://console.anthropic.com/# Development mode (with auto-reload)
npm run dev
# Production mode
npm start# Health check
curl http://localhost:3000/health
# Get a positive affirmation
curl http://localhost:3000/api/affirmations/positive
# Get a humorous negative affirmation
curl http://localhost:3000/api/affirmations/negative
# Get mood support (POST example)
curl -X POST http://localhost:3000/api/emotions/support \
-H "Content-Type: application/json" \
-d '{"emotion": "anxious", "context": "job interview tomorrow"}'To use this API, you need an API key from Anthropic to access Claude.
- Go to console.anthropic.com
- Click "Sign Up" and create an account
- Verify your email address
- Navigate to "Billing" in the console
- Add a credit card or payment method
- Anthropic uses pay-as-you-go pricing
- Go to "API Keys" in the console
- Click "Create Key"
- Give your key a descriptive name (e.g., "emotions-api-production")
- Copy the key immediately - it won't be shown again!
# In your .env file
ANTHROPIC_API_KEY=sk-ant-api03-your-key-here| Model | Input (per 1M tokens) | Output (per 1M tokens) |
|---|---|---|
| Claude 3.5 Sonnet | $3.00 | $15.00 |
| Claude 3.5 Haiku | $0.25 | $1.25 |
| Claude 3 Opus | $15.00 | $75.00 |
Tip: For cost-sensitive applications, use claude-3-5-haiku-20241022 as your default model.
This API supports optional API key authentication to protect your endpoints from unauthorized access.
This template uses a simple single-key approach where one API key is stored in your environment variables. This is ideal for:
- Personal projects
- Internal APIs
- Single-client applications
- Development and testing
The key is stored in your .env file and compared against incoming requests using a timing-safe comparison to prevent timing attacks.
How authentication flows:
- Client sends request with
X-API-Keyheader - Middleware compares the provided key against
API_KEYin environment - If valid, request proceeds; if invalid, returns
401 Unauthorized
Set these environment variables in your .env file:
REQUIRE_API_KEY=true
API_KEY=your-secret-api-key-hereGenerating a secure API key:
# Using Node.js
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# Using OpenSSL
openssl rand -hex 32
# Using Python
python -c "import secrets; print(secrets.token_hex(32))"Example output: a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456
Use the /api/validate endpoint to check if an API key is valid:
# Check if your key is valid
curl -H "X-API-Key: your-secret-api-key" http://localhost:3000/api/validateResponse (valid key):
{
"success": true,
"valid": true,
"message": "API key is valid",
"authRequired": true,
"timestamp": "2026-01-11T00:30:00.000Z"
}Response (invalid/missing key when auth is required):
{
"success": false,
"error": {
"message": "Invalid API key",
"statusCode": 401
}
}Include your API key in the X-API-Key header:
# With curl
curl -H "X-API-Key: your-secret-api-key-here" \
http://localhost:3000/api/affirmations/positive
# With curl.exe (Windows PowerShell)
curl.exe -H "X-API-Key: your-secret-api-key-here" http://localhost:3000/api/affirmations/positive
# With fetch (JavaScript)
fetch('http://localhost:3000/api/affirmations/positive', {
headers: {
'X-API-Key': 'your-secret-api-key-here'
}
})
# With axios (JavaScript)
axios.get('http://localhost:3000/api/affirmations/positive', {
headers: { 'X-API-Key': 'your-secret-api-key-here' }
})The API also accepts keys via:
# Bearer token in Authorization header
curl -H "Authorization: Bearer your-secret-api-key-here" \
http://localhost:3000/api/affirmations/positive
# Query parameter (less secure, use only when headers aren't possible)
curl "http://localhost:3000/api/affirmations/positive?api_key=your-secret-api-key-here"The single-key approach in this template works well for simple use cases. Consider implementing database-backed API keys when you need:
- Multiple users/clients with different keys
- Usage tracking per key for billing or analytics
- Key revocation without restarting the server
- Per-key rate limits for different service tiers
- Key expiration dates
If you need to support multiple API keys, you would:
- Create a database table to store key hashes (never store plaintext keys!)
- Modify the auth middleware to query the database instead of checking the environment variable
- Add key management endpoints for creating/revoking keys
- Hash keys before storing using SHA-256 or similar
For production with multiple users, consider using a service like:
- Auth0 - Full authentication platform
- Clerk - Developer-friendly auth
- AWS API Gateway - Includes API key management
- Kong - API gateway with key management
| Method | Endpoint | Description |
|---|---|---|
| GET/POST | /api/affirmations/positive |
Get a positive, uplifting affirmation |
| GET/POST | /api/affirmations/negative |
Get a humorous "negative" affirmation |
| Method | Endpoint | Description |
|---|---|---|
| GET/POST | /api/emotions/support |
Get supportive content for a specific emotion |
| GET | /api/emotions/motivational-quote |
Get an inspirational quote |
| GET | /api/emotions/wellness-tip |
Get a practical wellness tip |
| POST | /api/emotions/analyze |
Analyze text for emotional content |
| POST | /api/emotions/custom |
Send a custom emotion-related prompt |
| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
Basic health check (for load balancers) |
| GET | /health/detailed |
Detailed health with memory/uptime info |
| GET | /health/ready |
Readiness probe (for Kubernetes) |
| GET | /api/info |
Full API documentation |
| GET | /api/models |
List available Claude models |
| GET | /api/stats |
Token usage and circuit breaker status |
All Claude API endpoints accept these optional parameters (via query string for GET, body for POST):
| Parameter | Type | Default | Description |
|---|---|---|---|
model |
string | claude-sonnet-4-20250514 |
Claude model to use |
maxTokens |
integer | 1024 |
Maximum response length (1-4096) |
temperature |
float | 0.7 |
Creativity level (0-1) |
context |
string | - | Additional context for personalization (max 500 chars) |
emotion |
string | - | Current emotion (see valid emotions below) |
happy, sad, anxious, angry, stressed, lonely, excited, neutral
curl "http://localhost:3000/api/affirmations/positive?emotion=anxious&temperature=0.8"curl -X POST http://localhost:3000/api/emotions/analyze \
-H "Content-Type: application/json" \
-d '{
"prompt": "I just got the promotion I have been working towards!",
"model": "claude-sonnet-4-20250514",
"temperature": 0.3
}'curl -X POST http://localhost:3000/api/emotions/custom \
-H "Content-Type: application/json" \
-H "X-API-Key: your-secret-api-key" \
-d '{
"prompt": "Give me a breathing exercise for stress relief",
"maxTokens": 500
}'{
"success": true,
"type": "positive",
"affirmation": "I am capable of handling whatever challenges come my way today.",
"metadata": {
"model": "claude-sonnet-4-20250514",
"tokens": {
"input": 45,
"output": 18
}
},
"requestParams": {
"emotion": "anxious",
"context": null
}
}| Variable | Description |
|---|---|
ANTHROPIC_API_KEY |
Your Anthropic API key |
| Variable | Default | Description |
|---|---|---|
DEFAULT_MODEL |
claude-sonnet-4-20250514 |
Default Claude model |
DEFAULT_MAX_TOKENS |
1024 |
Default max response tokens |
HARD_MAX_TOKENS |
4096 |
Absolute maximum tokens (cost protection) |
MAX_PROMPT_LENGTH |
50000 |
Max input prompt characters |
| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
Server port |
NODE_ENV |
development |
Environment mode |
REQUEST_TIMEOUT_MS |
60000 |
Request timeout in ms |
MAX_BODY_SIZE |
10kb |
Maximum request body size |
| Variable | Default | Description |
|---|---|---|
REQUIRE_API_KEY |
false |
Enable API key authentication |
API_KEY |
- | Your API key for client authentication |
CORS_ORIGIN |
* |
Allowed CORS origins |
TRUST_PROXY |
1 |
Trusted proxy count |
| Variable | Default | Description |
|---|---|---|
RATE_LIMIT_MAX |
100 |
Max requests per window (general) |
RATE_LIMIT_WINDOW_MINUTES |
15 |
Rate limit window |
CLAUDE_API_RATE_LIMIT_MAX |
30 |
Max Claude API requests per window |
CLAUDE_API_RATE_LIMIT_WINDOW_MINUTES |
15 |
Claude API rate limit window |
| Variable | Default | Description |
|---|---|---|
API_MAX_RETRIES |
3 |
Max retry attempts |
API_RETRY_BASE_DELAY_MS |
1000 |
Base retry delay |
CIRCUIT_BREAKER_THRESHOLD |
5 |
Failures before circuit opens |
CIRCUIT_BREAKER_RESET_MS |
30000 |
Time before circuit retry |
- Helmet: Sets security HTTP headers (CSP, X-Frame-Options, etc.)
- CORS: Configurable cross-origin request handling
- Rate Limiting: Dual rate limiting (general + Claude API specific)
- Input Validation: All inputs validated and sanitized
- Input Sanitization: Removes null bytes, prevents prototype pollution
- Error Handling: Secure error messages (no stack traces in production)
- Body Size Limits: Prevents large payload attacks
- Request Timeouts: Prevents hanging requests
- API Key Auth: Optional authentication with timing-safe comparison
- Hard Token Limit: Absolute cap on response tokens
- Prompt Length Limit: Prevents extremely long inputs
- Dedicated API Rate Limit: Separate, stricter limit for Claude calls
- Token Usage Tracking: Monitor costs via
/api/stats - Circuit Breaker: Prevents runaway costs during outages
Railway offers simple deployment with generous free tier.
# Install Railway CLI
npm install -g @railway/cli
# Login and deploy
railway login
railway init
railway upPros: Easy setup, automatic HTTPS, free tier available Cons: Limited free tier, less control Cost: Free tier, then ~$5/month
Render provides free web service hosting.
- Connect your GitHub repository
- Create a new "Web Service"
- Set environment variables in dashboard
- Deploy automatically on push
Pros: Free tier, automatic deploys, managed SSL Cons: Free tier sleeps after inactivity Cost: Free tier, then $7/month
Heroku is a classic PaaS option.
# Install Heroku CLI
npm install -g heroku
# Login and create app
heroku login
heroku create your-app-name
# Set environment variables
heroku config:set ANTHROPIC_API_KEY=your-key
heroku config:set NODE_ENV=production
heroku config:set REQUIRE_API_KEY=true
heroku config:set API_KEY=your-api-key
# Deploy
git push heroku mainPros: Mature platform, easy scaling, add-ons marketplace Cons: No free tier anymore Cost: Starting at $5/month (Eco dynos)
DigitalOcean App Platform offers simple container deployment.
- Connect GitHub repository
- Configure as Node.js app
- Set environment variables
- Deploy
Pros: Predictable pricing, good performance Cons: Fewer integrations than AWS Cost: Starting at $5/month
For production workloads requiring scale and control.
# Install EB CLI
pip install awsebcli
# Initialize and deploy
eb init
eb create production-env
eb setenv ANTHROPIC_API_KEY=your-key NODE_ENV=productionUse serverless for pay-per-request pricing:
- Package app with serverless-http
- Deploy via Serverless Framework or SAM
- Configure API Gateway
Pros: Infinite scale, pay-per-use, enterprise features Cons: Complex setup, potential cold starts Cost: Pay-per-use, ~$0.20 per 1M requests + compute
Cloud Run offers serverless container hosting.
# Build and deploy
gcloud run deploy emotions-api \
--source . \
--set-env-vars ANTHROPIC_API_KEY=your-key,NODE_ENV=productionPros: Serverless, scales to zero, generous free tier Cons: Cold starts, Google Cloud learning curve Cost: Free tier, then pay-per-use
For maximum control, deploy to a VPS provider:
- DigitalOcean Droplets - $4/month
- Linode - $5/month
- Vultr - $5/month
- Hetzner - $4/month (EU)
# On your VPS
git clone your-repo
cd your-repo
npm install
npm install -g pm2
# Start with PM2 process manager
pm2 start src/server.js --name emotions-api
pm2 save
pm2 startupPros: Full control, best price/performance, no vendor lock-in Cons: You manage everything (updates, security, backups) Cost: $4-20/month depending on specs
Create a Dockerfile:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "src/server.js"]Deploy to any Docker-compatible platform:
- Docker Compose (local/VPS)
- Kubernetes
- AWS ECS/Fargate
- Google Kubernetes Engine
- Azure Container Instances
| Platform | Free Tier | Min Cost | SSL | Scale | Complexity |
|---|---|---|---|---|---|
| Railway | Yes | $5/mo | Auto | Good | Low |
| Render | Yes | $7/mo | Auto | Good | Low |
| Heroku | No | $5/mo | Auto | Good | Low |
| DO App Platform | No | $5/mo | Auto | Good | Low |
| AWS Beanstalk | No | ~$15/mo | Config | Excellent | Medium |
| Cloud Run | Yes | Pay/use | Auto | Excellent | Medium |
| VPS | No | $4/mo | Manual | Manual | High |
Before deploying to production:
- Set
NODE_ENV=production - Set
REQUIRE_API_KEY=truewith a strongAPI_KEY - Set
CORS_ORIGINto your specific domain(s) - Set
HARD_MAX_TOKENSto control costs - Set
CLAUDE_API_RATE_LIMIT_MAXappropriately - Set
LOG_FORMAT=combinedfor full logging - Configure your reverse proxy/load balancer
- Set up monitoring and alerting
- Enable HTTPS (usually automatic with PaaS)
- Test health endpoints with your orchestrator
nodejs-claude-ai-API-template/
├── src/
│ ├── config/
│ │ └── index.js # Environment configuration & validation
│ ├── middleware/
│ │ ├── errorHandler.js # Error handling, async wrapper
│ │ ├── security.js # API key auth, sanitization, request ID
│ │ └── validation.js # Input validation rules
│ ├── routes/
│ │ ├── affirmations.js # Positive/negative affirmation endpoints
│ │ ├── emotions.js # Emotion support, analysis, quotes
│ │ └── health.js # Health checks, monitoring, API info
│ ├── services/
│ │ └── claudeService.js # Claude API with retry & circuit breaker
│ └── server.js # Express app entry point
├── .env.example # Example environment variables
├── .gitignore # Git ignore rules
├── package.json # Dependencies and scripts
└── README.md # This file
- Create or modify a route file in
src/routes/ - Add validation rules if needed in
src/middleware/validation.js - Use the
claudeServicefor AI interactions - Wrap async handlers with
asyncHandler - Mount the route in
src/server.js
System prompts define Claude's behavior. Add them to your route file:
const SYSTEM_PROMPTS = {
myNewPrompt: `You are a helpful assistant that...`,
};
// Use it in your route handler
const response = await promptWithSystem(
SYSTEM_PROMPTS.myNewPrompt,
userPrompt,
{ model, maxTokens, temperature }
);Add the model ID to the whitelist in src/config/index.js:
validModels: [
'claude-opus-4-5-20251101',
'claude-sonnet-4-20250514',
// Add your new model here
'new-model-id',
],Ensure ANTHROPIC_API_KEY is set in your .env file and the file is in your project root.
- Check your key at console.anthropic.com
- Ensure there are no extra spaces or quotes around the key
- Verify the key has not been revoked
- Check
/api/statsto see current usage - Increase
CLAUDE_API_RATE_LIMIT_MAXif needed - Consider using a faster model for high-volume use cases
The circuit breaker opens after 5 consecutive failures. Check:
- Your Anthropic API key is valid
- You have sufficient API credits
- The Anthropic API status at status.anthropic.com
Reset manually (development only):
curl -X POST http://localhost:3000/api/stats/reset- Lower
HARD_MAX_TOKENSto limit response length - Lower
DEFAULT_MAX_TOKENSfor shorter default responses - Switch to
claude-3-5-haiku-20241022(cheapest model) - Lower
CLAUDE_API_RATE_LIMIT_MAXto limit requests
MIT
Contributions are welcome! Please feel free to submit a Pull Request.