Backend API for AURA, a makeup e-commerce platform.
A production-ready REST API built with Node.js, Express, TypeScript, Prisma, PostgreSQL, and AWS S3.
This service provides authentication, product management, cart handling, order creation, and image uploads for the Aura store.
The API follows a modular architecture and uses presigned URLs to upload product images directly to cloud storage.
| Technology | Purpose |
|---|---|
| Node.js + Express | REST API server |
| TypeScript | Application logic and type safety |
| Zod | Request validation |
| PostgreSQL (Neon) | Cloud database |
| Prisma ORM | Database modeling and queries |
| Prisma Migrate | Database migrations |
| AWS S3 | Product image storage |
| Docker | Containerized development environment |
| Vitest | Unit and integration testing |
| Supertest | HTTP endpoint testing |
| Render | Production deployment |
The backend uses a layered structure to separate responsibilities:
Controller → Service → Repository
This structure keeps business logic isolated from HTTP handling and database access.
https://aura-backend-szzx.onrender.com/api/v1
POST /api/v1/auth/register
POST /api/v1/auth/login
GET /api/v1/auth/meGET /api/v1/products
GET /api/v1/products/:id
POST /api/v1/products
PATCH /api/v1/products/:id
DELETE /api/v1/products/:idGET /api/v1/cart
POST /api/v1/cart
PATCH /api/v1/cart/:productId
DELETE /api/v1/cart/:productIdGET /api/v1/orders
POST /api/v1/orders
GET /api/v1/orders/:idPOST /api/v1/uploads/presignGenerates a presigned URL for uploading product images directly to AWS S3.
Product images are uploaded using presigned URLs.
-
The client sends a request to the backend with:
productIdfilenamecontentType
-
The backend generates a presigned URL for AWS S3:
POST /api/v1/uploads/presignThe backend returns an object like:
{
"key": "products/clx123abc/cover-171987123-image.png",
"uploadUrl": "https://...",
"publicUrl": "https://..."
}The client uploads the file directly to S3 using the uploadUrl.
The returned publicUrl can be stored and used by the frontend to display the product image.
Example request
{
"productId": "clx123abc",
"filename": "lipstick.png",
"contentType": "image/png"
}Frontend upload example
const presignResponse = await fetch(`${API_URL}/uploads/presign`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
productId: "clx123abc",
filename: file.name,
contentType: file.type
})
});
const { uploadUrl, publicUrl } = await presignResponse.json();
await fetch(uploadUrl, {
method: "PUT",
headers: {
"Content-Type": file.type
},
body: file
});- avoids sending large files through the backend
- reduces server load
- improves scalability
- keeps uploads secure using temporary URLs
- JWT authentication
- Product management
- Shopping cart system
- Order creation
- Image upload using presigned URLs
- Modular architecture
- Automated API tests
- Request validation using Zod
- Restricted file upload types (
png,jpeg,webp) - Filename sanitization to prevent unsafe characters
- Presigned S3 URLs with short expiration time
- Environment variable validation at startup
The API uses a centralized error handling strategy through a custom AppError class.
Operational errors return structured responses with appropriate HTTP status codes.
Environment variables are managed through example files to keep secrets out of the repository.
.env.example
.env.docker.example
.env.test
The actual .env file is ignored by Git.
Environment variables are validated at startup through a centralized configuration file.
src/shared/config/env.ts
Required variables are checked before the server starts.
If any variable is missing, the application throws an error and stops.
Create your environment file:
cp .env.example .envWhen running the project with Docker:
cp .env.docker.example .env| Script | Description |
|---|---|
npm run dev |
Start development server with hot reload |
npm run build |
Compile TypeScript to JavaScript |
npm run start |
Start production server |
npm run test |
Run tests in watch mode |
npm run test:run |
Run all tests once |
npm run test:cov |
Run tests with coverage report |
npm run test:e2e |
Run end-to-end tests |
npm run db:test:migrate |
Run Prisma migrations for the test database |
npm run db:test:reset |
Reset the test database |
npm run db:test:studio |
Open Prisma Studio for the test database |
Typical development workflow:
npm install
npm run devBuild for production:
npm run build
npm run startRun tests:
npm run testRun migrations for the test database:
npm run db:test:migrateReset the test database:
npm run db:test:resetOpen Prisma Studio:
npm run db:test:studio