Data Service Quickstart
Get up and running with the Taruvi Data Service in minutes. This guide walks you through creating a complete blog application from scratch.
What You'll Build
A simple blog with:
- 👤 Users: Author accounts
- 📝 Posts: Blog articles
- 💬 Comments: User comments on posts
- 🏷️ Tags: Post categorization
Prerequisites
- ✅ Taruvi account with API access
- ✅ JWT authentication token
- ✅ Basic understanding of REST APIs
- ✅ curl, Postman, or similar HTTP client
Step 1: Create Your App
First, create an app to hold your blog data:
curl -X POST https://your-domain.com/api/apps/ \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "My Blog",
"description": "A simple blogging platform"
}'
Response:
{
"id": 1,
"name": "My Blog",
"slug": "my-blog",
"description": "A simple blogging platform",
"created_at": "2024-01-15T10:30:00Z"
}
Save the slug value (my-blog) - you'll use it in all future requests.
Step 2: Import Your Schema
Create a file called blog-schema.json with your complete database schema:
{
"name": "blog-schema",
"title": "Blog Database Schema",
"description": "Complete blog application schema",
"resources": [
{
"name": "users",
"title": "User Accounts",
"schema": {
"fields": [
{
"name": "id",
"type": "integer",
"constraints": {"required": true}
},
{
"name": "username",
"type": "string",
"constraints": {"required": true, "maxLength": 50}
},
{
"name": "email",
"type": "string",
"format": "email",
"constraints": {"required": true}
},
{
"name": "bio",
"type": "string"
},
{
"name": "created_at",
"type": "datetime"
}
],
"primaryKey": ["id"]
}
},
{
"name": "posts",
"title": "Blog Posts",
"schema": {
"fields": [
{
"name": "id",
"type": "integer",
"constraints": {"required": true}
},
{
"name": "author_id",
"type": "integer",
"constraints": {"required": true}
},
{
"name": "title",
"type": "string",
"constraints": {"required": true, "maxLength": 200}
},
{
"name": "content",
"type": "string",
"constraints": {"required": true}
},
{
"name": "excerpt",
"type": "string"
},
{
"name": "published_at",
"type": "datetime"
},
{
"name": "status",
"type": "string",
"constraints": {
"required": true,
"enum": ["draft", "published", "archived"]
}
}
],
"primaryKey": ["id"],
"foreignKeys": [
{
"fields": ["author_id"],
"reference": {
"resource": "users",
"fields": ["id"]
}
}
]
}
},
{
"name": "comments",
"title": "Post Comments",
"schema": {
"fields": [
{
"name": "id",
"type": "integer",
"constraints": {"required": true}
},
{
"name": "post_id",
"type": "integer",
"constraints": {"required": true}
},
{
"name": "user_id",
"type": "integer",
"constraints": {"required": true}
},
{
"name": "content",
"type": "string",
"constraints": {"required": true}
},
{
"name": "created_at",
"type": "datetime"
}
],
"primaryKey": ["id"],
"foreignKeys": [
{
"fields": ["post_id"],
"reference": {
"resource": "posts",
"fields": ["id"]
}
},
{
"fields": ["user_id"],
"reference": {
"resource": "users",
"fields": ["id"]
}
}
]
}
},
{
"name": "tags",
"title": "Post Tags",
"schema": {
"fields": [
{
"name": "id",
"type": "integer",
"constraints": {"required": true}
},
{
"name": "name",
"type": "string",
"constraints": {"required": true, "maxLength": 50}
},
{
"name": "slug",
"type": "string",
"constraints": {"required": true, "maxLength": 50}
}
],
"primaryKey": ["id"]
}
},
{
"name": "post_tags",
"title": "Post-Tag Relationships",
"schema": {
"fields": [
{
"name": "post_id",
"type": "integer",
"constraints": {"required": true}
},
{
"name": "tag_id",
"type": "integer",
"constraints": {"required": true}
}
],
"primaryKey": ["post_id", "tag_id"],
"foreignKeys": [
{
"fields": ["post_id"],
"reference": {
"resource": "posts",
"fields": ["id"]
}
},
{
"fields": ["tag_id"],
"reference": {
"resource": "tags",
"fields": ["id"]
}
}
]
}
}
]
}
Now import it with automatic table creation:
curl -X POST "https://your-domain.com/api/apps/my-blog/datatables/create_schema/?materialize=true" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d @blog-schema.json
Response:
{
"created_count": 5,
"error_count": 0,
"created_tables": [
{"id": 1, "name": "users", "is_materialized": true, "field_count": 5},
{"id": 2, "name": "posts", "is_materialized": true, "field_count": 7},
{"id": 3, "name": "comments", "is_materialized": true, "field_count": 5},
{"id": 4, "name": "tags", "is_materialized": true, "field_count": 3},
{"id": 5, "name": "post_tags", "is_materialized": true, "field_count": 2}
],
"errors": [],
"materialized": true
}
✅ All 5 tables created and ready to use!
Step 3: Create Some Users
curl -X POST https://your-domain.com/api/apps/my-blog/datatables/users/data/ \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{
"id": 1,
"username": "john_doe",
"email": "[email protected]",
"bio": "Software developer and tech blogger",
"created_at": "2024-01-15T10:00:00Z"
},
{
"id": 2,
"username": "jane_smith",
"email": "[email protected]",
"bio": "Product designer and UX enthusiast",
"created_at": "2024-01-16T09:30:00Z"
}
]'
Step 4: Create Blog Posts
curl -X POST https://your-domain.com/api/apps/my-blog/datatables/posts/data/ \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{
"id": 1,
"author_id": 1,
"title": "Getting Started with Taruvi",
"content": "In this post, we will explore the powerful features of Taruvi...",
"excerpt": "Learn the basics of Taruvi Data Service",
"status": "published",
"published_at": "2024-01-15T12:00:00Z"
},
{
"id": 2,
"author_id": 1,
"title": "Advanced Querying Techniques",
"content": "Taruvi provides powerful query capabilities including...",
"excerpt": "Master advanced filtering and population",
"status": "published",
"published_at": "2024-01-16T14:00:00Z"
},
{
"id": 3,
"author_id": 2,
"title": "Designing Data-Driven UIs",
"content": "When building interfaces powered by Taruvi...",
"excerpt": "Best practices for UI design",
"status": "draft",
"published_at": null
}
]'
Step 5: Add Some Comments
curl -X POST https://your-domain.com/api/apps/my-blog/datatables/comments/data/ \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{
"id": 1,
"post_id": 1,
"user_id": 2,
"content": "Great introduction! Very helpful for beginners.",
"created_at": "2024-01-15T14:20:00Z"
},
{
"id": 2,
"post_id": 1,
"user_id": 1,
"content": "Thanks! Glad you found it useful.",
"created_at": "2024-01-15T15:30:00Z"
},
{
"id": 3,
"post_id": 2,
"user_id": 2,
"content": "The populate feature is amazing!",
"created_at": "2024-01-16T16:00:00Z"
}
]'
Step 6: Query Your Data
List All Published Posts
curl "https://your-domain.com/api/apps/my-blog/datatables/posts/data/?status=published" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Get Posts with Authors
curl "https://your-domain.com/api/apps/my-blog/datatables/posts/data/?populate=author" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Response:
{
"data": [
{
"id": 1,
"author_id": 1,
"title": "Getting Started with Taruvi",
"status": "published",
"author": {
"id": 1,
"username": "john_doe",
"email": "[email protected]",
"bio": "Software developer and tech blogger"
}
},
{
"id": 2,
"author_id": 1,
"title": "Advanced Querying Techniques",
"status": "published",
"author": {
"id": 1,
"username": "john_doe",
"email": "[email protected]",
"bio": "Software developer and tech blogger"
}
}
],
"total": 2
}
Get Single Post with Full Details
curl "https://your-domain.com/api/apps/my-blog/datatables/posts/data/1/?populate=author,comments.user" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Response:
{
"data": {
"id": 1,
"title": "Getting Started with Taruvi",
"content": "In this post, we will explore...",
"excerpt": "Learn the basics of Taruvi Data Service",
"status": "published",
"published_at": "2024-01-15T12:00:00Z",
"author": {
"id": 1,
"username": "john_doe",
"email": "[email protected]",
"bio": "Software developer and tech blogger"
},
"comments": [
{
"id": 1,
"content": "Great introduction! Very helpful for beginners.",
"created_at": "2024-01-15T14:20:00Z",
"user": {
"id": 2,
"username": "jane_smith",
"email": "[email protected]"
}
},
{
"id": 2,
"content": "Thanks! Glad you found it useful.",
"created_at": "2024-01-15T15:30:00Z",
"user": {
"id": 1,
"username": "john_doe",
"email": "[email protected]"
}
}
]
}
}
Search Posts
curl "https://your-domain.com/api/apps/my-blog/datatables/posts/data/?title_contains=taruvi&status=published" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Get User's Posts
curl "https://your-domain.com/api/apps/my-blog/datatables/posts/data/?author_id=1&_sort=published_at&_order=desc" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Paginate Results
curl "https://your-domain.com/api/apps/my-blog/datatables/posts/data/?status=published&_start=0&_end=10" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Step 7: Update Data
Update a Post
curl -X PATCH https://your-domain.com/api/apps/my-blog/datatables/posts/data/3/ \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"status": "published",
"published_at": "2024-01-17T10:00:00Z"
}'
Update a User's Bio
curl -X PATCH https://your-domain.com/api/apps/my-blog/datatables/users/data/1/ \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"bio": "Senior software developer and technical writer"
}'
Step 8: Delete Data
Delete a Comment
curl -X DELETE https://your-domain.com/api/apps/my-blog/datatables/comments/data/3/ \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Step 9: Aggregation Queries
Use aggregations for analytics and reporting without fetching all data:
Count Posts by Author
curl "https://your-domain.com/api/apps/my-blog/datatables/posts/data/?_aggregate=count(*)&_group_by=author_id&populate=author" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Response:
{
"data": [
{
"author_id": 1,
"author": {
"id": 1,
"username": "john_doe",
"email": "[email protected]"
},
"count": 2
},
{
"author_id": 2,
"author": {
"id": 2,
"username": "jane_smith",
"email": "[email protected]"
},
"count": 1
}
],
"total": 2
}
Posts Per Day Statistics
curl "https://your-domain.com/api/apps/my-blog/datatables/posts/data/?_aggregate=count(*)&_group_by=date_trunc('day',published_at)&status=published" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Average Comments Per Post
curl "https://your-domain.com/api/apps/my-blog/datatables/comments/data/?_aggregate=count(*),avg(id)&_group_by=post_id" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Total Stats
curl "https://your-domain.com/api/apps/my-blog/datatables/posts/data/?_aggregate=count(*),count(distinct author_id)" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Response:
{
"data": [],
"aggregates": {
"count": 3,
"count_distinct_author_id": 2
}
}
See Aggregations Guide for advanced GROUP BY, HAVING, and more aggregate functions.
Step 10: Bulk Operations
Efficiently create, update, or delete multiple records at once:
Bulk Create Tags
curl -X POST https://your-domain.com/api/apps/my-blog/datatables/tags/data/ \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{"id": 1, "name": "Python", "slug": "python"},
{"id": 2, "name": "Django", "slug": "django"},
{"id": 3, "name": "JavaScript", "slug": "javascript"},
{"id": 4, "name": "Tutorial", "slug": "tutorial"},
{"id": 5, "name": "Advanced", "slug": "advanced"}
]'
Response:
{
"data": [
{"id": 1, "name": "Python", "slug": "python"},
{"id": 2, "name": "Django", "slug": "django"},
{"id": 3, "name": "JavaScript", "slug": "javascript"},
{"id": 4, "name": "Tutorial", "slug": "tutorial"},
{"id": 5, "name": "Advanced", "slug": "advanced"}
]
}
Bulk Update Post Status
Update multiple posts at once:
curl -X PATCH https://your-domain.com/api/apps/my-blog/datatables/posts/data/ \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{"id": 1, "status": "archived"},
{"id": 2, "status": "archived"}
]'
Upsert (Insert or Update)
Create new records or update existing ones based on unique fields:
curl -X POST "https://your-domain.com/api/apps/my-blog/datatables/tags/data/upsert/?unique_fields=slug" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{"name": "Python Programming", "slug": "python"},
{"name": "React", "slug": "react"},
{"name": "TypeScript", "slug": "typescript"}
]'
If a tag with slug="python" exists, it updates the name. Otherwise, it creates a new tag.
Bulk Delete by Filter
Delete all draft posts older than 30 days:
curl -X DELETE "https://your-domain.com/api/apps/my-blog/datatables/posts/data/?filter={\"status\":\"draft\",\"created_at__lt\":\"2024-01-01\"}" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Response:
{
"deleted": 5
}
See CRUD Operations Guide for complete bulk operation documentation.
Common Patterns
Blog Homepage
Show recent published posts with authors:
curl "https://your-domain.com/api/apps/my-blog/datatables/posts/data/?\
status=published&\
populate=author&\
_sort=published_at&\
_order=desc&\
_start=0&\
_end=10" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Author Profile
Get author with their posts:
curl "https://your-domain.com/api/apps/my-blog/datatables/users/data/1/?\
populate=posts&\
posts_status=published&\
posts_sort=published_at&\
posts_order=desc" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Comment Count
Get total comments for monitoring:
curl "https://your-domain.com/api/apps/my-blog/datatables/comments/data/" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" | jq '.total'
The response will include the total count:
{
"data": [...],
"total": 15
}
Next Steps
Deep Dive into Features
Now that you've built your first app, explore these comprehensive guides:
Core Operations
- CRUD Operations: Complete guide to creating, reading, updating, and deleting data
- Querying & Filtering: Master 20+ filter operators, sorting, and pagination
- Aggregations: SQL-like GROUP BY, HAVING, and aggregate functions
Advanced Topics
- Relationships: Foreign keys, JOINs, and relationship population
- Hierarchies: Tree structures and organizational charts
- Graph Traversal: Network queries with BFS/DFS algorithms
- Authorization: Policy-based access control with row-level security
Schema & Data Management
- Schema Guide: Detailed Frictionless Table Schema documentation
- Migrations: Schema evolution with Alembic
- Indexes: Create indexes for better performance
- Data Imports: Bulk data loading from CSV/JSON files
Build Something Real
Now that you've mastered the basics, try building:
- E-commerce Store: Products, orders, customers, reviews
- Project Management: Projects, tasks, users, comments
- CRM: Contacts, companies, deals, activities
- Social Network: Users, posts, follows, likes
Integration Examples
Check out our example integrations:
- React + TypeScript: Full-stack blog application
- Vue.js: E-commerce storefront
- Python Backend: Data aggregation service
- Mobile (React Native): Blog reader app
Troubleshooting
"Method POST not allowed"
Problem: Trying to create data at wrong endpoint
Solution: Add /data/ to the URL:
# ❌ Wrong
POST /api/apps/my-blog/datatables/posts/
# ✅ Correct
POST /api/apps/my-blog/datatables/posts/data/
"Table is not materialized"
Problem: Trying to insert data into unmaterialized table
Solution: Use ?materialize=true when importing schema, or materialize individually:
POST /api/apps/my-blog/datatables/posts/materialize/
Authentication Errors
Problem: Getting 401 Unauthorized
Solution: Check your JWT token:
# Get a new token
POST /api/auth/jwt/token/
{
"username": "your-username",
"password": "your-password"
}
Get Help
Happy coding! 🚀