How Builtwith.appwrite.io Was Made
Tags: opensource, webdev, tutorial
Published: 2023-06-08 16:06:35

We recently announced the [builtwith.appwrite.io](http://builtwith.appwrite.io) website - the one-stop solution to create awareness about your Appwrite-related projects as well as discover the amazing work being done by our community members.

In this article, we will see how the website was built. If you want to learn more about the website and what features it has, read the announcement blog [here](https://dev.to/appwrite/announcing-the-built-with-appwrite-platform-c7j). 

## 🤔 Why Did We Build Built with Appwrite?

The vision around [builtwith.appwrite.io](https://builtwith.appwrite.io) was to highlight our community members who are building with Appwrite and aimed to provide developers with a playground where they could explore real-world examples, learn best practices, and ignite their imagination for building feature-rich applications. The website is now accepting submissions of Appwrite-built projects, currently consisting of around 50 community-contributed projects. If you are looking for some ideas, you can use our filtering options to discover new projects based on Appwrite services, frameworks, UI libraries, or specific use cases. 

![Screenshot of the website](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0dofr4uau6gq0wovi433.png)



## 🎒Tech Stack

We leveraged the power of [Appwrite Cloud](https://cloud.appwrite.io) for our backend, incorporating seamless authentication through one of Appwrite's OAuth providers, GitHub. To store data, we utilized Appwrite's Storage and Database. Security was enhanced using Appwrite Functions during submission and upvoting. For a streamlined frontend experience, we turned to **[Qwik](https://qwik.builder.io/)**, while the elegant **[Pink Design](https://pink.appwrite.io/)** served as our trusted design system.

The combination of Qwik, Appwrite, and Pink Design transformed the development process into an enjoyable and efficient journey, making this project both delightful and time-efficient.

**Setting up the server and client**

Setting up the server is really simple, all you need to do is:

- Register Appwrite account
- Create a project
- Setup GitHub authentication, a database, and a storage bucket
- Setup Appwrite functions

**Note: You can use the Appwrite CLI for easy and quick setup**

On the client side, you need to:

- Install libraries with `npm install`
- Update projectID in `src/AppwriteService.ts`
- Start server `npm run dev`

For detailed steps, you can follow our [GitHub README](https://github.com/appwrite/builtwith).

## Functions Highlight 🔦

We wanted to add more security to the website and also prevent spam entries at the same time, and this is when Appwrite Functions came to the rescue.

**A. Submitting project** - We wanted to review each submission and then add them to the website. This needed an extra step of approval and making sure that all the necessary details were added. We also had to make sure that the `projects` collection did permit users to `CREATE`, and the only way to do it is over a function.

It ensures that people cannot set “upvotes” to fake huge numbers, and neither can they set `isPublished=true`, and skip the verification step.

We also wanted to send an email to the Appwrite core team whenever there is a new submission. 
Here is how the email looks 👇


![Screenshot of emails](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vc7i1omci4lgr95q6tut.png)

The following code ensures that all the necessary fields are added and by default, marks each project submission as `isPublished=false` and `upvotes: 0`.

```jsx
const sdk = require("node-appwrite");
const nodemailer = require("nodemailer");

module.exports = async function (req, res) {
  const client = new sdk.Client();
	const databases = new sdk.Databases(client);

	client
		.setEndpoint("https://cloud.appwrite.io/v1")
		.setProject(req.variables["APPWRITE_FUNCTION_PROJECT_ID"])
		.setKey(req.variables["APPWRITE_FUNCTION_API_KEY"]);

	const payload = JSON.parse(req.payload ?? "{}");
	const userId = req.variables["APPWRITE_FUNCTION_USER_ID"] ?? "";

	const project = await databases.createDocument(
		"main",
		"projects",
		sdk.ID.unique(),
		{
			isPublished: false, // Enforce
			isFeatured: false, // Enforce
			upvotes: 0, // Enforce
			platform: payload.platform,
			name: payload.name,
			tagline: payload.tagline,
			imageId: payload.fileId,
      // And others...
		});

	res.json({
		ok: true,
		msg: "Submission successful.",
	});
	
	const approverEmails = req.variables["APPROVER_EMAILS"].split(",");
	const transporter = nodemailer.createTransport({
		// Configuration of SMTP
	});
	
	await transporter.sendMail({
		from: SMTP_USERNAME,
		to: approverEmails,
		subject: "Project Submission - builtwith.appwrite.io",
		text: `Project "${project.name}" has been submitted.`
	});
};
```

**B. Upvoting** - The website has an upvote feature, but we wanted to ensure it is not misused by fake IDs. This was avoided by not giving `CREATE` permission to users, and the only way to achieve this is by using a function.

*Why did we not delegate create permission?* 👀
*To prevent people from making multiple upvote documents and having no way to validate those*

The following code expects string input of the project ID that the user wants to upvote and ensures that the same user cannot upvote the same project multiple times. It then also aggregates the vote count and updates the project document with a new count.

```jsx
const sdk = require("node-appwrite");

module.exports = async function (req, res) {
  const client = new sdk.Client();

  const databases = new sdk.Databases(client);

  client
    .setEndpoint("https://cloud.appwrite.io/v1")
    .setProject(req.variables["APPWRITE_FUNCTION_PROJECT_ID"])
    .setKey(req.variables["APPWRITE_FUNCTION_API_KEY"]);

  const projectId = req.payload ?? "";
  const userId = req.variables["APPWRITE_FUNCTION_USER_ID"] ?? "";

  const search = await databases.listDocuments("main", "projectUpvotes", [
    sdk.Query.limit(1),
    sdk.Query.equal("projectId", projectId),
    sdk.Query.equal("userId", userId),
  ]);
  const isUpvoted = search.documents.length > 0;

	// Mark upvoted / not-upvoted
  if (isUpvoted) {
    await databases.deleteDocument("main", "projectUpvotes", search.documents[0].$id);
  } else {
    await databases.createDocument("main", "projectUpvotes", sdk.ID.unique(), {
      projectId,
      userId,
    });
  }

	// Re-count total votes
  const aggregation = await databases.listDocuments("main", "projectUpvotes", [
    sdk.Query.limit(1),
    sdk.Query.equal("projectId", projectId),
  ]);

  const totalUpvotes = aggregation.total;

  await databases.updateDocument("main", "projects", projectId, {
    upvotes: totalUpvotes,
  });

  res.json({
    ok: true
  });
};
```

## 🐞Blooper

**What bug did we handle? 🐛**

Soon after we launched the Built With Appwrite website, we received bug reports from our community members about not being able to login. The search began to see what went wrong, and it turned out that the search dialog didn’t work as expected. 

**What happened? 🤔**

When the dialog was added, all the code that was in `useVisibleTask` stopped working, including login and some global keyboard shortcuts. 

**Why did it happen? 🙅‍♀️**

While building, we assumed the `useVisibleTask()` hook was like react `useEffect()` hook. We later figured it only runs when the component is visible using [Intersection observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).  

**How did we solve it? 👀**

It took lots of debugging by reverting each individual change to figure out the issue. Eventually, we noticed that if we kept everything the same - but instead used a div rather than a dialog, things worked again!

P.S. - Adding the entire code would make the article very long. However, if you're curious about the implementation details of a specific feature, you can see the full source code [here](https://github.com/appwrite/builtwith/blob/main/src/AppwriteService.ts)

## 🔗 Learn More

Now that you know how Appwrite and Qwik were used to build the entire website, here are some resources that can come in handy.

**Appwrite Resources**
- [Appwrite Docs](https://appwrite.io/docs)
- [Appwrite Discord](https://appwrite.io/discord)
- [Appwrite Cloud](https://appwrite.io/cloud)
- [Pink Design](https://pink.appwrite.io/)

**Qwik Resources**
- [QwikCity](https://qwik.builder.io/qwikcity/overview/)
- [Qwik Docs](https://qwik.builder.io/)
- [Qwik Discord](https://qwik.builder.io/chat)
- [Qwik GitHub](https://github.com/BuilderIO/qwik)