Skip to main content

TypeScript RESTful Web App

In this guide we will build a RESTful web application that demonstrates the following features of Tigris:

  • Flexible Document Model
  • Global Search
  • Automatic Indexing
  • Event Streaming
  • Transactions
  • Simple APIs

This tutorial would provide you with a starting point towards building a feature rich web application.

info

The application uses the Express framework.

Now let's get started with Tigris.

Getting Started

Clone the application code repository

git clone https://github.com/tigrisdata/tigris-starter-ts.git

And navigate to the directory created

cd tigris-starter-ts

Startup Tigris local development environment

Install Tigris CLI

Use the command below to install the CLI on macOS

brew install tigrisdata/tigris/tigris-cli

Use the command below to install the CLI on linux

curl -sSL https://tigris.dev/cli-linux | sudo tar -xz -C /usr/local/bin

Alternative ways of installation can be found here.

Start Tigris development environment

tigris dev start

Once this command has completed, Tigris will be available on port 8081.

Starting the application

Build the project

The next step is to build project

npm run build

Run the application

npm run dev

Using the application

Event streaming

Every time a new user is created we publish a new event. Take a look at the code inside src/controllers/user-controller.ts.

Let's subscribe to this event stream. Open a new terminal window and run the following command

curl -N -X POST localhost:8080/users/subscribe

Keep this terminal window open so that you can watch the events live. Use a separate terminal window for the remainder of the tutorial.

CRUD operations

Tigris allows you to quickly add data and easily retrieve or edit that data through simple and intuitive APIs. Continue using your preferred programming language, there is no new database query language to learn.

Take a look at the source files inside src/controllers to see how Tigris CRUD APIs are used.

Let's create user and product records, and then read them back.

Insert users

Run following commands to create two user records

curl localhost:8080/users/create \
-X POST \
-H 'Content-Type: application/json' \
-d '{
"name":"John",
"balance":100
}'
curl localhost:8080/users/create \
-X POST \
-H 'Content-Type: application/json' \
-d '{
"name":"Jane",
"balance":200
}'

Now if you go back to the terminal window where you ran the curl .... subscribe command you will notice two events displayed.

Insert products

Run the following commands to insert some product records

curl localhost:8080/products/create \
-X POST \
-H 'Content-Type: application/json' \
-d '{
"name":"Avocado",
"price":10,
"quantity":5
}'
curl localhost:8080/products/create \
-X POST \
-H 'Content-Type: application/json' \
-d '{
"name":"Avocado Oil",
"price":80,
"quantity":15
}'
curl localhost:8080/products/create \
-X POST \
-H 'Content-Type: application/json' \
-d '{
"name":"Gold",
"price":3000,
"quantity":1
}'

Read the records

Now go ahead and confirm that the data has been persisted

curl http://localhost:8080/users
curl http://localhost:8080/products

Tigris comes with an integrated search engine that eliminates the need to run a separate search platform and synchronize data.

Let's search for users named "Jane"

curl http://localhost:8080/users/search \
-X POST \
-H 'Content-Type: application/json' \
-d '{"q": "jane"}'

Or, search for products named "avocado" and price less than 50

curl localhost:8080/products/search \
-X POST \
-H 'Content-Type: application/json' \
-d '{
"q": "avocado",
"searchFields": ["name"],
"filter": {"price": {"$lt": 50}}
}'

Extending the application

One of the main features of Tigris is the ability to perform ACID transactions. Now let's set up a HTTP handler that will perform the insert and update operations in a transaction ensuring that the collections are consistently updated.

Open the project in your favorite IDE and add the following to src/controllers/order-controller.ts

Additional imports

order-controller.ts
import { User } from "../models/user";
import { Product } from "../models/product";

New HTTP handler

order-controller.ts
public createOrder = async (req: Request, res: Response) => {
this.db.transact(async tx => {
// get user
const user: (User | undefined) = await this.db.getCollection<User>('users').findOne({
userId: req.params.userId
}, tx);
if (user === undefined) {
res.status(404).json({error: 'User not found'});
return;
}

// get product
const product: (Product | undefined) = await this.db.getCollection<Product>('products').findOne({
productId: req.params.productId
},
tx
);
if (product === undefined) {
res.status(404).json({error: 'Product not found'});
return;
}

// read quantity for order
const qty: number = Number.parseInt(req.params.quantity);

// check quantity available
if (qty > product.quantity) {
res.status(412).json({error: 'Insufficient product quantity'});
return;
}
const orderTotal: number = qty * product.price;

// check balance available
if (user.balance < orderTotal) {
res.status(412).json({error: 'Insufficient user balance'});
return;
}

// deduct balance
await this.db.getCollection<User>('users').update(
{
userId: user.userId
},
{
balance: user.balance - orderTotal
},
tx
);
console.log('deducted user balance');

// deduct product quantity
await this.db.getCollection<Product>('products').update(
{
productId: product.productId
},
{
quantity: product.quantity - qty
},
tx
);
// create order
const order: Order = {
orderTotal: orderTotal,
userId: user.userId,
productItems: [
{
productId: product.productId,
quantity: qty
}
]
};

await this.orders.insert(order, tx);
console.log('order created');

res.status(200).json({status: 'Order placed successfully'});
});
};

Configure HTTP route

Add the following new route to the OrderController.setupRoutes function

order-controller.ts
this.router.post(`${this.path}/:userId/:productId/:quantity`, this.createOrder);

Now that we have set up the new HTTP route, let's use it to create a new order record.

Insert an order

Replace {user-id}, and {product-id} below with IDs of the records you see when running the commands in the Read the records section.

curl -X 'POST' 'http://localhost:8080/orders/{user-id}/{product-id}/4'

Read all order records

Now go ahead and confirm that the order record has been persisted

curl http://localhost:8080/orders

Understanding what just happened

When you launched the application, code located in App.initializeTigris created a database named tigris_starter_ts. It then created the collections for storing the documents users, products, orders, and user_events for storing the events. All of this was done instantaneously in true serverless fashion.

The model definitions for these collections are located inside src/models/.

You also leveraged the event streaming and search features through code without having to setup any complicated infrastructure.