Using Turso DB with NextJS

brightonmboya

Brighton Mboya

Posted on May 15, 2023

Using Turso DB with NextJS

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, and the source code is available here.

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. After that, we will start a new nextJS project. I prefer starting my next app using the Create-T3-App 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.

npm create t3-app@latest
Enter fullscreen mode Exit fullscreen mode

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.

turso auth signup
Enter fullscreen mode Exit fullscreen mode

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.

turso db create [DATABASE-NAME]
Enter fullscreen mode Exit fullscreen mode

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.

turso db shell [DATABASE-NAME]
Enter fullscreen mode Exit fullscreen mode

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.

-- 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
Enter fullscreen mode Exit fullscreen mode

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.

url=YOUR_DATABASE_URL
authToken=YOUR_AUTH_TOKEN
Enter fullscreen mode Exit fullscreen mode

To get the URL, run this command

turso db show [DATABASE-NAME] --url
Enter fullscreen mode Exit fullscreen mode

Then create the authentication token,

turso db show [DATABASE-NAME] --url
Enter fullscreen mode Exit fullscreen mode

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

npm install @libsql/client
Enter fullscreen mode Exit fullscreen mode

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.

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

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

    return createClient(config);
}
Enter fullscreen mode Exit fullscreen mode

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.

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;
Enter fullscreen mode Exit fullscreen mode

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.

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;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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.

💖 💪 🙅 🚩
brightonmboya
Brighton Mboya

Posted on May 15, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related