API REST de notas con autenticación por JWT, escrita en Node.js + Express puro, sin ningún framework de frontend alrededor. Es el hermano Node de los otros dos backends del portfolio (Java/Spring y Python/FastAPI), con un dominio distinto (notas con etiquetas) para que no sea el mismo proyecto reescrito tres veces.
CRUD de notas por usuario autenticado, con etiquetas y búsqueda:
| Método | Ruta | Descripción |
|---|---|---|
| POST | /api/auth/register |
Registro de usuario |
| POST | /api/auth/login |
Login, devuelve JWT |
| GET | /api/notes |
Lista paginada de notas del usuario, con filtros ?tag= y ?search= |
| POST | /api/notes |
Crear nota (title, body, tags[]) |
| GET | /api/notes/:id |
Ver una nota propia |
| PUT | /api/notes/:id |
Actualizar una nota propia (reemplaza sus etiquetas) |
| DELETE | /api/notes/:id |
Eliminar una nota propia |
Todas las rutas de /api/notes requieren Authorization: Bearer <token>.
Pedir una nota que no existe o que pertenece a otro usuario devuelve
404 en los dos casos — nunca 403 para la segunda, porque distinguirlas
confirmaría que el ID pertenece a otra persona.
src/
db/ → conexión SQLite (node:sqlite) y schema.sql
repositories/ → único lugar con SQL: users, notes + tags (tabla de unión)
services/ → reglas de negocio: validación, hashing, propiedad de recursos
controllers/ → traducen HTTP <-> llamadas a los servicios
routes/ → definición de endpoints
middleware/ → requireAuth (verifica JWT), errorHandler (centraliza errores)
app.js → ensambla todo con inyección manual de dependencias
server.js → punto de entrada real (lee .env, abre la DB, arranca Express)
app.js recibe la conexión a base de datos como parámetro en vez de
importar un singleton — eso es lo que permite que los tests levanten una
base de datos en memoria distinta por archivo de test, sin tocar ningún
fichero en disco.
git clone https://github.com/Nicolas-Silva-Cremona/notes-api-nodejs.git
cd notes-api-nodejs
npm install
cp .env.example .env
npm start # arranca en http://localhost:3000
npm run dev # igual, pero reinicia solo al guardar (node --watch)
npm test # 14 tests con Jest + SupertestPrueba rápida con curl:
curl -X POST localhost:3000/api/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"demo@example.com","password":"password123"}'
# usa el token devuelto:
curl -X POST localhost:3000/api/notes \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{"title":"Comprar leche","tags":["recados"]}'node:sqliteen vez debetter-sqlite3opg: es el módulo SQLite integrado en Node (estable desde Node 22). Para esta API no hace falta levantar un servidor de base de datos aparte ni compilar un binding nativo al hacernpm install— el repo se clona y funciona. El coste es fijar"engines": {"node": ">=22.5.0"}enpackage.json.- Etiquetas en tabla propia (
tags+note_tags), no una columna de texto separada por comas: permite filtrar por etiqueta con un JOIN indexado y evita el problema clásico de "¿case-sensitive? ¿espacios? ¿cómo busco una nota con dos etiquetas a la vez?" que arrastra el string separado por comas. - 404 en vez de 403 al acceder a una nota de otro usuario: devolver
403 confirmaría que el ID existe pero no es tuyo, filtrando información.
noteService.getOwnedtrata "no existe" y "no es tuya" exactamente igual. - Mismo mensaje de error para email inexistente y contraseña incorrecta en el login: distinguirlos permite a un atacante enumerar qué emails están registrados probando contraseñas al azar.
- Inyección de dependencias manual, sin contenedor:
app.jsconstruye a mano repository -> service -> controller -> route. Para el tamaño de este proyecto, un contenedor de DI sería una capa de indirección sin beneficio real; pasardbcomo parámetro ya es suficiente para poder testear con una base de datos en memoria.
MIT. Ver LICENSE.