Scarce City logo

How Scarce City is Built

by Aryan Jabbari

Shortly after losing my job in the midst of this crazy COVID mess, I was given the opportunity to build Scarce City, a transparent marketplace where buyers benefit from dynamic pricing and customer revenue sharing (yup! We do lotteries that put bitcoin back into our buyers' wallets). Building Scarce City was my gateway drug into Bitcoin and an opportunity to learn some new technologies.

In the two months since the beginning of this project, I built an e-commerce website that accepts Bitcoin and Bitcoin Lightning with the ability to store transactions permanently on Arweave's blockchain. I'm proud to be a part of the Scarce City team and excited to share it with all of you.

Here are some of the technologies that build Scarce City.


The backend is hosted on Vercel using NextJS' API Routes. This sped up development as application deployment (both backend and frontend) was taken care of by linking Vercel to my GitHub repository. With every file mapping to a route, I was quickly able to add new API routes to my application.

The transaction data lives in a Postgres database on AWS RDS. The database was very simple to set up and add to Prisma. I was even able to use the AWS free tier for the development database.

The invoicing and payment processing is handled by btcpayserver, an open-source bitcoin payment processor. I instantly fell in love with this project; it's simply a beautiful piece of software. It was easy to set up once the bitcoin node was synced. Set up included adding our wallet and pairing with the Scarce City backend code. This server is deployed to AWS EC2 using Docker.

The Lightning Network powers our Bitcoin Lightning payments. As usual, btcpayserver made it extremely easy to get onto the Lightning Network. An instance of Ride The Lightning, a fully functioning web app that wraps the LND (or C-Lightning) CLI, is included with btcpayserver. This made opening up incoming and outgoing channels super easy.

Since content (such as blog posts, product copy, product pictures, etc.) needs to be managed by my non-developer colleague, I decided a CMS was important. I heard nothing but good things about Sanity and decided to try it out. I am beyond impressed by the ease of manipulating the data schema and adding new features to the CMS. Additionally, creating content on the CMS is super easy. After editing or creating a new document, Sanity allows me to call a Vercel web hook which rebuilds the entire app (incremental builds can't come soon enough!).

We use Arweave to permanently store completed transactions as a means of verifying the supply and authenticity of the Scarce City merchandise. This is a vital piece of the puzzle as it gives Scarce City buyers transparency into the transactions we process.

Since data was coming from multiple sources (our own database, btcpayserver, Arweave and Sanity), GraphQL was the logical choice for the API. Being a huge fan of Prisma, I opted to use it as our ORM (especially since our data model is extremely simple). Even though it is in beta, it met all of Scarce City's needs and didn't break once!


Scarce City is built with NextJS since it's the React framework I am most familiar with. Out of the box, I got server-side generation for the product pages and static pages for the home page and blog posts.

I initially went with Apollo Client for GraphQL requests and caching. However, Apollo Client is an absolute mess with NextJS. Furthermore, it added about 35kB to my build! I ran into the loving arms of SWR and graphql-request and I'll never look back.

All of the styling is done with TailwindCSS which is the most versatile CSS framework out now.I can't recommend it enough. It's super easy and development is lightning fast with the TailwindCSS VSCode extension.

The product page forms are built with Formik and validated with Yup. Getting Formik up and running was a bit frustrating and, honestly, I'm not completely in love with its syntax. If I were to make the forms again, I'd probably give React Hook Form a shot.

Lastly, all the social cards for the blog posts are dynamically created using Cloudinary and get-share-image which dynamically creates an image URL featuring the title of the blog post. What a fun and easy-to-use library!

For updates on Scarce City, follow us on Twitter: @scarcedotcity or sign up for our newsletter at If you have any questions about Scarce City (whether it be the product itself or how it was built), please reach out to me on Twitter at @aryanjabbari.

Subscribe to our newsletter 💡

Join our mailing list to receive product drop and blog updates