A modern, secure RESTful API for managing personal notes. Built with Express.js and SQLite, featuring JWT authentication, ownership-based authorization, full-text search, tag filtering, pagination, and interactive Swagger documentation.
- JWT Authentication — Secure register, login, and token-based access control with configurable expiration
- Full CRUD Operations — Create, read, update, and delete notes with a clean RESTful interface
- Ownership-Based Authorization — Users can only access and manage their own notes
- Full-Text Search — Search across note titles and content with the
?search=query parameter - Tag Filtering — Organize and filter notes by tags using
?tag= - Pagination — Efficient paginated responses with
?page=and?limit=parameters - Interactive API Docs — Auto-generated Swagger/OpenAPI documentation at
/api-docs - Security Hardened — Helmet security headers, bcrypt password hashing, input validation
- Lightweight Database — SQLite via better-sqlite3 for zero-config, file-based storage
Note: The API is hosted on Render's free tier. The first request may take a few seconds while the service spins up.
- Express.js 5 — Modern, fast web framework for Node.js
- better-sqlite3 — High-performance, synchronous SQLite3 driver
- jsonwebtoken — Industry-standard JWT authentication
- bcryptjs — Secure password hashing with salt rounds
- express-validator — Declarative request validation and sanitization
- swagger-jsdoc + swagger-ui-express — Auto-generated interactive API documentation
- helmet — Security middleware for HTTP headers
- cors — Cross-Origin Resource Sharing support
- dotenv — Environment variable management
- nodemon — Development auto-reload (dev dependency)
- Clone the repository:
git clone https://github.com/Serkanbyx/notes-api.git
cd notes-api- Install dependencies:
npm install- Create your environment file:
cp .env.example .env- Configure environment variables in
.env:
| Variable | Description | Default |
|---|---|---|
PORT |
Server port | 3000 |
JWT_SECRET |
Secret key for signing tokens | — |
JWT_EXPIRES_IN |
Token expiration time | 7d |
DB_PATH |
SQLite database file path | ./notes.db |
- Start the server:
# Development (with auto-reload)
npm run dev
# Production
npm start- Open Swagger docs in your browser:
http://localhost:3000/api-docs
- Register a new account via
POST /api/auth/register - Login with your credentials via
POST /api/auth/loginto receive a JWT token - Include the token in the
Authorization: Bearer <token>header for all subsequent requests - Create notes with titles, content, and tags via
POST /api/notes - Browse your notes with pagination via
GET /api/notes?page=1&limit=10 - Search notes by keyword via
GET /api/notes?search=meeting - Filter by tag via
GET /api/notes?tag=work - Update or delete notes via
PUT /api/notes/:idandDELETE /api/notes/:id
The API uses JSON Web Tokens (JWT) for stateless authentication:
Register → Password hashed with bcrypt → User stored in SQLite
Login → Credentials verified → JWT token issued (configurable expiry)
Request → Token validated via middleware → User identity extracted → Access granted
Every note is linked to a user via user_id. A dedicated ownership middleware verifies that the authenticated user owns the requested note before allowing any read, update, or delete operation.
-- Users table
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
email TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Notes table with foreign key
CREATE TABLE notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
content TEXT NOT NULL,
tags TEXT DEFAULT '[]',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);The project follows the MVC + Layered Architecture pattern:
- Routes — Define endpoints, validation rules, and Swagger docs
- Controllers — Handle business logic and request/response flow
- Models — Data access layer with SQL queries
- Middleware — Authentication, ownership checks, validation, and error handling
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/auth/register |
Register a new user |
POST |
/api/auth/login |
Login and receive JWT token |
GET |
/api/auth/profile |
Get current user profile |
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/notes |
Create a new note |
GET |
/api/notes |
Get all user's notes (paginated) |
GET |
/api/notes/tags |
Get all unique tags |
GET |
/api/notes/:id |
Get a specific note |
PUT |
/api/notes/:id |
Update a note |
DELETE |
/api/notes/:id |
Delete a note |
| Parameter | Example | Description |
|---|---|---|
search |
?search=meeting |
Search in title and content |
tag |
?tag=work |
Filter by tag |
page |
?page=2 |
Page number (default: 1) |
limit |
?limit=10 |
Items per page (default: 20, max: 100) |
Create a new file in src/middleware/ and integrate it into routes:
const customMiddleware = (req, res, next) => {
// Your custom logic here
next();
};
module.exports = customMiddleware;Replace better-sqlite3 with your preferred database driver in src/config/database.js and update the model queries in src/models/.
Add new fields to the notes table in src/config/database.js and update the corresponding model methods in src/models/Note.js.
├── src/
│ ├── app.js # Express app entry point
│ ├── config/
│ │ ├── database.js # SQLite connection & schema
│ │ └── swagger.js # Swagger/OpenAPI configuration
│ ├── controllers/
│ │ ├── authController.js # Auth business logic
│ │ └── noteController.js # Note CRUD business logic
│ ├── middleware/
│ │ ├── auth.js # JWT authentication
│ │ ├── errorHandler.js # Global error handler
│ │ ├── ownership.js # Note ownership verification
│ │ └── validate.js # Request validation
│ ├── models/
│ │ ├── Note.js # Note data access layer
│ │ └── User.js # User data access layer
│ └── routes/
│ ├── auth.js # Auth routes + Swagger docs
│ └── notes.js # Note routes + Swagger docs
├── .env.example
├── .gitignore
├── package.json
├── render.yaml
└── README.md
- Push this repository to GitHub
- Go to render.com and create a New Web Service
- Connect your GitHub repository
- Render will auto-detect settings from
render.yaml - Add
JWT_SECRETenvironment variable in Render dashboard - Deploy and access your API
Note: SQLite data on Render's free tier is ephemeral — it resets on each deploy. For persistent data in production, consider upgrading to a Render Disk or switching to PostgreSQL.
✅ User registration and login with JWT
✅ Secure password hashing with bcrypt
✅ Full CRUD for notes
✅ Ownership-based access control
✅ Full-text search across titles and content
✅ Tag-based filtering
✅ Pagination with configurable limits
✅ Interactive Swagger documentation
✅ Security headers via Helmet
✅ Input validation and sanitization
✅ Global error handling
✅ Render deployment configuration
- 🔮 Rate limiting for API endpoints
- 🔮 Note sharing between users
- 🔮 Note categories and folders
- 🔮 File/image attachments
- 🔮 Export notes as PDF/Markdown
- 🔮 PostgreSQL support for production
- Fork the repository
- Create your feature branch:
git checkout -b feat/amazing-feature - Commit your changes:
git commit -m "feat: add amazing feature" - Push to the branch:
git push origin feat/amazing-feature - Open a Pull Request
feat:— New featurefix:— Bug fixrefactor:— Code refactoringdocs:— Documentation changeschore:— Maintenance tasks
This project is licensed under the MIT License — see the LICENSE file for details.
Serkanby
- Website: serkanbayraktar.com
- GitHub: @Serkanbyx
- Email: serkanbyx1@gmail.com
- Open an Issue
- Email: serkanbyx1@gmail.com
- Website: serkanbayraktar.com