A RESTful API built with Express.js, Prisma ORM, and PostgreSQL for managing movies and user watchlists. Features JWT authentication and Zod validation for request validation.
- Runtime: Node.js
- Framework: Express.js 5.2.1
- Database: PostgreSQL (via Neon)
- ORM: Prisma 6.19.0
- Authentication: JWT (jsonwebtoken)
- Validation: Zod
- Password Hashing: bcryptjs
- Environment Variables: dotenv / Doppler
- Secrets Management: Doppler
- Package Manager: pnpm
- Node.js (v18 or higher)
- pnpm (or npm/yarn)
- PostgreSQL database (Neon, local, or cloud)
- Git
- Doppler CLI (optional, for secrets management)
-
Clone the repository
git clone <repository-url> cd backend-express
-
Install dependencies
pnpm install
-
Set up environment variables
Option A: Using Doppler (Recommended)
Install Doppler CLI:
# Linux/macOS curl -L --tlsv1.2 --proto "=https" -sSf https://cli.doppler.com/install.sh | sh # Or using Homebrew (macOS) brew install doppler # Or using npm/pnpm pnpm add -g doppler-cli
Authenticate with Doppler:
doppler login
Link your project to a Doppler config:
doppler setup
This will prompt you to select:
- Your Doppler project
- The config (e.g.,
dev,staging,prod)
Option B: Using .env file (Local Development)
Create a
.envfile in the root directory:DATABASE_URL="postgresql://user:password@host:port/database" JWT_SECRET="your-secret-key-here" JWT_EXPIRES_IN="7d" PORT=5001 NODE_ENV="development"
-
Generate Prisma Client
npx prisma generate
-
Run database migrations
npx prisma migrate dev
-
Seed the database (optional)
pnpm run seed:movies
| Variable | Description | Required | Default |
|---|---|---|---|
DATABASE_URL |
PostgreSQL connection string | Yes | - |
JWT_SECRET |
Secret key for JWT tokens | Yes | - |
JWT_EXPIRES_IN |
JWT token expiration time | No | 7d |
PORT |
Server port | No | 5001 (dev) / 3000 (production) |
NODE_ENV |
Environment mode | No | development/production |
This project supports Doppler for secure secrets management. Doppler allows you to:
- Store secrets securely in the cloud
- Manage different configs (dev, staging, prod) per environment
- Sync secrets across team members
- Rotate secrets without code changes
- Audit secret access
Setting up Doppler:
-
Create a Doppler account at doppler.com
-
Create a project in the Doppler dashboard
-
Add your secrets to Doppler:
- Go to your project β Config (e.g.,
dev) - Add all required environment variables:
DATABASE_URLJWT_SECRETJWT_EXPIRES_INPORTNODE_ENV
- Go to your project β Config (e.g.,
-
Authenticate with Doppler:
doppler login
-
Verify secrets are loaded:
doppler secrets -p backend-express -c dev
Using Doppler in different environments:
The project is configured to use the backend-express Doppler project. The scripts automatically use the correct config:
# Development (uses dev config)
pnpm run dev
# Equivalent to: doppler run -p backend-express -c dev -- nodemon src/server.js
# Production (uses prod config)
pnpm run start
# Equivalent to: doppler run -p backend-express -c prod -- node src/server.js
# For other configs, run directly:
doppler run -p backend-express -c staging -- node src/server.jsNote: The default scripts (pnpm run dev, pnpm run start) use Doppler with the backend-express project. Use dev:local or start:local scripts if you prefer using .env files.
- Create a PostgreSQL database (or use Neon)
- Add
DATABASE_URLto your Doppler config or.envfile - Run migrations:
npx prisma migrate dev
With Doppler (Recommended):
pnpm run devThis uses Doppler to inject environment variables and starts the server with nodemon for auto-reloading.
With .env file (Local):
pnpm run dev:localWith Doppler:
pnpm run startWith .env file:
pnpm run start:localThe server will start on http://localhost:5001 (or your configured PORT).
Note: Make sure you have either:
- Doppler configured (
doppler setup) and authenticated (doppler login), OR - A
.envfile in the root directory with all required variables
The application can be deployed to any platform that supports Node.js applications (e.g., Heroku, Railway, Render, AWS, DigitalOcean, etc.).
Doppler provides seamless integration with most deployment platforms:
-
Install Doppler CLI in your deployment environment
-
Authenticate using a service token:
doppler configure set token <service-token>
-
Update your start command to use Doppler with the project and config:
doppler run -p backend-express -c prod -- node src/server.js
Or use the npm script:
pnpm run start
Platform-specific guides:
Note: Make sure to set all required environment variables when deploying. The app listens on 0.0.0.0 in production to allow external connections.
For platforms that don't support Doppler, set environment variables directly in your platform's dashboard or configuration.
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| POST | /auth/register |
Register a new user | No |
| POST | /auth/login |
Login user | No |
| POST | /auth/logout |
Logout user | No |
Register Request Body:
{
"name": "John Doe",
"email": "john@example.com",
"password": "password123"
}Login Request Body:
{
"email": "john@example.com",
"password": "password123"
}| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| GET | /movies |
Get all movies | No |
| GET | /movies/:id |
Get movie by ID | No |
| POST | /movies |
Create a new movie | Yes |
| PUT | /movies/:id |
Update a movie (creator only) | Yes |
| DELETE | /movies/:id |
Delete a movie (creator only) | Yes |
Create Movie Request Body:
{
"title": "The Matrix",
"overview": "A computer hacker learns about the true nature of reality",
"releaseYear": 1999,
"genres": ["Action", "Sci-Fi"],
"runtime": 136,
"posterUrl": "https://example.com/poster.jpg"
}Update Movie Request Body:
{
"title": "The Matrix Reloaded",
"overview": "Neo continues his journey",
"releaseYear": 2003,
"genres": ["Action", "Sci-Fi"],
"runtime": 138,
"posterUrl": "https://example.com/poster2.jpg"
}Delete Movie Response:
{
"status": "success",
"message": "Movie deleted successfully"
}Note: All fields in the update request are optional. Only the creator of a movie can update or delete it.
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| POST | /watchlist |
Add movie to watchlist | Yes |
| PUT | /watchlist/:id |
Update watchlist item | Yes |
| DELETE | /watchlist/:id |
Remove movie from watchlist | Yes |
Add to Watchlist Request Body:
{
"movieId": "uuid",
"status": "PLANNED",
"rating": 5,
"notes": "Looking forward to watching this"
}Update Watchlist Request Body:
{
"status": "COMPLETED",
"rating": 8,
"notes": "Great movie!"
}Watchlist Status Values:
PLANNEDWATCHINGCOMPLETEDDROPPED
| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
Server health check |
backend-express/
βββ prisma/
β βββ schema.prisma # Database schema
β βββ migrations/ # Database migrations
β βββ seed.js # Database seed file
βββ src/
β βββ config/
β β βββ db.js # Database connection
β βββ controllers/
β β βββ authController.js
β β βββ watchlistController.js
β βββ middleware/
β β βββ authMiddleware.js
β β βββ validateRequestMiddleware.js
β βββ routes/
β β βββ authRoutes.js
β β βββ movieRoutes.js
β β βββ watchlistRoutes.js
β βββ utils/
β β βββ generateToken.js
β βββ validators/
β β βββ authValidator.js
β β βββ watchlistValidator.js
β βββ server.js # Application entry point
βββ .env # Environment variables (not in git)
βββ .gitignore
βββ package.json
βββ README.md
| Script | Description |
|---|---|
pnpm run dev |
Start development server with nodemon (uses Doppler) |
pnpm run dev:local |
Start development server with nodemon (uses .env file) |
pnpm run start |
Start production server (uses Doppler) |
pnpm run start:local |
Start production server (uses .env file) |
pnpm run seed:movies |
Seed database with movie data (uses Doppler) |
pnpm run seed:movies:local |
Seed database with movie data (uses .env file) |
npx prisma generate |
Generate Prisma Client |
npx prisma migrate dev |
Run database migrations |
npx prisma studio |
Open Prisma Studio (database GUI) |
The API uses JWT (JSON Web Tokens) for authentication.
- Register/Login to get a token
- Include the token in the
Authorizationheader:Authorization: Bearer <your-jwt-token> - Protected routes require valid authentication
The API uses Zod for request validation. All incoming request bodies are validated against predefined schemas before processing.
The validateRequestMiddleware validates request bodies using Zod schemas:
router.post('/', authMiddleware, validateRequestMiddleware(schema), controller);- authValidator.js - Validates registration and login requests
- watchlistValidator.js - Validates watchlist operations
addToWatchlistSchema- Validates adding movies to watchlistupdateWatchlistItemSchema- Validates updating watchlist items
- Type Safety - Ensures correct data types (UUID, string, number, enum)
- Custom Error Messages - Provides clear validation error messages
- Automatic Coercion - Converts compatible types (e.g., string to number)
- Field Constraints - Enforces min/max values, string lengths, and enum values
Invalid requests return a 400 Bad Request with detailed error messages.
-
User - User accounts with authentication
id(UUID)name(String)email(String, unique)password(String, hashed)createdAt(DateTime)
-
Movie - Movie information
id(UUID)title(String)overview(String, optional)releaseYear(Int)genres(String array)runtime(Int, optional)posterUrl(String, optional)createdBy(String, foreign key to User)createdAt(DateTime)
-
WatchlistItem - User's watchlist entries
id(UUID)userId(String, foreign key to User)movieId(String, foreign key to Movie)status(WatchlistStatus enum)rating(Int, optional)notes(String, optional)createdAt(DateTime)updatedAt(DateTime)
See prisma/schema.prisma for full schema details.
# Generate Prisma Client
npx prisma generate
# Create a new migration
npx prisma migrate dev --name migration_name
# Reset database (WARNING: deletes all data)
npx prisma migrate reset
# View database in browser
npx prisma studioThe API uses try/catch blocks for error handling and returns appropriate HTTP status codes:
200- Success201- Created400- Bad Request (validation errors, invalid input, missing required fields)401- Unauthorized (invalid or missing authentication)403- Forbidden (insufficient permissions, e.g., not the creator)404- Not Found (resource doesn't exist)409- Conflict (resource cannot be deleted due to foreign key constraints)500- Internal Server Error (server/database errors)
Validation Errors:
When request validation fails (Zod), the API returns a 400 Bad Request with a message containing all validation errors.
Contributions, issues, and feature requests are welcome!