TypeScript RESTful Web App
In this guide we will build a RESTful web application that demonstrates the following features of Tigris:
- Flexible Document Model
- Integrated Search
- Automatic Indexing
- Transactions
- Simple APIs
This tutorial would provide you with a starting point towards building a feature rich web application.
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
Global search
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
import { User } from "../models/user";
import { Product } from "../models/product";
New HTTP handler
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
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.
Next steps
We have some other examples to help you get started with Tigris: