A small REST API for user registration/login and personal notes management built with Express, TypeScript, and Prisma (Postgres).
Quick Summary
- Server entry:
server.ts - API base:
/api - Notes routes:
/api/notes(protected) - User routes:
/api/user - Database: PostgreSQL (Prisma)
Prerequisites
- Node.js (v16+ recommended)
- npm
- PostgreSQL database
Environment
Create a .env file at the project root with the following variables:
DATABASE_URL=postgresql://USER:PASSWORD@HOST:PORT/DATABASE
JWT_SECRET=your_jwt_secret_here
PORT=3000
Make sure DATABASE_URL points to a running Postgres instance.
Install
npm installPrisma setup / Migrations
If you haven't generated the client or applied migrations yet, run:
npx prisma generate
npx prisma migrate dev --name initNotes:
- The project includes a
prisma/schema.prisma. AdjustDATABASE_URLbefore running migrations. - If you already have migrations in
prisma/migrations,migrate devwill apply them.
Run (development)
npm run devThis runs nodemon + tsx (see package.json script dev) and listens by default on the port defined in PORT or 3000.
Project structure (relevant files)
server.ts- Express app entryroutes/notesRoutes.ts- Notes endpoints (protected by auth)routes/userRoutes.ts- User registration/login and profilecontrollers/*.ts- Route handlersmiddlewares/authHandler.ts- JWT auth middlewarelib/prisma.ts- Prisma client initializationprisma/schema.prisma- Prisma models (User, Note)
API Endpoints
All endpoints are under /api.
User
-
POST /api/user/register— Register a new user (public)- Body (application/json):
{ "name": "Alice", "email": "alice@example.com", "password": "secret" } - Response:
{ id, name, email, token }
- Body (application/json):
-
POST /api/user/login— Login (public)- Body:
{ "email": "alice@example.com", "password": "secret" } - Response:
{ id, name, email, token }
- Body:
-
GET /api/user/current— Get current user (private)- Header:
Authorization: Bearer <token> - Response: JWT payload attached by middleware (id, name, email)
- Header:
-
GET /api/user/profile— Get user with their notes (private)- Header:
Authorization: Bearer <token> - Response:
{ id, name, email, notes: [...] }
- Header:
Notes (all protected — require Authorization header)
-
GET /api/notes— Get all notes for the authenticated user- Header:
Authorization: Bearer <token> - Response: Array of note objects
- Header:
-
GET /api/notes/:id— Get a single note by id (only if owned by user)- Header:
Authorization: Bearer <token>
- Header:
-
POST /api/notes— Create a new note- Header:
Authorization: Bearer <token> - Body:
{ "title": "...", "content": "..." } - Response: Created note object
- Header:
-
PUT /api/notes/:id— Update note (only if owned by user)- Header:
Authorization: Bearer <token> - Body:
{ "title": "...", "content": "..." } - Response: Updated note object
- Header:
-
DELETE /api/notes/:id— Delete note (only if owned by user)- Header:
Authorization: Bearer <token> - Response:
{ message: "Note deleted successfully" }
- Header:
Authentication (JWT)
- The app issues a JWT on registration and login using
JWT_SECRETfrom the environment. - Protected routes expect the header:
Authorization: Bearer <token>
If the token is missing, invalid, or the user is not found, the middleware returns a 401 error.
Sample curl flows
- Register:
curl -X POST http://localhost:3000/api/user/register \
-H "Content-Type: application/json" \
-d '{"name":"Alice","email":"alice@example.com","password":"secret"}'- Login:
curl -X POST http://localhost:3000/api/user/login \
-H "Content-Type: application/json" \
-d '{"email":"alice@example.com","password":"secret"}'Save the returned token and use it in subsequent requests.
- Create a note:
curl -X POST http://localhost:3000/api/notes \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"title":"First note","content":"Hello world"}'- Get all notes:
curl -X GET http://localhost:3000/api/notes \
-H "Authorization: Bearer <token>"Error handling
- Errors are thrown in controllers and the
errorHandlermiddleware formats responses. Typical HTTP status codes used:400(bad request),401(unauthorized),403(forbidden),404(not found),500(server error).
Development notes & tips
- The app uses
prismawith the@prisma/adapter-pgadapter and@prisma/clientgenerated intogenerated/prisma. - If you update
prisma/schema.prisma, runnpx prisma generateand create/apply a migration withnpx prisma migrate dev. - Passwords are hashed using
bcryptjs. - Tokens expire in 1 hour (see
controllers/userControllers.ts).
Troubleshooting
- If you see errors connecting to the DB, verify
DATABASE_URLand that Postgres is reachable. - If Prisma client errors reference
generated/prisma, ensure you rannpx prisma generatesuccessfully.
Next steps / Improvements
- Add request validation (e.g.,
zodorJoi) for stronger input validation. - Add tests and CI pipeline.
- Add rate-limiting and stronger security headers for production.