Build a Jamstack app with Tigris

Ovais Tariq - Dec 5 '22 - - Dev Community

Jamstack Tigris App

Building a web application can be especially challenging when the developer has to set up and configure multiple tools, which might include a database, server, building tools, and storage servers. Setting up all of these can take several hours of development time, dragging out the time it takes for the product to go live.

The Jamstack was created to relieve developers of this burden, allowing them to focus on their code or business logic while developing flexible, scalable, high-performance, and maintainable web applications. In this tutorial, we'll learn how to build a Jamstack app with Tigris.

To follow along with this article, you'll need Docker and Node.js *≥*v14 installed on your machine. Let's get started!

What is Tigris

Tigris is an open source, modern, scalable backend for developing real-time websites and apps. With its zero-ops approach, Tigris frees developers to concentrate on their applications. Tigris is a data platform that allows developers to build real-time applications without having to deal with all of the tedious infrastructure management, allowing for more code-focused development and less data infrastructure.

It's important to keep in mind that at the time of writing, Tigris is in beta.

Why use Tigris?

Let's consider some of the reasons you might choose Tigris for your web applications. For one, it handles all deployment, configuration, security, monitoring, and maintenance tasks, as well as infrastructure components. It is an alternative to traditional data handling methods like query tuning, index management, and maintenance.

Every Tigris application includes a database by default, so there is no need to learn a new database language. Tigris maintains data control while avoiding vendor lock-in, and it eliminates data silos and sprawling data infrastructure.

Getting started with Tigris

With the above requirements met, let's get started by scaffolding a Tigris application with the command below:

mkdir tigris-app && cd tigris-app
npx create-tigris-app
Enter fullscreen mode Exit fullscreen mode

The command above will create a tigris-app folder and scaffold a new Tigris application with the folder structure below:

tigris-app
┣ src
 ┃ ┣ lib
 ┃ ┃ ┗ config.ts
 ┃ ┣ models
 ┃ ┃ ┗ user.ts
 ┃ ┣ app.ts
 ┃ ┗ index.ts
 ┣ package-lock.json
 ┣ package.json
 ┗ tsconfig.json
Enter fullscreen mode Exit fullscreen mode
  • index.ts: The entry-point for the generated application
  • app.ts: The application class that initializes the Tigris client
  • models/user.ts: Contains the data container and schema for the user collection. Here, all the schemas in the Tigris application are defined

You can modify the project structure however you prefer.

Starting Tigris with Docker

To run the Tigris server, run the command below to run the Docker image for Tigris:

docker run -d -p 8081:8081 tigrisdata/tigris-local
Enter fullscreen mode Exit fullscreen mode

The command above will run Tigris on localhost:8081. Now, run the application with the command below:

npm run start
Enter fullscreen mode Exit fullscreen mode

The command above will run the default operations configured in the project, but we'll change these later.

Create a RESTful web app

With our application created and running, let's modify the project to create a RESTful web application for a blog website. To get started, install Express with the command below:

npm install express @types/express
Enter fullscreen mode Exit fullscreen mode

Then, create a controllers folder in the src directory.

Create the model

Tigris allows you to declare data models as part of the application and then convert them to appropriate objects, like collections. We'll do that in the model's folder. Rename the models/user.ts file to blog.ts and replace the existing code with the code snippets below:

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

export interface Blog extends TigrisCollectionType {
    id?: number;
    title: string;
    content: string;
    description: string
}

export const blogSchema: TigrisSchema<Blog> = {
    id: {
        type: TigrisDataTypes.INT32,
        primary_key: {
            order: 1,
            autoGenerate: true,
        },
    },
    title: {
        type: TigrisDataTypes.STRING,
    },
    content: {
        type: TigrisDataTypes.STRING,
    },
    description: {
        type: TigrisDataTypes.STRING,
    },
};
Enter fullscreen mode Exit fullscreen mode

In the code snippet above, we imported TigrisCollectionType to define the interface type for the blogs we are creating, TigrisDataTypes to define the data types of the fields in our blog schema, and TigrisSchema to define the schema type.

Next, we created the blog schema and defined the fields. We have an id that is an auto-generated primary key, as well as a titlecontent, and a description for each blog.

Create the controllers

With the schema created, let's create the controllers of our application. To get started, create blog.ts and controller.ts files in the controllers directory. In the code snippet below, we'll define an interface to set up the routes for this application in the controller.ts file:

import express from "express";

export interface Controller {
    setupRoutes(app: express.Application);
}
Enter fullscreen mode Exit fullscreen mode

Then, in the controller/blog.ts file, we'll create the required imports, create a BlogController class, and define the required variables with the code below:

import express, { Request, Response, Router } from "express";
import { Collection, DB } from "@tigrisdata/core";
import { Blog } from "../models/blog";
import { Controller } from "./controller";

export class BlogController implements Controller {
   private readonly db: DB;
   private readonly blogs: Collection<Blog>;
   private readonly router: Router;
   private readonly path: string;

  constructor(db: DB, app: express.Application) {
    this.blogs = db.getCollection<Blog>("blogs");
    this.path = "/blogs";
    this.router = Router();
    this.db = db;
  }
}
Enter fullscreen mode Exit fullscreen mode

In the code snippet above, we imported Collection and DB from Tigris, the Blog schema, and the Controller interface. Then, we defined the global variables and assigned the appropriate values and method to them in our constructor method.

Now, let's add CRUD methods to the class with the code snippets below:

//...
  public createBlog = async (req: Request, res: Response) => {
        try {
            const newBlog = await this.blogs.insert(req.body);

            res.status(200).json({
                data: newBlog,
                message: "Blog Created!",
            });
        } catch (e) {
            res.status(500).json({
                message: "An error occured:" + e,
            });
        }
    }
    public getBlogs = async (req: Request, res: Response) => {
        try {
            const blogs = await this.blogs.findMany(req.body);
            res.status(200).json({
                data: blogs
            });
        } catch (e) {
            res.status(500).json({
                message: "An error occured:" + e,
            });
        }
    }

    public getBlog = async (req: Request, res: Response) => {
        try {
            const blog = await this.blogs.findOne({
                id: Number.parseInt(req.params.id),
            });
            if (!blog) {
                return res.status(404).json({
                    data: blog,
                    message: "Blog not found!",
                });
            }
            return res.status(200).json({
                data: blog
            });
        } catch (e) {
            res.status(500).json({
                message: "An error occured:" + e,
            });
        }
    };

    public updateBlog = async (req: Request,
        res: Response,) => {
        try {
            await this.blogs.update({ id: parseInt(req.params.id) }, req.body);
            res.status(200).json({
                message: "Blog Updated"
            });

        } catch (e) {
            res.status(500).json({
                message: "An error occured:" + e,
            });
        }
    }
    public deleteBlog = async (
        req: Request,
        res: Response,
    ) => {
        try {
            await this.blogs.delete({
                id: Number.parseInt(req.params.id),
            });
            res.status(200).json({
                message: "Blog deleted",
            });
        } catch (e) {
            res.status(500).json({
                message: "An error occured:" + e,
            });
        }
    };
  //...
Enter fullscreen mode Exit fullscreen mode

In the code snippet above, we used the blog's instance, which is the collection for our Blog schema we accessed through the db.getCollection function. This function provides us with the method we need to perform CRUD operations in our database.

Then, set up the routes for this method by adding the method below:

//...
   public setupRoutes(app: express.Application) {
        this.router.post(`${this.path}/`, this.createBlog);
        this.router.get(`${this.path}/`, this.getBlogs);
        this.router.get(`${this.path}/:id`, this.getBlog);
        this.router.put(`${this.path}/:id`, this.updateBlog);
        this.router.delete(`${this.path}/:id`, this.deleteBlog);
        app.use("/", this.router);
    }
//...
Enter fullscreen mode Exit fullscreen mode

In the code snippet above, we used the Express router, which is accessible through the router instance, to define the route's endpoints for each method in our BlogController. Lastly, call the setupRoutes method in the constructor method:

constructor(db: DB, app: express.Application){
   // ...
   this.setupRoutes(app);
   //...
}
Enter fullscreen mode Exit fullscreen mode

Configure the application

With the controllers and routes set up, let's configure the application, initialize Tigris, create a database for the application, and create a blog collection. To get started, delete the code in the app.ts file and replace it with the code snippets below:

import { DB, Tigris } from "@tigrisdata/core";
import { Blog, blogSchema } from "./models/blog";
import express from "express";
import { BlogController } from "./controllers/blog";

export class Application {
    private readonly tigris: Tigris;
    private db: DB
    private readonly app: express.Application;
    private readonly PORT: string | number;
    private readonly dbName: string;

    constructor(tigris: Tigris) {
        this.tigris = tigris
        this.app = express()
        this.PORT = 3000;
        this.dbName = 'tigris_blog'
        this.setup();
    }

    public async setup() {
        this.app.use(express.json());
        await this.initTigris();
    }

    public async initTigris() {
        //create a database
        this.db = await this.tigris.createDatabaseIfNotExists(this.dbName);
        console.log('database created successfully')

        //register collections
        await this.db.createOrUpdateCollection<Blog>('blogs', blogSchema);

        //setup controllers
        new BlogController(this.db, this.app);
    }
    public start() {
        this.app.listen(this.PORT, () => {
            console.log(`Server is running at ${this.PORT}`)
        })
    }
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we imported and defined the modules and global variables we need in this class. We create a setup method where we create the middleware of the application and initialize Tigris by calling the initTigris method, which will create a database and our blog collection. We also create a start method to run the application on port 3000, as defined by the PORT variable.

Now, let's modify the index.ts file to call the start method in our Application class, which will start running the server:

//.. app.start();
Enter fullscreen mode Exit fullscreen mode

Lastly, ensure that your Tigris client serverUrl, which was initialized in the lib/config file, is listening to localhost:8081, as shown in the code snippet below:

import {Tigris} from "@tigrisdata/core";

export class Config {
    public initializeTigrisClient(): Tigris {
        return new Tigris({
            serverUrl: "localhost:8081",
            insecureChannel: true,
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

Test the application

Now, let's test the application using cURL. Run the command below to create a new blog:

curl http://localhost:4000/blogs \
    -X POST \
    -H 'Content-Type: application/json' \
    -d '{
        "title": "Build a web app with Node.js",
        "content": "Content goes here",
        "description":"Description goes here"
      }'
Enter fullscreen mode Exit fullscreen mode

You should get a response as shown in the screenshot below:

Test Application Curl Result

Conclusion

In this tutorial, we've learned how to build a Jamstack application with Tigris. We started by introducing Tigris and discussing why you should consider using it. Then, as a demonstration, we built a RESTful web application to manage a blog.

Tigris is an exciting tool, and I recommend checking out the Tigris documentation to learn more. I hope you enjoyed this article, and happy coding!

Author: Ekekenta Odionyenfe

. . . . . . . . . . . . . . . . . . . . . .