This project is an Airbnb clone that aims to replicate some of the key features of the popular online accommodation marketplace. The focus of this clone is to provide two main CRUD (Create, Read, Update, Delete) functionalities for listings and reviews.
The project allows users to create, read, update, and delete listings. Users can add details about their properties, such as the location, description, price, type of place, and how much of the place is available. They can also update and delete their listings as needed. The listing data is stored securely in a database and can be easily accessed and managed by users.
The project also includes a reviews functionality that enables users to leave feedback on listings. Users can read existing reviews, add new reviews, update their own reviews, and delete their reviews if necessary. This feature enhances the user experience by providing valuable insights and recommendations for potential guests.
To develop this Airbnb clone, the following technologies and tools were utilized:
- Backend: Express
- Frontend: Create-React-App, Redux
- Database: Sequelize, Sqlite3 (locally), PostgreSQL (live)
The primary goals of this project are as follows:
- To create a user-friendly and intuitive interface for managing property listings.
- To implement a robust reviews system that allows users to share their experiences.
- To ensure data security and privacy by employing best practices in authentication and authorization.
- To deliver a functional and reliable Airbnb clone that provides value to its users.
Feel free to explore the project further and provide feedback on any additional features or improvements you'd like to see.
Happy exploring!
To run the Airbnb clone project on your local machine, please follow the steps below:
-
Clone the repository:
git clone [repository-url]
-
Install Dependencies:
- Navigate to the backend directory:
cd backend npm install
- Navigate to the frontend directory
cd ../frontend npm install
-
Create a .env file in the backend directory with the following content:
PORT=8000 DB_FILE=db/dev.db JWT_SECRET=<generate-secret> JWT_EXPIRES_IN=604800
-
Run database migrations:
- In the backend directory, execute the follow command:
npx dotenv sequelize db:migrate
-
Seed the database (optional):
npx dotenv sequelize db:seed:all
-
Start the backend and frontend servers:
- In the backend directory run:
npm start
- In the frontend directory run:
npm start
- Access the application:
- Open your web browser and visit http://localhost:3000 to access the YCSHIYP.
All endpoints that require a current user to be logged in.
-
Request: endpoints that require authentication
-
Error Response: Require authentication
-
Status Code: 401
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Authentication required" }
-
All endpoints that require authentication and the current user does not have the correct role(s) or permission(s).
-
Request: endpoints that require proper authorization
-
Error Response: Require proper authorization
-
Status Code: 403
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Forbidden" }
-
Returns the information about the current user that is logged in.
-
Require Authentication: true
-
Request
- Method: GET
- URL: /api/session
- Body: none
-
Successful Response when there is a logged in user
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "user": { "id": 1, "firstName": "John", "lastName": "Smith", "email": "john.smith@gmail.com", "username": "JohnSmith" } }
-
-
Successful Response when there is no logged in user
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "user": null }
-
Logs in a current user with valid credentials and returns the current user's information.
-
Require Authentication: false
-
Request
-
Method: POST
-
URL: /api/session
-
Headers:
- Content-Type: application/json
-
Body:
{ "credential": "john.smith@gmail.com", "password": "secret password" }
-
-
Successful Response
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "user": { "id": 1, "firstName": "John", "lastName": "Smith", "email": "john.smith@gmail.com", "username": "JohnSmith" } }
-
-
Error Response: Invalid credentials
-
Status Code: 401
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Invalid credentials" }
-
-
Error response: Body validation errors
-
Status Code: 400
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Bad Request", // (or "Validation error" if generated by Sequelize), "errors": { "credential": "Email or username is required", "password": "Password is required" } }
-
Creates a new user, logs them in as the current user, and returns the current user's information.
-
Require Authentication: false
-
Request
-
Method: POST
-
URL: /api/users
-
Headers:
- Content-Type: application/json
-
Body:
{ "firstName": "John", "lastName": "Smith", "email": "john.smith@gmail.com", "username": "JohnSmith", "password": "secret password" }
-
-
Successful Response
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "user": { "id": 1, "firstName": "John", "lastName": "Smith", "email": "john.smith@gmail.com", "username": "JohnSmith" } }
-
-
Error response: User already exists with the specified email
-
Status Code: 500
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "User already exists", "errors": { "email": "User with that email already exists" } }
-
-
Error response: User already exists with the specified username
-
Status Code: 500
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "User already exists", "errors": { "username": "User with that username already exists" } }
-
-
Error response: Body validation errors
-
Status Code: 400
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Bad Request", // (or "Validation error" if generated by Sequelize), "errors": { "email": "Invalid email", "username": "Username is required", "firstName": "First Name is required", "lastName": "Last Name is required" } }
-
Returns all the spots.
-
Require Authentication: false
-
Request
- Method: GET
- URL: /api/spots
- Body: none
-
Successful Response
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "Spots": [ { "id": 1, "ownerId": 1, "address": "123 Disney Lane", "city": "San Francisco", "state": "California", "country": "United States of America", "lat": 37.7645358, "lng": -122.4730327, "name": "App Academy", "description": "Place where web developers are created", "price": 123, "createdAt": "2021-11-19 20:39:36", "updatedAt": "2021-11-19 20:39:36", "avgRating": 4.5, "previewImage": "image url" } ] }
-
Returns all the spots owned (created) by the current user.
-
Require Authentication: true
-
Request
- Method: GET
- URL: /api/spots/current
- Body: none
-
Successful Response
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "Spots": [ { "id": 1, "ownerId": 1, "address": "123 Disney Lane", "city": "San Francisco", "state": "California", "country": "United States of America", "lat": 37.7645358, "lng": -122.4730327, "name": "App Academy", "description": "Place where web developers are created", "price": 123, "createdAt": "2021-11-19 20:39:36", "updatedAt": "2021-11-19 20:39:36", "avgRating": 4.5, "previewImage": "image url" } ] }
-
Returns the details of a spot specified by its id.
-
Require Authentication: false
-
Request
- Method: GET
- URL: /api/spots/:spotId
- Body: none
-
Successful Response
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "id": 1, "ownerId": 1, "address": "123 Disney Lane", "city": "San Francisco", "state": "California", "country": "United States of America", "lat": 37.7645358, "lng": -122.4730327, "name": "App Academy", "description": "Place where web developers are created", "price": 123, "createdAt": "2021-11-19 20:39:36", "updatedAt": "2021-11-19 20:39:36", "numReviews": 5, "avgStarRating": 4.5, "SpotImages": [ { "id": 1, "url": "image url", "preview": true }, { "id": 2, "url": "image url", "preview": false } ], "Owner": { "id": 1, "firstName": "John", "lastName": "Smith" } }
-
-
Error response: Couldn't find a Spot with the specified id
-
Status Code: 404
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Spot couldn't be found" }
-
Creates and returns a new spot.
-
Require Authentication: true
-
Request
-
Method: POST
-
URL: /api/spots
-
Headers:
- Content-Type: application/json
-
Body:
{ "address": "123 Disney Lane", "city": "San Francisco", "state": "California", "country": "United States of America", "lat": 37.7645358, "lng": -122.4730327, "name": "App Academy", "description": "Place where web developers are created", "price": 123 }
-
-
Successful Response
-
Status Code: 201
-
Headers:
- Content-Type: application/json
-
Body:
{ "id": 1, "ownerId": 1, "address": "123 Disney Lane", "city": "San Francisco", "state": "California", "country": "United States of America", "lat": 37.7645358, "lng": -122.4730327, "name": "App Academy", "description": "Place where web developers are created", "price": 123, "createdAt": "2021-11-19 20:39:36", "updatedAt": "2021-11-19 20:39:36" }
-
-
Error Response: Body validation error
-
Status Code: 400
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Bad Request", // (or "Validation error" if generated by Sequelize), "errors": { "address": "Street address is required", "city": "City is required", "state": "State is required", "country": "Country is required", "lat": "Latitude is not valid", "lng": "Longitude is not valid", "name": "Name must be less than 50 characters", "description": "Description is required", "price": "Price per day is required" } }
-
Create and return a new image for a spot specified by id.
-
Require Authentication: true
-
Require proper authorization: Spot must belong to the current user
-
Request
-
Method: POST
-
URL: /api/spots/:spotId/images
-
Headers:
- Content-Type: application/json
-
Body:
{ "url": "image url", "preview": true }
-
-
Successful Response
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "id": 1, "url": "image url", "preview": true }
-
-
Error response: Couldn't find a Spot with the specified id
-
Status Code: 404
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Spot couldn't be found" }
-
Updates and returns an existing spot.
-
Require Authentication: true
-
Require proper authorization: Spot must belong to the current user
-
Request
-
Method: PUT
-
URL: /api/spots/:spotId
-
Headers:
- Content-Type: application/json
-
Body:
{ "address": "123 Disney Lane", "city": "San Francisco", "state": "California", "country": "United States of America", "lat": 37.7645358, "lng": -122.4730327, "name": "App Academy", "description": "Place where web developers are created", "price": 123 }
-
-
Successful Response
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "id": 1, "ownerId": 1, "address": "123 Disney Lane", "city": "San Francisco", "state": "California", "country": "United States of America", "lat": 37.7645358, "lng": -122.4730327, "name": "App Academy", "description": "Place where web developers are created", "price": 123, "createdAt": "2021-11-19 20:39:36", "updatedAt": "2021-11-20 10:06:40" }
-
-
Error Response: Body validation error
-
Status Code: 400
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Bad Request", // (or "Validation error" if generated by Sequelize), "errors": { "address": "Street address is required", "city": "City is required", "state": "State is required", "country": "Country is required", "lat": "Latitude is not valid", "lng": "Longitude is not valid", "name": "Name must be less than 50 characters", "description": "Description is required", "price": "Price per day is required" } }
-
-
Error response: Couldn't find a Spot with the specified id
-
Status Code: 404
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Spot couldn't be found" }
-
Deletes an existing spot.
-
Require Authentication: true
-
Require proper authorization: Spot must belong to the current user
-
Request
- Method: DELETE
- URL: /api/spots/:spotId
- Body: none
-
Successful Response
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Successfully deleted" }
-
-
Error response: Couldn't find a Spot with the specified id
-
Status Code: 404
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Spot couldn't be found" }
-
Returns all the reviews written by the current user.
-
Require Authentication: true
-
Request
- Method: GET
- URL: /api/reviews/current
- Body: none
-
Successful Response
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "Reviews": [ { "id": 1, "userId": 1, "spotId": 1, "review": "This was an awesome spot!", "stars": 5, "createdAt": "2021-11-19 20:39:36", "updatedAt": "2021-11-19 20:39:36", "User": { "id": 1, "firstName": "John", "lastName": "Smith" }, "Spot": { "id": 1, "ownerId": 1, "address": "123 Disney Lane", "city": "San Francisco", "state": "California", "country": "United States of America", "lat": 37.7645358, "lng": -122.4730327, "name": "App Academy", "price": 123, "previewImage": "image url" }, "ReviewImages": [ { "id": 1, "url": "image url" } ] } ] }
-
Returns all the reviews that belong to a spot specified by id.
-
Require Authentication: false
-
Request
- Method: GET
- URL: /api/spots/:spotId/reviews
- Body: none
-
Successful Response
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "Reviews": [ { "id": 1, "userId": 1, "spotId": 1, "review": "This was an awesome spot!", "stars": 5, "createdAt": "2021-11-19 20:39:36", "updatedAt": "2021-11-19 20:39:36", "User": { "id": 1, "firstName": "John", "lastName": "Smith" }, "ReviewImages": [ { "id": 1, "url": "image url" } ] } ] }
-
-
Error response: Couldn't find a Spot with the specified id
-
Status Code: 404
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Spot couldn't be found" }
-
Create and return a new review for a spot specified by id.
-
Require Authentication: true
-
Request
-
Method: POST
-
URL: /api/spots/:spotId/reviews
-
Headers:
- Content-Type: application/json
-
Body:
{ "review": "This was an awesome spot!", "stars": 5 }
-
-
Successful Response
-
Status Code: 201
-
Headers:
- Content-Type: application/json
-
Body:
{ "id": 1, "userId": 1, "spotId": 1, "review": "This was an awesome spot!", "stars": 5, "createdAt": "2021-11-19 20:39:36", "updatedAt": "2021-11-19 20:39:36" }
-
-
Error Response: Body validation errors
-
Status Code: 400
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Bad Request", // (or "Validation error" if generated by Sequelize), "errors": { "review": "Review text is required", "stars": "Stars must be an integer from 1 to 5" } }
-
-
Error response: Couldn't find a Spot with the specified id
-
Status Code: 404
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Spot couldn't be found" }
-
-
Error response: Review from the current user already exists for the Spot
-
Status Code: 500
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "User already has a review for this spot" }
-
Create and return a new image for a review specified by id.
-
Require Authentication: true
-
Require proper authorization: Review must belong to the current user
-
Request
-
Method: POST
-
URL: /api/reviews/:reviewId/images
-
Headers:
- Content-Type: application/json
-
Body:
{ "url": "image url" }
-
-
Successful Response
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "id": 1, "url": "image url" }
-
-
Error response: Couldn't find a Review with the specified id
-
Status Code: 404
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Review couldn't be found" }
-
-
Error response: Cannot add any more images because there is a maximum of 10 images per resource
-
Status Code: 403
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Maximum number of images for this resource was reached" }
-
Update and return an existing review.
-
Require Authentication: true
-
Require proper authorization: Review must belong to the current user
-
Request
-
Method: PUT
-
URL: /api/reviews/:reviewId
-
Headers:
- Content-Type: application/json
-
Body:
{ "review": "This was an awesome spot!", "stars": 5 }
-
-
Successful Response
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "id": 1, "userId": 1, "spotId": 1, "review": "This was an awesome spot!", "stars": 5, "createdAt": "2021-11-19 20:39:36", "updatedAt": "2021-11-20 10:06:40" }
-
-
Error Response: Body validation errors
-
Status Code: 400
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Bad Request", // (or "Validation error" if generated by Sequelize), "errors": { "review": "Review text is required", "stars": "Stars must be an integer from 1 to 5" } }
-
-
Error response: Couldn't find a Review with the specified id
-
Status Code: 404
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Review couldn't be found" }
-
Delete an existing review.
-
Require Authentication: true
-
Require proper authorization: Review must belong to the current user
-
Request
- Method: DELETE
- URL: /api/reviews/:reviewId
- Body: none
-
Successful Response
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Successfully deleted" }
-
-
Error response: Couldn't find a Review with the specified id
-
Status Code: 404
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Review couldn't be found" }
-
Return all the bookings that the current user has made.
-
Require Authentication: true
-
Request
- Method: GET
- URL: /api/bookings/current
- Body: none
-
Successful Response
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "Bookings": [ { "id": 1, "spotId": 1, "Spot": { "id": 1, "ownerId": 1, "address": "123 Disney Lane", "city": "San Francisco", "state": "California", "country": "United States of America", "lat": 37.7645358, "lng": -122.4730327, "name": "App Academy", "price": 123, "previewImage": "image url" }, "userId": 2, "startDate": "2021-11-19", "endDate": "2021-11-20", "createdAt": "2021-11-19 20:39:36", "updatedAt": "2021-11-19 20:39:36" } ] }
-
Return all the bookings for a spot specified by id.
-
Require Authentication: true
-
Request
- Method: GET
- URL: /api/spots/:spotId/bookings
- Body: none
-
Successful Response: If you ARE NOT the owner of the spot.
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "Bookings": [ { "spotId": 1, "startDate": "2021-11-19", "endDate": "2021-11-20" } ] }
-
-
Successful Response: If you ARE the owner of the spot.
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "Bookings": [ { "User": { "id": 2, "firstName": "John", "lastName": "Smith" }, "id": 1, "spotId": 1, "userId": 2, "startDate": "2021-11-19", "endDate": "2021-11-20", "createdAt": "2021-11-19 20:39:36", "updatedAt": "2021-11-19 20:39:36" } ] }
-
-
Error response: Couldn't find a Spot with the specified id
-
Status Code: 404
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Spot couldn't be found" }
-
Create and return a new booking from a spot specified by id.
-
Require Authentication: true
-
Require proper authorization: Spot must NOT belong to the current user
-
Request
-
Method: POST
-
URL: /api/spots/:spotId/bookings
-
Body:
{ "startDate": "2021-11-19", "endDate": "2021-11-20" }
-
-
Successful Response
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "id": 1, "spotId": 1, "userId": 2, "startDate": "2021-11-19", "endDate": "2021-11-20", "createdAt": "2021-11-19 20:39:36", "updatedAt": "2021-11-19 20:39:36" }
-
-
Error response: Body validation errors
-
Status Code: 400
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Bad Request", // (or "Validation error" if generated by Sequelize), "errors": { "endDate": "endDate cannot be on or before startDate" } }
-
-
Error response: Couldn't find a Spot with the specified id
-
Status Code: 404
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Spot couldn't be found" }
-
-
Error response: Booking conflict
-
Status Code: 403
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Sorry, this spot is already booked for the specified dates", "errors": { "startDate": "Start date conflicts with an existing booking", "endDate": "End date conflicts with an existing booking" } }
-
Update and return an existing booking.
-
Require Authentication: true
-
Require proper authorization: Booking must belong to the current user
-
Request
-
Method: PUT
-
URL: /api/bookings/:bookingId
-
Headers:
- Content-Type: application/json
-
Body:
{ "startDate": "2021-11-19", "endDate": "2021-11-20" }
-
-
Successful Response
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "id": 1, "spotId": 1, "userId": 2, "startDate": "2021-11-19", "endDate": "2021-11-20", "createdAt": "2021-11-19 20:39:36", "updatedAt": "2021-11-20 10:06:40" }
-
-
Error response: Body validation errors
-
Status Code: 400
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Bad Request", // (or "Validation error" if generated by Sequelize), "errors": { "endDate": "endDate cannot come before startDate" } }
-
-
Error response: Couldn't find a Booking with the specified id
-
Status Code: 404
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Booking couldn't be found" }
-
-
Error response: Can't edit a booking that's past the end date
-
Status Code: 403
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Past bookings can't be modified" }
-
-
Error response: Booking conflict
-
Status Code: 403
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Sorry, this spot is already booked for the specified dates", "errors": { "startDate": "Start date conflicts with an existing booking", "endDate": "End date conflicts with an existing booking" } }
-
Delete an existing booking.
-
Require Authentication: true
-
Require proper authorization: Booking must belong to the current user or the Spot must belong to the current user
-
Request
- Method: DELETE
- URL: /api/bookings/:bookingId
- Body: none
-
Successful Response
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Successfully deleted" }
-
-
Error response: Couldn't find a Booking with the specified id
-
Status Code: 404
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Booking couldn't be found" }
-
-
Error response: Bookings that have been started can't be deleted
-
Status Code: 403
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Bookings that have been started can't be deleted" }
-
Delete an existing image for a Spot.
-
Require Authentication: true
-
Require proper authorization: Spot must belong to the current user
-
Request
- Method: DELETE
- URL: /api/spot-images/:imageId
- Body: none
-
Successful Response
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Successfully deleted" }
-
-
Error response: Couldn't find a Spot Image with the specified id
-
Status Code: 404
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Spot Image couldn't be found" }
-
Delete an existing image for a Review.
-
Require Authentication: true
-
Require proper authorization: Review must belong to the current user
-
Request
- Method: DELETE
- URL: /api/review-images/:imageId
- Body: none
-
Successful Response
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Successfully deleted" }
-
-
Error response: Couldn't find a Review Image with the specified id
-
Status Code: 404
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Review Image couldn't be found" }
-
Return spots filtered by query parameters.
-
Require Authentication: false
-
Request
- Method: GET
- URL: /api/spots
- Query Parameters
- page: integer, minimum: 1, maximum: 10, default: 1
- size: integer, minimum: 1, maximum: 20, default: 20
- minLat: decimal, optional
- maxLat: decimal, optional
- minLng: decimal, optional
- maxLng: decimal, optional
- minPrice: decimal, optional, minimum: 0
- maxPrice: decimal, optional, minimum: 0
- Body: none
-
Successful Response
-
Status Code: 200
-
Headers:
- Content-Type: application/json
-
Body:
{ "Spots": [ { "id": 1, "ownerId": 1, "address": "123 Disney Lane", "city": "San Francisco", "state": "California", "country": "United States of America", "lat": 37.7645358, "lng": -122.4730327, "name": "App Academy", "description": "Place where web developers are created", "price": 123, "createdAt": "2021-11-19 20:39:36", "updatedAt": "2021-11-19 20:39:36", "avgRating": 4.5, "previewImage": "image url" } ], "page": 2, "size": 25 }
-
-
Error Response: Query parameter validation errors
-
Status Code: 400
-
Headers:
- Content-Type: application/json
-
Body:
{ "message": "Bad Request", // (or "Validation error" if generated by Sequelize), "errors": { "page": "Page must be greater than or equal to 1", "size": "Size must be greater than or equal to 1", "maxLat": "Maximum latitude is invalid", "minLat": "Minimum latitude is invalid", "minLng": "Maximum longitude is invalid", "maxLng": "Minimum longitude is invalid", "minPrice": "Minimum price must be greater than or equal to 0", "maxPrice": "Maximum price must be greater than or equal to 0" } }
-