Building data-rich apps with Next.js and Tigris
Tags: nextjs, vercel, typescript, tutorial
Published: 2022-10-27 16:38:53

[Next.js](https://nextjs.org/) gives you the best developer experience with all the features you need to build modern, fast production-ready applications. [Tigris](https://docs.tigrisdata.com/guides/nextjs/) is the perfect companion for Next.js as it is similarly built with developer experience in mind and is truly serverless: build data-rich features, seamlessly implement search, and easily use it with serverless functions, all without needing to do Ops.

![Next.js and Tigris](https://blog.tigrisdata.com/assets/images/nextjs-blog-banner-59458fcd323325acf8d7c7c260797e74.jpg)

Now with the introduction out of the way, it is time to demonstrate how.

This is the first of a series of blog posts where we will demonstrate how easy it is to build Next.js apps with Tigris. We will build a to-do list app and deploy it to [Vercel](https://vercel.com/). The to-do list app will have the following features:

-   add to-do items to the list
-   update to-do items as completed
-   delete to-do items
-   search for to-do items in the list

To follow along with this tutorial you can get the code from the [GitHub repo](https://github.com/tigrisdata/tigris-vercel-starter). This is how the to-do app will look once its deployed: <https://tigris-nextjs-starter-kit.vercel.app/>

Prerequisites[​](https://blog.tigrisdata.com/build-data-rich-apps-nextjs-tigris-vercel#prerequisites "Direct link to heading")
------------------------------------------------------------------------------------------------------------------------------

For this tutorial you'll need:

1.  [GitHub account (sign up for free)](https://github.com/)
2.  [Tigris Cloud account (sign up for free)](https://www.tigrisdata.com/beta#signup-form)
3.  [Vercel account (sign up for free)](https://vercel.com/) to deploy app
4.  Node.js 16+
5.  npm and npx

Setting up Tigris[​](https://blog.tigrisdata.com/build-data-rich-apps-nextjs-tigris-vercel#setting-up-tigris "Direct link to heading")
--------------------------------------------------------------------------------------------------------------------------------------

The CLI is the easiest way to get started with Tigris. Let's install the CLI first:

-   macOS
-   linux

```
brew install tigrisdata/tigris/tigris-cli
```

The next step is to generate credentials: *clientId*, *clientSecret*. These credentials will be used by the Next.js to-do list app to connect and authenticate with Tigris. Let's login to your Tigris Cloud account and create the application credentials.

```
tigris login
```

```
tigris create application todo_list "to-do list application"
```

Once the command is executed, it would create the application credentials (*clientID* and *clientSecret*) and print it on the screen. Make a note of `id` and `secret` from the output.


{% embed https://youtu.be/Ls50EOrU3AQ %}

Alternatively, you can generate the application credentials through the [Tigris Cloud web console](https://console.preview.tigrisdata.cloud/).

Deploying the to-do list app to Vercel[​](https://blog.tigrisdata.com/build-data-rich-apps-nextjs-tigris-vercel#deploying-the-to-do-list-app-to-vercel "Direct link to heading")
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

We will start off first by deploying the pre-prepared to-do list app to Vercel from the [GitHub repo](https://github.com/tigrisdata/tigris-vercel-starter). Then once it is deployed and running, we will explore the code in detail.

### Create a project on Vercel[​](https://blog.tigrisdata.com/build-data-rich-apps-nextjs-tigris-vercel#create-vercel-project "Direct link to heading")

Vercel makes it easier to deploy Git projects with a few clicks.

Hit the following Deploy button to get started with the Vercel workflow to clone the repo to your account

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Ftigrisdata%2Ftigris-vercel-starter%2F&env=TIGRIS_URI,TIGRIS_CLIENT_ID,TIGRIS_CLIENT_SECRET&envDescription=Application%20secrets%20to%20access%20Tigris%20cloud&envLink=https%3A%2F%2Fgithub.com%2Ftigrisdata%2Ftigris-vercel-starter%2Fblob%2Fmain%2F.env.example&project-name=tigris-nextjs-todo-app&repo-name=tigris-nextjs-todo-app&demo-title=Tigris%20todo%20app&demo-description=A%20ToDo%20list%20web%20app%20using%20NextJS%20and%20Tigris%20Data)

This should take you to Vercel to the "Create Git Repository" step

![Vercel create repo](https://blog.tigrisdata.com/assets/images/vercel-create-repo-bdc676093585e7784ad8d8ed846b81ed.jpg)

Pick a name for your new Git repo and then you'll configure the environment variables needed to connect the to-do list app to Tigris. This is where we'd need the *clientID* and *clientSecret* generated in the [setting up Tigris](https://blog.tigrisdata.com/build-data-rich-apps-nextjs-tigris-vercel#setting-up-tigris) section above.

```
TIGRIS_URI=api.preview.tigrisdata.cloud
TIGRIS_CLIENT_ID=<your_client_id_here>
TIGRIS_CLIENT_SECRET=<your_client_secret_here>
```

![Vercel environment](https://blog.tigrisdata.com/assets/images/vercel-env-variables-fce8683e7ab432229fb74016b1c6f0ca.jpg)

Once you have entered the environment variables hit the Deploy button. That's it!

Once the deployment completes, continue to your project dashboard on Vercel where you'll find URL for your to-do list app

![Vercel project dashboard](https://blog.tigrisdata.com/assets/images/vercel-project-dashboard-43ae64833423601ff31b0fffa09c9708.jpg)

🎉 All done. Visit the URL in browser to access your to-do list app and play around. 🎉

Now let's continue to explore the code for the to-do list app to see how easily Tigris can be integrated with Next.js.

Code walk-through[​](https://blog.tigrisdata.com/build-data-rich-apps-nextjs-tigris-vercel#code-walk-through "Direct link to heading")
--------------------------------------------------------------------------------------------------------------------------------------

This section will elaborate on important aspects of the to-do list app you just deployed. Let's glance over the important components of the project.

File structure

```
|-- package.json
|-- lib
|   |-- schema.ts
|   |-- tigris.ts
|-- pages
    |-- index.tsx
    |-- api
        |-- item
        |   |-- [id].ts
        |-- items
            |-- index.ts
            |-- search.ts
```

### Tigris schema definition - `lib/schema.ts`[​](https://blog.tigrisdata.com/build-data-rich-apps-nextjs-tigris-vercel#tigris-schema-definition---libschemats "Direct link to heading")

With Tigris it all starts with the data model!

Tigris stores data records as documents. Documents are analogous to JSON objects but Tigris stores them in an optimized binary format. Documents are grouped together in collections. The to-do list app has a single [collection](https://docs.tigrisdata.com/documents/documents) `todoItems` that stores the to-do items. The first thing you would do is define the schema that enables you to structure the data. The schema can be easily evolved in a lightweight way without costly rebuild operations.

The schema for the collection is defined in `lib/schema.ts` as follows:

**lib/schema.ts**

```
import {
  TigrisCollectionType,
  TigrisDataTypes,
  TigrisSchema,
} from "@tigrisdata/core/dist/types";

export const DB_NAME = "tigrisTodoApp";
export const COLLECTION_NAME = "todoItems";

export interface TodoItem extends TigrisCollectionType {
  id?: number;
  text: string;
  completed: boolean;
}

export const TodoItemSchema: TigrisSchema<TodoItem> = {
  id: {
    type: TigrisDataTypes.INT32,
    primary_key: { order: 1, autoGenerate: true },
  },
  text: { type: TigrisDataTypes.STRING },
  completed: { type: TigrisDataTypes.BOOLEAN },
};
```

### Connecting to Tigris - `lib/tigris.ts`[​](https://blog.tigrisdata.com/build-data-rich-apps-nextjs-tigris-vercel#connecting-to-tigris---libtigrists "Direct link to heading")

This file loads the environment variables you specified previously in [creating a Vercel project section](https://blog.tigrisdata.com/build-data-rich-apps-nextjs-tigris-vercel#create-vercel-project) and uses them to configure the Tigris client. This client is used to manage all the Tigris operations from here on. Also, note how we are caching the client instance so that it can be used for subsequent requests.

**lib/tigris.ts**

```
import { DB, Tigris, TigrisClientConfig } from "@tigrisdata/core";
import { DB_NAME } from "./schema";

if (!process.env.TIGRIS_URI) {
  throw new Error("Cannot find TIGRIS_URI environment variable ");
}

const tigrisUri = process.env.TIGRIS_URI;
const clientConfig: TigrisClientConfig = { serverUrl: tigrisUri };

if (process.env.TIGRIS_CLIENT_ID) {
  clientConfig.clientId = process.env.TIGRIS_CLIENT_ID;
}
if (process.env.TIGRIS_CLIENT_SECRET) {
  clientConfig.clientSecret = process.env.TIGRIS_CLIENT_SECRET;
}

declare global {
  var tigrisDb: DB;
}

let tigrisDb: DB;

if (process.env.NODE_ENV === "development") {
  if (!global.tigrisDb) {
    // re-use the same connection in dev
    const tigrisClient = new Tigris(clientConfig);
    global.tigrisDb = tigrisClient.getDatabase(DB_NAME);
  }
  tigrisDb = global.tigrisDb;
} else {
  const tigrisClient = new Tigris(clientConfig);
  tigrisDb = tigrisClient.getDatabase(DB_NAME);
}

// export to share DB across modules
export default tigrisDb;
```

### Creating the database and collection - `scripts/setup.ts`[​](https://blog.tigrisdata.com/build-data-rich-apps-nextjs-tigris-vercel#creating-the-database-and-collection---scriptssetupts "Direct link to heading")

This app ensures that the database and collection are automatically created at build time. There is no need to manually create them. Databases and collections can be created in an idempotent way instantaneously, i.e. it is safe to run the creation operation during build time. More details on this automatic setup can be found in `scripts/setup.ts`.

### Pages[​](https://blog.tigrisdata.com/build-data-rich-apps-nextjs-tigris-vercel#pages "Direct link to heading")

Let's take a look at `fetchListItems()` in this React component that loads and renders the to-do list items.

```
// Fetch Todo List
const fetchListItems = () => {
  setIsLoading(true);
  setIsError(false);

  fetch("/api/items")
    .then((response) => response.json())
    .then((data) => {
      setIsLoading(false);
      if (data.result) {
        setViewMode("list");
        setTodoList(data.result);
      } else {
        setIsError(true);
      }
    })
    .catch(() => {
      setIsLoading(false);
      setIsError(true);
    });
};
```

Evidently this React component is only rendering the items returned by `/api/items`.

Similarly, the `addTodoItem()`, to add a to-do list item, simply makes a *POST* request to `/api/items`.

```
// Add a new to-do item
const addToDoItem = () => {
  if (queryCheckWiggle()) {
    return;
  }
  setIsLoading(true);

  fetch("/api/items", {
    method: "POST",
    body: JSON.stringify({ text: textInput, completed: false }),
  }).then(() => {
    setIsLoading(false);
    setTextInput("");
    fetchListItems();
  });
};
```

We will now dive into the API routes to see how these are integrated with Tigris that is powering our application.

`TIGRIS AND SERVERLESS FUNCTIONS
All the API routes are deployed as Serverless Functions. Tigris is serverless itself and natively supports HTTP. This makes it a perfect fit for Serverless Functions.`

### API routes to find and add items[​](https://blog.tigrisdata.com/build-data-rich-apps-nextjs-tigris-vercel#api-routes-to-find-and-add-items "Direct link to heading")

All the Next.js API routes are defined under `/pages/api/`. We have three files: `/pages/api/items/index.ts`, `/pages/api/items/search.ts` and `/pages/api/item/[id].ts` that expose following endpoints:

-   `GET /api/items` to get an array of to-do items as `Array<TodoItem>`
-   `POST /api/items` to add an item to the list
-   `GET /api/items/search?q=query` to find and return items matching the given query
-   `GET /api/item/{id}` to fetch an item
-   `PUT /api/item/{id}` to update the given item
-   `DELETE /api/item/[id]` to delete an item

Let's look at the `/api/items` api that supports both `GET` and `POST` handlers.

**pages/api/items/index.ts**

```
import type { NextApiRequest, NextApiResponse } from 'next'
import { COLLECTION_NAME, TodoItem } from '../../../lib/schema'
import tigrisDb from '../../../lib/tigris'

type Response = {
  result?: Array<TodoItem>,
  error?: string
}

// GET /api/items -- gets items from collection
// POST /api/items {ToDoItem} -- inserts a new item to collection
export default async function handler (
  req: NextApiRequest,
  res: NextApiResponse<Response>
) {
  switch (req.method) {
    case 'GET':
      await handleGet(req, res)
      break
    case 'POST':
      await handlePost(req, res)
      break
    default:
      res.setHeader('Allow', ['GET', 'POST'])
      res.status(405).end(`Method ${req.method} Not Allowed`)
  }
}

...
```

`handleGet()` method is fetching and returning items from Tigris collection, let's take a look at its implementation. You will see how easy it is to fetch data from Tigris.

**pages/api/items/index.ts**

```
async function handleGet(req: NextApiRequest, res: NextApiResponse<Response>) {
  try {
    const itemsCollection = tigrisDb.getCollection<TodoItem>(COLLECTION_NAME);
    const cursor = itemsCollection.findMany();
    const items = await cursor.toArray();
    res.status(200).json({ result: items });
  } catch (err) {
    const error = err as Error;
    res.status(500).json({ error: error.message });
  }
}
```

The `itemsCollection.findMany()` function sends a query to Tigris and returns a cursor to fetch results from collection.

Let's look at `handlePost()` implementation that inserts a `TodoItem` in collection by using the `insertOne()` function.

**pages/api/items/index.ts**

```
async function handlePost(req: NextApiRequest, res: NextApiResponse<Response>) {
  try {
    const item = JSON.parse(req.body) as TodoItem;
    const itemsCollection = tigrisDb.getCollection<TodoItem>(COLLECTION_NAME);
    const inserted = await itemsCollection.insertOne(item);
    res.status(200).json({ result: [inserted] });
  } catch (err) {
    const error = err as Error;
    res.status(500).json({ error: error.message });
  }
}
```

### API route to search items[​](https://blog.tigrisdata.com/build-data-rich-apps-nextjs-tigris-vercel#api-route-to-search-items "Direct link to heading")

Tigris makes it really easy to implement search within your applications by providing an embedded search engine that makes all your data instantly searchable.

Let's take a look at the search handler to see how easy it is to add powerful real-time search functionality. The `itemsCollection.search` functions sends a search query to Tigris and fetches the documents that match the query.

`TIGRIS REAL-TIME SEARCH
Note how you did not have to setup Elasticsearch, or configure search indexes. It was all taken care for you automatically.`

**pages/api/items/search.ts**

```
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse<Data>
) {
  const query = req.query["q"];
  if (query === undefined) {
    res.status(400).json({ error: "No search query found in request" });
    return;
  }
  try {
    const itemsCollection = tigrisDb.getCollection<TodoItem>(COLLECTION_NAME);
    const searchRequest: SearchRequest<TodoItem> = { q: query as string };
    const searchResult = await itemsCollection.search(searchRequest);
    const items = new Array<TodoItem>();
    for (const hit of searchResult.hits) {
      items.push(hit.document);
    }
    res.status(200).json({ result: items });
  } catch (err) {
    const error = err as Error;
    res.status(500).json({ error: error.message });
  }
}
```

Summary[​](https://blog.tigrisdata.com/build-data-rich-apps-nextjs-tigris-vercel#summary "Direct link to heading")
------------------------------------------------------------------------------------------------------------------

In this tutorial, you deployed a to-do list Next.js app that uses Tigris as the backend. You saw all the powerful functionality that Tigris provides, and how easy it is to use it within Serverless Functions.

Tigris is the easiest way to work with data in your Next.js applications. Tigris and Next.js provide developers with the fastest way to build fast, data-rich and highly-responsive applications.

You can find the complete source code for this tutorial [GitHub repo](https://github.com/tigrisdata/tigris-vercel-starter), feel free to raise issues or contribute to the project.

Happy learning!

* * * * *

*Tigris is the data platform built for Next.js applications! Use it as a scalable, ACID transactional, real-time backend for your serverless applications. Build rich features with dynamic data without worrying about slow database queries or missing indexes. Seamlessly implement search within your applications with its embedded search engine. Connect serverless functions with its event streams to build highly responsive applications that scale automatically.*

[![Sign up for the beta](https://blog.tigrisdata.com/assets/images/hand-laptop-08df8ce107a739b1f0fb27f84b07b85b.png)](https://www.tigrisdata.com/beta#signup-form)

[Get early access](https://www.tigrisdata.com/beta#signup-form) and try out Tigris for your next Next.js application. Join our [Slack](https://join.slack.com/t/tigrisdatacommunity/shared_invite/zt-16fn5ogio-OjxJlgttJIV0ZDywcBItJQ) or [Discord](http://discord.tigrisdata.com/) community to ask any questions you might have.