The Bakery Website API is a RESTful API built with Express.js that provides endpoints for product management, shopping cart operations, order processing, and user management. All endpoints return JSON responses and require appropriate authentication.
Development: http://localhost:5000/api
Production: https://api.yourdomain.com/api
The API uses JWT token verification for authentication. The frontend uses Clerk to authenticate users and obtain JWT tokens. All protected endpoints require a valid Bearer token in the Authorization header.
Authorization: Bearer <jwt_token>In the React app, use Clerk's useAuth hook:
const { getToken } = useAuth();
const token = await getToken();
// Include in API requests
const response = await axios.get('/api/protected', {
headers: {
'Authorization': `Bearer ${token}`
}
});The FastAPI backend verifies JWT tokens using the JWKS endpoint:
from jose import jwt, JWTError
from fastapi import HTTPException, Depends
from fastapi.security import HTTPBearer
security = HTTPBearer()
async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)):
try:
payload = jwt.decode(
credentials.credentials,
key=JWKS_CLIENT, # Fetched from Clerk's JWKS endpoint
algorithms=["RS256"]
)
return payload
except JWTError:
raise HTTPException(status_code=401, detail="Invalid authentication token"){
"success": true,
"data": { ... },
"message": "Operation successful"
}{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human readable error message",
"details": { ... }
}
}{
"success": true,
"data": {
"items": [ ... ],
"pagination": {
"page": 1,
"limit": 10,
"total": 100,
"pages": 10
}
}
}Since authentication is handled entirely by Clerk on the frontend, the backend only needs to verify tokens and sync user data.
GET /api/users/meGet the current authenticated user's profile.
Response:
{
"success": true,
"data": {
"id": "cuid",
"clerkId": "user_xxx",
"email": "user@example.com",
"name": "John Doe",
"role": "CUSTOMER",
"createdAt": "2024-01-01T00:00:00Z"
}
}PUT /api/users/meUpdate the current user's profile information.
Body:
{
"name": "Jane Doe"
}GET /api/categoriesGet all product categories.
Response:
{
"success": true,
"data": [
{
"id": "cuid",
"name": "Cake",
"slug": "cake",
"description": "Delicious cakes for all occasions",
"displayOrder": 1,
"_count": {
"products": 15
}
}
]
}GET /api/categories/:slug/productsGet all products in a specific category.
Parameters:
slug(path): Category slug
Query Parameters:
page(optional): Page number (default: 1)limit(optional): Items per page (default: 12)sort(optional): Sort by price_asc, price_desc, name_asc, name_desc (default: name_asc)
GET /api/productsGet all products with optional filtering.
Query Parameters:
page(optional): Page number (default: 1)limit(optional): Items per page (default: 12)category(optional): Filter by category slugfeatured(optional): Filter featured products (true/false)search(optional): Search by name or descriptionsort(optional): Sort by price_asc, price_desc, name_asc, name_desc, created_desc
Response:
{
"success": true,
"data": {
"items": [
{
"id": "cuid",
"name": "Chocolate Cake",
"description": "Rich chocolate cake with ganache",
"price": 35.00,
"image": "https://example.com/chocolate-cake.jpg",
"category": {
"id": "cuid",
"name": "Cake",
"slug": "cake"
},
"featured": true,
"inStock": true,
"stockQuantity": 20
}
],
"pagination": {
"page": 1,
"limit": 12,
"total": 48,
"pages": 4
}
}
}GET /api/products/:idGet detailed information about a specific product.
Parameters:
id(path): Product ID
Response:
{
"success": true,
"data": {
"id": "cuid",
"name": "Chocolate Cake",
"description": "Rich chocolate cake with ganache",
"price": 35.00,
"image": "https://example.com/chocolate-cake.jpg",
"category": {
"id": "cuid",
"name": "Cake",
"slug": "cake"
},
"featured": true,
"inStock": true,
"stockQuantity": 20,
"createdAt": "2024-01-01T00:00:00Z",
"updatedAt": "2024-01-01T00:00:00Z"
}
}POST /api/productsCreate a new product. Requires admin role.
Body:
{
"name": "Red Velvet Cake",
"description": "Classic red velvet with cream cheese frosting",
"price": 40.00,
"categoryId": "cuid",
"featured": false,
"stockQuantity": 15
}Note: Image upload is handled separately via /api/upload endpoint.
PUT /api/products/:idUpdate an existing product. Requires admin role.
Parameters:
id(path): Product ID
Body: Same as create, all fields optional
DELETE /api/products/:idDelete a product. Requires admin role.
Parameters:
id(path): Product ID
GET /api/cartGet the current user's shopping cart.
Response:
{
"success": true,
"data": {
"id": "cuid",
"items": [
{
"id": "cuid",
"quantity": 2,
"product": {
"id": "cuid",
"name": "Chocolate Cake",
"price": 35.00,
"image": "https://example.com/chocolate-cake.jpg",
"inStock": true
}
}
],
"subtotal": 70.00,
"itemCount": 2
}
}POST /api/cart/itemsAdd a product to the cart.
Body:
{
"productId": "cuid",
"quantity": 1
}PUT /api/cart/items/:idUpdate quantity of a cart item.
Parameters:
id(path): Cart item ID
Body:
{
"quantity": 3
}DELETE /api/cart/items/:idRemove an item from the cart.
Parameters:
id(path): Cart item ID
DELETE /api/cartRemove all items from the cart.
POST /api/ordersCreate a new order from the current cart.
Body:
{
"customerName": "John Doe",
"customerEmail": "john@example.com",
"customerPhone": "+1234567890",
"deliveryMethod": "PICKUP",
"deliveryAddress": "123 Main St, City, State 12345",
"deliveryDate": "2024-01-15T10:00:00Z",
"notes": "Please call when ready"
}Response:
{
"success": true,
"data": {
"id": "cuid",
"orderNumber": "ORD-20240101-001",
"status": "PENDING",
"total": 75.00,
"createdAt": "2024-01-01T00:00:00Z"
}
}GET /api/ordersGet all orders for the current user (or all orders for admin).
Query Parameters:
page(optional): Page number (default: 1)limit(optional): Items per page (default: 10)status(optional): Filter by status
Response:
{
"success": true,
"data": {
"items": [
{
"id": "cuid",
"orderNumber": "ORD-20240101-001",
"status": "COMPLETED",
"total": 75.00,
"itemCount": 2,
"createdAt": "2024-01-01T00:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 25,
"pages": 3
}
}
}GET /api/orders/:idGet detailed information about a specific order.
Parameters:
id(path): Order ID
Response:
{
"success": true,
"data": {
"id": "cuid",
"orderNumber": "ORD-20240101-001",
"status": "COMPLETED",
"items": [
{
"id": "cuid",
"quantity": 2,
"unitPrice": 35.00,
"totalPrice": 70.00,
"product": {
"id": "cuid",
"name": "Chocolate Cake",
"image": "https://example.com/chocolate-cake.jpg"
}
}
],
"subtotal": 70.00,
"tax": 5.00,
"total": 75.00,
"customerName": "John Doe",
"customerEmail": "john@example.com",
"customerPhone": "+1234567890",
"deliveryMethod": "PICKUP",
"deliveryDate": "2024-01-15T10:00:00Z",
"createdAt": "2024-01-01T00:00:00Z"
}
}PUT /api/orders/:id/statusUpdate the status of an order. Requires admin role.
Parameters:
id(path): Order ID
Body:
{
"status": "PROCESSING"
}Valid Status Values:
PENDINGPROCESSINGREADYCOMPLETEDCANCELLED
POST /api/upload/productUpload a product image using Multer.
Headers:
Content-Type: multipart/form-data
Authorization: Bearer <token>
Body:
image: Image file (JPEG, PNG, WebP, max 5MB)
Response:
{
"success": true,
"data": {
"url": "/uploads/products/1704067200000-chocolate-cake.jpg",
"filename": "1704067200000-chocolate-cake.jpg",
"originalName": "chocolate-cake.jpg",
"mimetype": "image/jpeg",
"size": 102400
}
}POST /api/upload/productsUpload multiple product images (max 5 files).
Headers:
Content-Type: multipart/form-data
Authorization: Bearer <token>
Body:
images: Array of image files
Response:
{
"success": true,
"data": {
"files": [
{
"url": "/uploads/products/1704067200000-image1.jpg",
"filename": "1704067200000-image1.jpg",
"size": 102400
},
{
"url": "/uploads/products/1704067200001-image2.jpg",
"filename": "1704067200001-image2.jpg",
"size": 98304
}
]
}
}File Upload Configuration:
- Maximum file size: 5MB per file
- Accepted formats: JPEG, PNG, WebP
- Storage location:
./uploads/products(development) - Files served from:
http://localhost:5000/uploads/products/
GET /api/admin/dashboardGet dashboard statistics. Requires admin role.
Response:
{
"success": true,
"data": {
"totalOrders": 150,
"pendingOrders": 5,
"totalRevenue": 12500.00,
"totalProducts": 48,
"totalUsers": 230,
"recentOrders": [ ... ],
"popularProducts": [ ... ]
}
}| Code | Description |
|---|---|
UNAUTHORIZED |
Missing or invalid authentication token |
FORBIDDEN |
Insufficient permissions |
NOT_FOUND |
Resource not found |
VALIDATION_ERROR |
Invalid input data |
DUPLICATE_ENTRY |
Resource already exists |
OUT_OF_STOCK |
Product is out of stock |
CART_EMPTY |
Cart is empty |
INTERNAL_ERROR |
Server error |
API endpoints are rate-limited to prevent abuse:
- Public endpoints: 100 requests per minute
- Authenticated endpoints: 300 requests per minute
- Admin endpoints: 500 requests per minute
Rate limit headers:
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 299
X-RateLimit-Reset: 1609459200
POST https://your-webhook-url.com/order-statusTriggered when an order status changes.
Payload:
{
"event": "order.status.updated",
"timestamp": "2024-01-01T00:00:00Z",
"data": {
"orderId": "cuid",
"orderNumber": "ORD-20240101-001",
"oldStatus": "PENDING",
"newStatus": "PROCESSING",
"userId": "cuid"
}
}Get products:
curl -X GET http://localhost:5000/api/productsAdd to cart (with auth):
curl -X POST http://localhost:5000/api/cart/items \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"productId":"cuid","quantity":1}'Import the Postman collection from /docs/postman-collection.json for a complete set of API requests with examples.
import { BakeryAPI } from '@bakery/sdk';
const api = new BakeryAPI({
baseURL: 'http://localhost:5000/api',
getToken: async () => await clerk.session?.getToken()
});
// Get products
const products = await api.products.list({ featured: true });
// Add to cart
await api.cart.addItem({ productId: 'cuid', quantity: 2 });