Using Turso DB with NextJS
Tags: turso, edge
Published: 2023-05-15 09:33:39

Turso is the new database that allows you to deploy your database easily to the edge. It solves the co-location problem by replicating the database to different regions, and the replicas are always in sync with one primary. Turso is built on top of libSQL, an open-source, open-contribution of SQLite.

We will build a popular tourist destination app showing a list of popular tourist destinations. Check out the [live link](https://app-turso-next.vercel.app/), and the source code is available [here](https://github.com/BrightonMboya/app-turso-next/tree/main).

Using Turso with NextJS is quite easy, as the way you could use any other database. First things first, we need to install the Turso CLI. You can follow the instructions on installing the CLI from the documentation [here](https://docs.turso.tech/reference/turso-cli#installation). After that, we will start a new nextJS project. I prefer starting my next app using the [Create-T3-App](https://create.t3.gg/) command, but you can still use the official nextJS installation command. Select TypeScript as the language of choice, and slam Yes when prompted to use Tailwind, coz why not.

```bash
npm create t3-app@latest
```
After installing the Turso CLI, we need to login. Turso will open up a new browser window where you will require to grant the Github Turso app some permissions to your account.

```bash
turso auth signup
```

After the login process completes, the CLI receives a token that identifies you and stores it in a local file. On macOS, the file is located in `$HOME/Library/Application` Support/turso. On Linux, it's `$HOME/.config/turso`. Some things to note about this token:

- Do not share this token with anyone you don't trust fully; they can use it to work with Turso on your behalf.

- The token is passed along with all backend API requests made by the CLI while logged in.

- You can print it using the command. `turso auth token`.

- It expires after 7 days. After that, you must log in again with turso auth login.

After the auth part, it is time to create our database and seed some data into it. You can create the database using the turso db create command.

```bash
turso db create [DATABASE-NAME]
```

This will create a new database that is located geographically closer to you. Then start interacting with the database via the shell command using this command.

```bash
turso db shell [DATABASE-NAME]
```

This will open a SQL shell where you can interact with the database you just created. Paste this command which will create the popular_destinations table, and from there, it will seed the database with some data.


```sql
-- create the popular destinations table
CREATE TABLE popular_destinations (
    id SERIAL PRIMARY KEY,
    country VARCHAR(255) NOT NULL,
    name VARCHAR(255) NOT NULL,
    location VARCHAR(255) NOT NULL,
    image_url VARCHAR(255) NOT NULL
);

-- Seeding up the database
INSERT INTO popular_destinations (country, name, location, image_url)
VALUES
    ('United States', 'New York City', 'New York', 'https://images.unsplash.com/photo-1485871981521-5b1fd3805eee?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxzZWFyY2h8M3x8bmV3JTIweW9ya3xlbnwwfDB8MHx8&auto=format&fit=crop&w=500&q=60');
INSERT INTO popular_destinations (country, name, location, image_url)
VALUES
    ('France', 'Paris', 'Ile-de-France', 'https://images.unsplash.com/photo-1502602898657-3e91760cbb34?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxzZWFyY2h8M3x8cGFyaXN8ZW58MHwwfDB8fA%3D%3D&auto=format&fit=crop&w=500&q=60');
INSERT INTO popular_destinations (country, name, location, image_url)
VALUES
    ('Japan', 'Tokyo', 'Kanto Region', 'https://images.unsplash.com/photo-1578469645742-46cae010e5d4?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NXx8a3lvdG98ZW58MHwwfDB8fA%3D%3D&auto=format&fit=crop&w=500&q=60');
INSERT INTO popular_destinations (country, name, location, image_url)
VALUES
    ('Australia', 'Sydney', 'New South Wales', 'https://images.unsplash.com/photo-1526958977630-bc61b30a2009?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=870&q=80');

INSERT INTO popular_destinations (country, name, location, image_url)
VALUES
('London', 'UK', 'United Kingdom', 'https://images.unsplash.com/photo-1513635269975-59663e0ac1ad?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8bG9uZG9ufGVufDB8fDB8fA%3D%3D&auto=format&fit=crop&w=500&q=60');


-- Quitting the shell
.quit
```

Now we need to get the database URL and the authentication token of the database and configure it on the .env file. At the root of the project, create a .env file and paste these variable names.

```bash
url=YOUR_DATABASE_URL
authToken=YOUR_AUTH_TOKEN
```

To get the URL, run this command

```bash
turso db show [DATABASE-NAME] --url
```

Then create the authentication token,

```bash
turso db show [DATABASE-NAME] --url
```

Lastly, we need to install a package called `libsql/client` which will allow us to interact with the Turso database in our application.

```bash
npm install @libsql/client
```

Now enough of the terminal thing, and let's write some code. Start the dev server by running `npm run dev` and open up `localhost:3000`.

At the src folder, create a folder named db, and create a turso.ts file. Paste this code in the file, this code imports the createClient from libsql/client and configures it so that we can make SQL queries to our database easily.


```typescript
import { createClient } from "@libsql/client/web";

export default function Turso() {
    const config = {
        url: process.env.url,
        authToken: process.env.authToken,
    }

    return createClient(config);
}
```

Then create another folder called hooks and create a file called `useFetch.ts` this file will allow us to make queries to our database and will return as a loading and error state as well as the data that we will receive from the database.

```typescript
import { useEffect, useState } from "react";
import axios from "axios";
import type { QueryResult } from "../pages";

export type fetchResponse = {
    data: QueryResult[];
    loading: boolean;
    error: string;
};


const useFetch = (url: string): fetchResponse => {
    const [data, setdata] = useState<QueryResult[]>(null);
    const [loading, setloading] = useState<boolean>(true);
    const [error, seterror] = useState<string>("");
    useEffect(() => {
        axios.get(url).then((res): void => {
            setdata(res.data as QueryResult[]);
            setloading(false);
        })
            .catch((err: unknown) => {
                if (err instanceof Error) {
                    seterror(err.message);
                    setloading(false);
                }
            });
    }, [url]);
    return { data, loading, error };
};

export default useFetch;
```

Then add a file in the `src/api` folder called `turso.ts` , [pardon my file naming skills]. This file will include a query that queries the popular_destination table and return the data.

```typescript
import Turso from '../../db/turso';
import type { NextApiRequest, NextApiResponse } from 'next';

export default async function Handler(
    req: NextApiRequest,
    res: NextApiResponse
) {
    try {
        const client = Turso();
        const response = await client.execute("SELECT * FROM popular_destinations;");
        const posts = response.rows.map((post) => {
            return {
                id: post.id,
                country: post.country,
                name: post.name,
                location: post.location,
                image_url: post.image_url,
            }
        })

        res.status(200).json(posts);


    } catch (error: unknown) {
        console.log(error);
        if (error instanceof Error) {
            res.status(500).json({ message: error.message });
            return;
        }
    }
}
```

Now finally, on the `index.tsx` page. Here we will create a card component and style it with some tailwind. Then we will fetch the data using our useFetch hook and style the results using grid.

```typescript
import { useState } from "react";
import Image from "next/image";
import useFetch from "../hooks/useFetch";
import type { fetchResponse } from "../hooks/useFetch";

export type QueryResult = {
  id: number | null;
  country: string;
  name: string;
  location: string;
  image_url: string;
};

function cn(...classes: string[]) {
  return classes.filter(Boolean).join(" ");
}

export default function MyPage() {
  const [isLoading, setLoading] = useState(true);
  const { data, loading, error }: fetchResponse = useFetch("/api/turso");

  return (
    <main className="font-montserrat flex items-center justify-center">
      <section>
        <h3 className="text-2xl font-medium ">Some Fancy Destinations</h3>
        <p className="text-base">
          List of Vacation Homes to Enjoy once in a while
        </p>

        {loading && <p>Loading...</p>}
        {error && <p>Error countered while fetching: {error}</p>}
        <div className="flex flex-col items-center justify-center gap-5 md:grid md:grid-cols-2 lg:grid-cols-3">
          {data?.map((item: QueryResult) => (
            <div
              className="w-[300px] relative flex flex-col items-center justify-center p-4 m-4 bg-white rounded-xl shadow-md hover:shadow-lg transition duration-500 ease-in-out transform hover:-translate-y-1 hover:scale-110"
              key={item.image_url}
            >
              <Image
                src={item.image_url}
                alt="Picture of the author"
                width={350}
                height={200}
                priority={false}
                onLoadingComplete={() => setLoading(false)}
                draggable={false}
                className={cn(
                  "object-cover object-top duration-700 ease-in-out border-[1px]",

                  isLoading
                    ? "scale-110 blur-2xl grayscale bg-blue-300 "
                    : "scale-100 blur-0 grayscale-0"
                )}
              />

              <h3 className="text-lg ">
                {item.name}, {item.location}
              </h3>
            </div>
          ))}
        </div>
      </section>
    </main>
  );
}
```

And that's it; you have created the Turso database, seeded the database, and queried it to your front-end application.

If you enjoy this, plz give it a thumbs up and check me out on [Twitter](https://www.twitter.com/brightonMboya).