A simple serverless REST API built with Cloudflare Workers and D1 SQLite database for managing a book collection.
- Full CRUD operations for books with input validation
- Pagination and filtering with query parameters
- Full-text search across all book fields
- Rate limiting using Cloudflare Workers Rate Limiting API
- Comprehensive error handling with structured logging
- Request validation including content-type and size checks
- Response caching for optimized performance
- CORS support for cross-origin requests
- Health monitoring and statistics endpoints
npm create cloudflare@latest -- books-serverless-apiNote: This project uses the new json wrangler configuration file.
npx wrangler d1 create prod-d1-books-serverless-apiAdd the database binding to your wrangler.json:
{
"d1_databases": [
{
"binding": "DB",
"database_name": "prod-d1-books-serverless-api",
"database_id": "<your-database-id>"
}
]
}npx wrangler d1 execute prod-d1-books-serverless-api --file=database_schema_01.sql --remoteSee database_schema_01.sql and OpenAPI Specification Schema.
Add a rate limiting binding to your wrangler.json:
{
"rate_limit": [
{
"binding": "RATE_LIMITER",
"simple": {
"limit": 100,
"period": 60
}
}
]
}This allows 100 requests per minute. Adjust as needed for your use case.
CREATE TABLE books (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
author TEXT NOT NULL,
year INTEGER,
isbn TEXT,
genre TEXT,
description TEXT
);GET /api/healthReturns the API's operational status and timestamp.
Response:
{
"status": "healthy",
"timestamp": "2025-11-10T12:00:00.000Z"
}GET /api/statsReturns overall statistics including total books, genre breakdown, and publication year range.
Response:
{
"totalBooks": 42,
"genreBreakdown": [
{ "genre": "Fiction", "count": 15 },
{ "genre": "Science Fiction", "count": 12 }
],
"yearRange": {
"earliest": 1949,
"latest": 2024
}
}GET /api/booksQuery Parameters:
page(optional): Page number, default: 1limit(optional): Items per page, default: 10, max: 100genre(optional): Filter by genreyear(optional): Filter by publication year
Response:
{
"data": [
{
"id": 1,
"title": "1984",
"author": "George Orwell",
"year": 1949,
"isbn": "9780451524935",
"genre": "Fiction",
"description": "A dystopian social science fiction novel"
}
],
"pagination": {
"total": 42,
"page": 1,
"limit": 10,
"pages": 5
}
}POST /api/books
Content-Type: application/jsonRequired Fields:
title(string, max 500 chars)author(string, max 200 chars)
Optional Fields:
year(number, 0 to current year + 10)isbn(string, valid ISBN-10 or ISBN-13 format)genre(string, max 100 chars)description(string, max 5000 chars)
Request Body:
{
"title": "Neuromancer",
"author": "William Gibson",
"year": 1984,
"genre": "Science Fiction",
"isbn": "9780441569595",
"description": "A groundbreaking cyberpunk novel"
}Response: Returns the created book with generated ID (HTTP 201).
GET /api/books/:idReturns a single book by ID.
PUT /api/books/:id
Content-Type: application/jsonAllowed Fields:
title,author,year,isbn,genre,description
Request Body:
{
"genre": "Classic Literature",
"description": "Updated description"
}Response: Returns the updated book.
DELETE /api/books/:idDeletes a book by ID. Returns HTTP 204 on success.
GET /api/books/search?q=querySearches across title, author, genre, ISBN, year, and description fields.
Query Parameters:
?q=(required): Search query, max 200 chars
Response:
{
"query": "fiction",
"results": [...],
"count": 8
}curl "https://api.dlsdemo.com/api/books?page=1&limit=5&genre=Literary%20Fiction"curl "https://api.dlsdemo.com/api/books/search?q=cyberpunk"curl -X POST "https://api.dlsdemo.com/api/books" \
-H "Content-Type: application/json" \
-d '{
"title": "Neuromancer",
"author": "William Gibson",
"year": 1984,
"genre": "Science Fiction",
"isbn": "9780441569595",
"description": "A groundbreaking cyberpunk novel about a washed-up computer hacker hired for one last job."
}'curl -X PUT "https://api.dlsdemo.com/api/books/1" \
-H "Content-Type: application/json" \
-d '{
"genre": "Classic Literature"
}'curl -X DELETE "https://api.dlsdemo.com/api/books/1"The API returns consistent error responses:
{
"error": "Error message description"
}HTTP Status Codes:
400- Bad Request (validation errors, invalid input)404- Not Found (book doesn't exist)413- Request Too Large (body exceeds 1MB)429- Rate Limit Exceeded500- Internal Server Error504- Gateway Timeout (database query timeout)
- Input Validation: All user inputs are validated for type, length, and format
- SQL Injection Prevention: Uses parameterized queries (D1 prepared statement methods) with proper field whitelisting
- Rate Limiting: Configurable per-IP rate limits via Workers Binding
- Request Size Limits: Maximum 1MB request body size
- Content-Type Validation: Enforces
application/jsonfor POST/PUT requests - Query Timeouts: 5-second timeout for database operations
- Structured Logging: Request IDs and comprehensive error logging for debugging
npm run devnpm run deploynpx wrangler tail-
Response Caching:
- GET single book: 5 minutes cache, 10 minutes stale-while-revalidate
- GET book list: 1 minute cache, 2 minutes stale-while-revalidate
- Search results: 1 minute cache, 2 minutes stale-while-revalidate
- Stats: 5 minutes cache, 10 minutes stale-while-revalidate
-
Database Indexing: Consider adding indexes on frequently queried fields (genre, year) for better performance
- Maximum request body size: 1MB
- Maximum field lengths enforced (see Create Book section)
- Search queries limited to 200 characters
- Rate limiting (if configured): 100 requests per minute per IP
This is a demonstration project showcasing Cloudflare Workers and D1 database capabilities with a minimal demonstration of security features and best practices. While it implements comprehensive security measures, additional considerations (authentication, authorization, advanced monitoring) should be evaluated based on your specific production requirements.
Educational and demonstration purposes only.