Building A URL Shortener using Appwrite and Express

ยท

5 min read

What is Appwrite?

Appwrite is a self-hosted backend solution that enables developers get up and running with most of their backend needs in any project. Appwrite grants access to features like:

  • Storage (Media files)
  • Database (Documents)
  • Authentication (Manage Users)
  • Cloud Functions and more

and you can access these from REST APIs.

We're going to be building a URL Shortener with Appwrite in this article.

Prerequisites

  • Docker
  • Node
  • Yarn (or npm but we'll be using Yarn)
  • Working knowledge of Express

Setting up Appwrite

Head over to the Appwrite homepage and click on Get Started.

Image description

Copy the corresponding terminal command for your computer, ensure Docker is up and running, then run the command. Wait a while for the installation to be completed and you should see this.

Image description

Set the HTTP PORT to 3000 and leave all other values as their default. Head to the browser and visit localhost:3000

You should be on the Signup page. If not, head here

Image description

After creating an account, create a new project assigning it the name url-short.

Image description

For this project, we're only using the Database feature but we need our API Key first. Click API Keys and Add a new key

Image description

Click Select All to enable all permissions and give it a name of 'admin'. Create it and click Show Secret to view your secret. Copy it somewhere as we'll be needing it later.

Lastly, from the homepage, Click Settings and copy your Project ID as well as your API Endpoint.

Image description

##Setting up the Express Server

Clone this repo locally. Run yarn or npm install to install all packages needed. Add a .env file containing the following variables:

APPWRITE_KEY="YOUR_APPWRITE_KEY"
PROJECT_ID="YOUR_PROJECT_ID"
LINKS_COLLECTION_ID="YOUR_LINKS_COLLECTION_ID"

Your folder structure at this point should look like this:

Image description

I know you're wondering where this 'Links Collection' is coming from. Well, we need to create a Collection (Table) to hold the links and their short names and we'll call it Links. Open the appwrite-links.js file .

require("dotenv").config();

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

const client = new sdk.Client();

client
  .setEndpoint("http://localhost:3000/v1") // Your API Endpoint
  .setProject(process.env.PROJECT_ID) // Your project ID
  .setKey(process.env.APPWRITE_KEY); // Your API key

const db = new sdk.Database(client);

const run = async () => {
  try {
    let collection = await db.createCollection(
      "Links",
      [],
      [],
      [
        {
          label: "originalURL",
          key: "originalurl",
          type: "text",
          default: "Empty Name",
          required: true,
          array: false,
        },
        {
          label: "uniqueName",
          key: "uniquename",
          type: "text",
          default: "Empty",
          required: true,
          array: false,
        },
        {
          label: "shortUrl",
          key: "shorturl",
          type: "text",
          default: "Empty",
          required: true,
          array: false,
        },
      ]
    );
    console.log(collection.$id);
  } catch (e) {
    console.log(e);
  }
};

run();

We're simply initializing our client object by passing it our API Endpoint as well as our Environmental Variables. The db.createCollection function on line 16 takes in 4 arguments in the following order:

  1. Name - The name of your collection.
  2. Read Array - Array of User Ids of Users that can read from the Collection.
  3. Write Array - Array of User Ids of Users that can write to the Collection.
  4. Rules - An array of objects with each object dictating how each field in the collection must be used.

Run node appwrite-links.js on the terminal and it would spit out your Links Collection id. You can add it to the .env file.

The public folder contains the frontend we'd be serving. The app.js file simply sets up our server and the routes it'd be accepting requests from. To see the crux of the app, head to the url.js file in the controllers folder.

const { getDB } = require("./../appwrite");
const { v4: uuidv4 } = require("uuid");

const PORT = process.env.port || 3001;

const baseUrl = process.env.BASE_URL || `http://localhost:${PORT}`;

const createLink = async (req, res) => {
  let { originalURL, uniqueName } = req.body;

  const db = getDB();

  if (uniqueName) {
    let documents = await db.listDocuments(process.env.LINKS_COLLECTION_ID, [
      `uniquename=${uniqueName}`,
    ]);
    if (documents.sum > 0)
      return res.status(403).send("This unique name is taken");
  } else {
    while (true) {
      uniqueName = uuidv4().substr(0, 6);
      let documents = await db.listDocuments(process.env.LINKS_COLLECTION_ID, [
        `uniquename=${uniqueName}`,
      ]);
      if (documents.sum == 0) break;
    }
  }

  db.createDocument(process.env.LINKS_COLLECTION_ID, {
    originalurl: originalURL,
    uniquename: uniqueName,
    shorturl: baseUrl + "/" + uniqueName,
  })
    .then((resp) => {
      return res.status(201).send({ shortUrl: resp.shorturl });
    })
    .catch(console.log);
};

const getLink = async (req, res) => {
  const { uniqueId } = req.params;

  const db = getDB();

  if (!uniqueId || uniqueId === "undefined")
    return res.status(403).send("Invalid Request");

  let documentsList = await db.listDocuments(process.env.LINKS_COLLECTION_ID, [
    `uniquename=${uniqueId}`,
  ]);

  if (documentsList.sum == 0)
    return res.status(404).send("Unique Name not found");

  res.redirect(documentsList.documents[0].originalurl);
};

module.exports = {
  createLink,
  getLink,
};
  • Create Link It expects a uniqueName and an originalURL in the request body. If a uniqueName is not sent, we create one for the user with the uuid package but ensure it's not longer than 6 characters. We also check the database if it already exists and either create a new one or send an error to the user if the uniqueName was in the request body.

  • Get Link We simply use the uniqueName passed as a parameter to get the originalURL and redirect the user to that url.

Run yarn start in the terminal and head to localhost:3001 to see the project LIVE ๐ŸŒŸ

Image description

In this article, I showed you a quick dive into Appwrite and its main features. We then went ahead to build a simple URL shortener with Appwrite. Hope you enjoyed it ๐Ÿค—.

Feel free to comment any suggestions or questions. You can also reach me at my email, lorddro1532@gmail.com or on twitter, @the_dro _.

ย