1. Introduction
Back in September, I set myself a goal of building a new marketplace quickly using the Speed Build Marketplace boilerplate.
The vision was to create a two-sided platform for renting out your spare desk. So, you could say - a way to turn your house into a co-working space. Crazy idea..? 🤔
The MVP is now done and it is called My Spare Desk.
In this video, you can see my initial thoughts about the idea and competitive landscape.
This post delves into the tech stack I chose to bring My Spare Desk to life:
- Next.js 14 for both frontend and backend
- Tailwind CSS for styling
- MongoDB as the database
- and Clerk for authentication.
Additionally, I used the Google Maps API to enhance listing visibility on a map.
I'll share why I picked each tool, the benefits they brought, and a few things to watch out for if you’re considering these technologies for your own projects.
2. Tech Stack Overview
Building a marketplace requires a versatile set of technologies that makes it easy to quickly build your MVP - but that also makes it easy to iterate after you launched your MVP.
You will for sure have to make some changes after you launch.
Here’s a breakdown of the tools I used:
Next.js 14: The backbone of the project, handling both frontend and backend functionalities. Using the App router of course. Its features for server-rendered pages, API routes, and serverless functions made it ideal for creating a seamless, fullstack experience. (I’ll probably update to Next.js 15 in the next 3-6 months.)
Tailwind CSS: Tailwind made it easy to build a consistent design system, keeping styles modular and easy to maintain.
MongoDB: MongoDB’s schema flexibility was crucial as it allowed me to iterate on the data model without rigid constraints. This made it perfect for handling user-generated content like listings and offers.
Clerk: For secure and flexible user authentication, Clerk was a great choice, especially with its built-in session management and social sign-in options. It allowed me to focus more on marketplace functionality and less on handling authentication. Also, integrating Google oAuth was super easy.
Google Maps API: This API allowed me to easily display listing locations on a map, providing users with a quick visual overview of where each desk is located.
Each choice came with its own pros and cons, and in the next sections, I’ll dive deeper into why each was a fit and what I learned along the way.
3. Why Next.js?
Next.js 14’s fullstack capabilities were well-suited to My Spare Desk, as it provided the tools needed to manage both frontend and backend in a single codebase. Here’s why it worked well:
Key Advantages of Next.js for Marketplaces
Server-Side Rendering (SSR): SSR enabled the marketplace to load dynamic content quickly and be SEO-optimized, which is essential for a listing-heavy platform like My Spare Desk. All listings are optimized to schema.org as well to openGraph - which makes listings look good when sharing them on social media platforms. The Next.js MetaData API makes it easy to implement this.
API Routes and Serverless Functions: Next.js API routes allowed me to handle server-side logic (like fetching or updating listings) without a separate backend framework, which kept development streamlined within a single project. I find it nice not to have to switch between code-bases.
Incremental Static Regeneration (ISR): ISR was particularly useful for pages like the front page and listings, where changes are periodic rather than constant. This allowed for automatic updates without regenerating the entire site.
Caching Challenges
Caching presented some challenges, especially when users added or deleted listings. After troubleshooting, I found that using a combination of router.refresh(), along with:
import { revalidatePath } from "next/cache";
import { revalidateTag } from "next/cache";
However, caching can still be tricky, especially on serverless platforms like Vercel, where function region settings and caching policies require careful management.
But I know that many find caching tricky. Just see this reddit thread. 😂. But this is also the reason, I am sure, why the Vercel team is changing the caching approach with Next.js 15.
But overall, Next.js 14’s fullstack approach provided a solid foundation for My Spare Desk.
4. Styling with Tailwind CSS
For My Spare Desk, I chose Tailwind CSS because it offered a highly modular way to build a consistent and responsive design system.
5. Database Choice: MongoDB
MongoDB was a natural fit for My Spare Desk due to its schema flexibility, allowing me to easily adapt the data model as the project grew. For a marketplace MVP, where rapid development is critical, I prefer using a flexible, scalable, and intuitive way to manage data—and MongoDB made that possible.
Why MongoDB?
Schema Flexibility: With MongoDB’s schema-less design, I could iterate on data structures without needing extensive migrations—this was especially useful as I added new features and adjusted existing ones.
This is the offer schema:
import mongoose, { Document, Model, Schema } from "mongoose";
interface IOffer extends Document {
listing: mongoose.Schema.Types.ObjectId;
slots: {
date: string;
start: string;
end: string;
}[];
amount: number;
buyer: mongoose.Schema.Types.ObjectId;
seller: mongoose.Schema.Types.ObjectId;
status: "pending" | "accepted" | "declined";
paymentIntentId?: string;
reviewGiven: boolean;
createdAt?: Date;
updatedAt?: Date;
}
const offerSchema: Schema<IOffer> = new mongoose.Schema(
{
listing: {
type: mongoose.Schema.Types.ObjectId,
ref: "Listing",
required: true,
},
slots: [
{
date: { type: String, required: true },
start: { type: String, required: true },
end: { type: String, required: true },
},
],
amount: {
type: Number,
required: true,
},
buyer: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
seller: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
status: {
type: String,
enum: ["pending", "accepted", "declined"],
default: "pending",
},
paymentIntentId: {
type: String,
required: false,
},
reviewGiven: {
type: Boolean,
default: false,
},
},
{
timestamps: true,
}
);
const Offer: Model<IOffer> =
mongoose.models.Offer || mongoose.model<IOffer>("Offer", offerSchema);
export default Offer;
Handling User-Generated Content: MongoDB’s document-based model is well-suited for user-generated data like listings, offers, and reviews, which may have varied fields and structures.
Considerations for Working with MongoDB
If you’re considering MongoDB for a marketplace, here are a few things to keep in mind:
Data Consistency: Define and validate required fields to avoid fragmented data, which can occur with MongoDB’s flexible schema.
Using an ODM: I found that using an ODM (like Mongoose) added useful structure while still allowing MongoDB’s flexibility.
Index Optimization: Plan for index optimization early on, especially for frequently queried fields like user IDs and listing data. Indexing can significantly improve performance as your app scales.
6. Authentication with Clerk
Auth is always a pain. And it’s not something you want to spend too much time on—you just want it to work! Clerk fit the bill perfectly, with a Next.js-friendly integration and features that made the authentication process smooth and secure.
Why Clerk?
Ease of Integration: Clerk’s SDK is straightforward to set up with Next.js, enabling quick integration of user sign-up, login, and session management.
Social Logins Made Easy: Clerk includes support for social logins, multi-factor authentication, and user profile management. This saved me significant development time since I didn’t need to code these features from scratch.
Tips for Using Clerk
Here’s what I learned working with Clerk:
Webhooks: It can take a bit of time to get familiar with their webhooks to handle events like user creation or deletion. But as always, once you done it once, it’s straightforward.
Development Mode: In development mode, you’ll need to use Ngrok or similar to get webhooks working locally. This adds a little setup but makes testing Clerk’s functionality much easier.
7. Key Features and Implementation Details
Building a marketplace requires more than just listing items—it needs smooth navigation, secure payments, and a way for users to communicate.
In this video, I walk through the main features of My Spare Desk:
Listing Creation and Management: Users can easily create, edit, and manage their listings. I used Next.js API routes to handle server-side logic for CRUD operations, ensuring efficient data handling and a straightforward user experience.
Booking Flow: A key feature was setting up a clear, seamless booking process. With Stripe integration, users can securely make payments, and the booking details are stored in MongoDB. The backend checks for double bookings and verifies availability, keeping everything smooth and reliable. The calendar on a listing also shows if a listing already have bookings.
Stripe Connect: To handle transactions, I used Stripe Connect, enabling direct payments to users’ accounts. To make a listing, users must first connect their account to Stripe Connect, providing a secure and direct way for funds to be transferred.
Messaging System: To facilitate communication between renters and hosts, I built a messaging system where messages are stored in MongoDB and include read status indicators. Users can also send images to each other, stored via Cloudinary.
8. Lessons Learned and Developer Considerations
Building My Spare Desk provided a lot of insights, especially in balancing speed with functionality for a marketplace MVP. Here are a few key takeaways and tips for developers considering similar projects:
Optimize for Performance Early: Working with serverless functions and caching in Next.js requires close attention to performance, particularly for first-call latency and caching strategies. For example, configuring cache revalidation and experimenting with Vercel’s function region settings noticeably impacted page load times.
Efficient Authentication with Clerk: Clerk saved time and simplified auth setup, but there’s a learning curve for webhook handling. Tools like Ngrok are helpful to streamline local testing, and Clerk’s metadata fields make it easier to store user-specific data without extra setup.
These insights helped me create a scalable and efficient marketplace MVP, and focusing on performance and ease of integration made a clear difference.
9. Conclusion
By using the Speed Build Marketplace boilerplate, built on Next.js, Tailwind, MongoDB, and Clerk, I was able to go from idea to MVP very fast.
Remember! The goal with any MVP is to build and launch quickly to see if the idea resonates and to quickly see if you can get both sides of the marketplace to connect.
And if not (which is quite normal), then you have a foundation to iterate from.
And if you built your MVP on a flexible tech-stack - like Next.js - it is easy to iterate later.
This is not the case if you choose to build in a more closed environment. So remember that, before you choose to build your MVP on a no-code platform or similar!
Moving forward, I plan to continue iterating on My Spare Desk, refining features, and monitoring performance as it scales. And most import of all - try and get as much feedback as possible from potential users.
If you’re a developer looking to build your own marketplace, I hope these insights offer a practical guide to tools, best practices, and considerations that can help you bring your idea to life.
Always just reach out if you have any questions!
Top comments (0)