Querying Data from a Next.js App

Welcome to part 2 of our quickstart guide! Previously, we covered the basics. If you haven't already been there, we'd recommend going there first and then coming to this page. If you're coming from there, let's build this app!


The sample app we're building is a Node.js-based app, so we assume you've got global commands like npm and node. If you don't, go ahead and install Node.js and we'll get started.

If you'd rather not install Node.js, it's not that important. The concepts of querying Xata are transferrable across languages. You may not be able to follow along with JavaScript/TypeScript, but The Xata RESTful API is designed to be easily accessible from any programming language. We have a similar guide on querying Xata purely with our REST API that you might find interesting.

Source Code

This todo application is open source, and all of its code is available on GitHub. If you'd like to run it locally, you'll need to add a Xata API key in an .env file at its root after you clone it.

Bootstrapping our Application

Okay! Let's start building a web app on Xata! To do this, we'll start at the beginning. Let's run these commands in a fresh new folder on our machines:

# Initialize an npm project with default options
npm init --yes

# Install dependencies
npm install react react-dom next

# Install devDependencies
npm install typescript @types/node @types/react -D

You could also run npx create-next-app --typescript to achieve the same effect.

⚠️ We're using TypeScript because we like it: it makes our applications predictable and safe, but everything we do with TypeScript can easily be done in JavaScript depending on your preference.

Now, let's get something on the screen by creating a pages directory and putting an index.tsx in there.

mkdir pages
touch pages/index.tsx

Great! Now, let's add a basic layout to our index page:

// ./pages/index.tsx

const Index = () => {
  return (
      <h1>My To-do List</h1>
            <input type="checkbox" />
            Buy oat milk

export default Index;

With this, we can comfortably start a development server by running the following command:

npx next dev

This will start a development server on http://localhost:3000 assuming port 3000 is free, and show us a very simple list of items to do.

Adding Xata to our Application

Great! We've got a basic app up and running. Let's connect it to Xata. We recommend using the Xata Software Development Kit (SDK) to work with Xata. Let's set it up using the Xata Command Line Interface (CLI). To begin, we'll install the Xata CLI globally:

# Install the Xata CLI globally
npm i -g @xata.io/cli

Now that this is installed, we have a global xata command that we can invoke from anywhere. This is why we recommend installing the CLI globally. It also works via npx, but this can be a bit more cumbersome.

Now that we've got the CLI, let's tell it who we are: let's login.

xata auth login

Running this command will present us with 2 choices:

  • create a new existing API key by opening a browser, or
  • paste in an existing one.

Since this is the quickstart, we'll create a new one. To learn more about API keys, see the API keys page. To learn more about authenticating with the CLI, see the CLI docs.

After we create an API key, we can close the browser window and come back to Xata. Now, the CLI knows who we are. Let's initialize a project. We can do this by running the following command:

xata init

This will launch a little questionnaire that will help us configure our project. Let's answer the questions, and use code generation. This is the way we recommend working with Xata for maximum safety and efficiency.

Once we're done, our project will be setup to query Xata. If your Next.js development server is running, now's a good time to kill it and start it again because the Xata CLI added your API key to .env. Next.js reads this when your development server starts, so we'll need to start it again since .env has changed.

Great! Now, let's query Xata!

Querying Xata

We always recommend querying Xata from a secure environment, like a serverless function, to hide your API keys from users. It is currently unsafe to query Xata from a browser, because you risk leaking your API key. We plan to address this soon.

To query Xata from our Next.js app, we will use getServerSideProps. This is a function that will be called by Next.js when it renders our page. We will also import our generated XataClient and use it here.

// ./pages/index.tsx

+import { FC } from 'react';
+import { XataClient } from '../util/xata';

+type Props = Awaited<ReturnType<typeof getServerSideProps>>['props'];

-const Index = () => {
+const Index: FC<Props> = ({ todos }) => {
  return (
      <h1>My To-do List</h1>
-          <label>
-            <input type="checkbox" />
-            Buy oat milk
+          {todos.map(t => <label key={t.id}>
+            <input type="checkbox" checked={t.is_done} />
+            {t.label}

+const xata = new XataClient();

+export const getServerSideProps = async () => {
+  const todos = await xata.db.items.getMany();
+  return { props: { todos } };

export default Index;

Let's talk about what just happened.

First, we imported our generated XataClient that we got from the CLI:

import { XataClient } from "../util/xata";

Then, we instantiated a new XataClient:

const xata = new XataClient();

Later, we used this in our getServerSideProps function to get to-do items from Xata:

export const getServerSideProps = async () => {
  const todos = await xata.db.items.getMany();
  return { props: { todos } };

Note that xata.db.[anything] automatically knows the design of your database and provides you autocompletion hints. This is why we recommend code generation. It makes it easy to write predictable queries that will work with your database.

Methods to query the database

You'll notice that our call to Xata is done with the SDK's getMany method. There are multiple methods to run the query and get the items from your database.

  • getFirst: Will return the first record in the table query results.
  • getMany: Will return a subset of records in the table query results in an array. It returns the default pagination size, and you can customise the number of items returned with a different size (xata.db.items.getMany({ pagination: { size: 100 }})).
  • getAll: Will return every item in your table query results by getting all the pages and joining them in an array. If your query returns a lot of items, it might affect performance.
  • getPaginated: Will return the first page of the table query results and allow you to paginate programmatically the rest of the pages.


It is likely that in your application you want to paginate the results and allow the user to navigate between pages.

const page = xata.db.items.getPaginated({ pagination: { size: 100 } });
page.records; // Array of items in the page

if (page.hasNextPage()) {
  const secondPage = await page.nextPage();

For more information on the SDK's pagination features, see its reference.

Great, now we know how to fine-tune the results we receive from Xata. In the snippet above, we also did some TypeScript stuff with FC<Props> and Awaited<ReturnType<typeof getServerSideProps>>. This is not really essential to understanding Xata, so we'll skip talking about it here. If you're curious, feel free to join our Discord server and we'll be happy to talk about these things there.


That's it! Now, our Next.js app is actively making a query to Xata and rendering to-do list items from our database! How can we create new ones though? Or mark existing ones as done? Maybe delete some? That's what we'll explore in the next steps! Let's go!

Last modified 1 mo11 days ago