Skip to content

Latest commit

 

History

History
231 lines (164 loc) · 6.63 KB

File metadata and controls

231 lines (164 loc) · 6.63 KB

JTech Marketplace — API Reference (Planned)

Status: planned contract. The current build is a frontend-only prototype — there is no live backend. All data lives in the browser (IndexedDB, see src/app/core/db.service.ts). This document specifies the REST API a future backend would implement so the Angular app can be pointed at it with minimal change. Field names match the TypeScript models in src/app/core/models.ts, which in turn mirror the Cleveland-Marketplace Supabase schema.

Base URL (proposed): https://api.jtechmarketplace.org/v1


Conventions

  • All requests and responses are application/json.
  • Timestamps are ISO-8601 UTC strings.
  • IDs are opaque strings.
  • Money (price) is a number in USD; 0 means a free / gemach item.
  • Collection endpoints return { "data": [...], "total": <int> }.
  • Mutations return the affected resource.

Authentication

A future backend would issue a bearer token on login. Send it on every authenticated request:

Authorization: Bearer <token>

In the prototype, "auth" is a dummy: AuthService just selects a profile and stores its id in localStorage. No token, no password check.

Error format

{ "error": { "code": "not_found", "message": "Product not found" } }
Status Meaning
200 OK
201 Created
400 Validation error
401 Missing / invalid token
403 Authenticated but not allowed (not owner / not admin)
404 Resource not found
409 Conflict (e.g. username taken)

Auth

POST /auth/signup

Create an account.

Request:

{ "username": "rivka_g", "fullName": "Rivka Greenbaum", "email": "rivka@example.com", "password": "•••••" }

Response 201: { "token": "...", "user": <Profile> } Errors: 409 username taken, 400 username < 3 chars.

POST /auth/login

Request: { "identifier": "rivka_g", "password": "•••••" }identifier is a username or email. Response 200: { "token": "...", "user": <Profile> }

GET /auth/me

Returns the current Profile. Requires auth. 401 if no valid token.

POST /auth/logout

Invalidates the current token. Response 204.


Products (listings)

GET /products

List / search listings. Query parameters:

Param Type Description
q string Full-text search over name + description
category string Category slug (judaica, seforim, kosher-tech, …)
condition string New · Like New · Good · Fair · For Parts
maxPrice number Upper price bound
status string Defaults to excluding hidden
sellerId string Listings by one seller
sort string recent (default) · price-asc · price-desc
page, pageSize number Pagination

Response 200: { "data": [<Product>], "total": 42 }

GET /products/:id

Returns one Product. 404 if missing.

POST /products

Create a listing. Requires auth — sellerId is taken from the token.

Request:

{
  "name": "Sterling Silver Menorah",
  "description": "Classic oil menorah…",
  "price": 240,
  "imageUrls": ["https://…/1.jpg"],
  "category": "judaica",
  "condition": "Like New",
  "location": "Lakewood"
}

Response 201: the created Product (status defaults to available).

PATCH /products/:id

Update a listing. Requires auth; must be the owner or an admin (403 otherwise). Body: any subset of the product fields, e.g. { "status": "sold" }. Response 200: updated Product.

DELETE /products/:id

Delete a listing. Owner or admin only. Response 204.


Favorites

GET /favorites

The current user's favorited products. Requires auth. Response 200: { "data": [<Product>] }

POST /favorites

Body: { "productId": "p-001" }. Response 201: <Favorite>.

DELETE /favorites/:productId

Remove a favorite. Response 204.


Comments

GET /products/:id/comments

Public. Response 200: { "data": [<Comment>] }.

POST /products/:id/comments

Requires auth. Body: { "content": "Is this still available?" }. Response 201: <Comment>.

DELETE /comments/:id

Author or admin only. Response 204.


Conversations & messages

GET /conversations

The current user's conversations, newest activity first. Requires auth. Response 200: { "data": [<Conversation & { lastMessage, unread }>] }

POST /conversations

Get-or-create a conversation with another user. Body: { "productId": "p-005" | null, "withUserId": "u-shloimy" } Response 200/201: <Conversation>.

GET /conversations/:id/messages

Messages in a conversation, oldest first. Participant only (403 otherwise).

POST /conversations/:id/messages

Body: { "content": "Sounds good — Sunday works." } Response 201: <Message>.

POST /conversations/:id/read

Marks the other party's messages as read. Response 204.


Users

GET /users/:username

Public seller profile + their non-hidden listings. Response 200: { "user": <Profile>, "listings": [<Product>] }

PATCH /users/me

Update your own profile. Requires auth. Body: subset of { fullName, bio, phone, neighborhood, avatarUrl }. Response 200: updated Profile.


Admin

All admin endpoints require a token whose Profile.role === "admin" (403 otherwise).

GET /admin/products

All listings including hidden, with seller info. Supports the same query params as GET /products.

GET /admin/users

All user profiles. Response 200: { "data": [<Profile>], "total": <int> }.

PATCH /admin/products/:id

Moderate a listing — typically { "status": "hidden" } or { "status": "available" }.


Resource shapes

Profile      { id, username, fullName, avatarUrl, bio, phone, email,
               role: 'user' | 'admin', neighborhood, createdAt }

Product      { id, sellerId, name, description, price, imageUrls: string[],
               category, condition, status: 'available'|'pending'|'sold'|'hidden',
               location, createdAt, updatedAt }

Favorite     { userId, productId, createdAt }

Comment      { id, productId, userId, content, createdAt }

Conversation { id, productId: string|null, participantIds: string[], createdAt }

Message      { id, conversationId, senderId, content, isRead,
               parentMessageId: string|null, createdAt }

CartItem     { productId, qty, addedAt }   // client-side only in the prototype

Not in scope for the prototype

Payments, shipping/tracking, real authentication & sessions, email notifications, and image uploads are intentionally not implemented — the UI shows them as dummy switches marked "Coming soon".