Building SaaS Website #09: Mastering Schemas and Validation
Welcome back to our TotalGPT development series! In this post, we'll dive deep into schemas and validation in Total.js v5. Understanding schemas is crucial for building robust applications as they help ensure data integrity and provide a structured way to handle business logic.
The Power of Schema Validation
Schema validation is a crucial aspect of web development that helps ensure data consistency and reliability. In Total.js v5, schemas serve multiple purposes:
- Data validation
- Business logic organization
- API endpoint definitions
- Database operations structuring
Let's explore how to implement schemas effectively in our TotalGPT project.
Understanding Schema Structure
In Total.js v5, schemas are defined using the NEWSCHEMA
function. Here's the basic structure:
// schemas/contactform.js
NEWSCHEMA('SchemaName', function(schema) {
schema.action('actionName', {
name: 'Human readable name',
input: 'field1:Type,field2:Type', // Input validation
params: 'param1:Type,param2:Type', // Parameter validation
action: function($, model) {
// Business logic here
}
});
});
Key components:
SchemaName
: Unique identifier for your schema
action
: Defines operations within the schema
input
: Validates incoming data
params
: Validates route parameters
action
: Contains the business logic
Let's examine our ContactForm schema implementation:
NEWSCHEMA('ContactForm', function(schema) {
schema.action('create', {
name: 'contact TotalGPT',
input: '*name:String,*email:Email,*phone:String,*subject:String,*content:String',
action: function($, model) {
// Generate unique ID and timestamp
model.id = UID();
model.dtcreated = NOW;
// Insert into database
DATA.insert('nosql/contacts').callback(function(err, response) {
if(err) {
console.log(err);
return;
}
// Notify admin and redirect user
FUNC.notify_admin('You have new contact via website: ' + model.email);
$.redirect('/contact?success=true');
});
}
});
});
Let's break down the key elements:
- Input Validation:
*name:String
: Required name field
*email:Email
: Required and must be valid email format
*phone:String
: Required phone number
*subject:String
: Required subject line
*content:String
: Required message content
- Automatic Validation: The asterisk (
*
) indicates required fields
- Data Processing:
UID()
: Generates unique identifier
NOW
: Captures current timestamp
Implementing Complex Schemas: Plan Management
Let's examine our more complex Plan schema that manages subscription plans:
// schemas/plans.js
NEWSCHEMA('Plan', function(schema) {
// List all plans
schema.action('list', {
name: 'List plans',
action: async function($) {
var plans = await DATA.find('plans')
.fields('id,description,description2,reminder,searchonline,links,more,limit,name,name2,text,context,voice,pictures,documents,image,price,oldprice,price2,oldprice2')
.promise($);
$.callback(plans);
}
});
// Read single plan
schema.action('read', {
name: 'Read a plan',
params: '*id:String',
action: async function($) {
var plan = await DATA.read('plans')
.id($.params.id)
.fields('id,description,description2,reminder,searchonline,links,more,limit,name,name2,text,context,voice,pictures,documents,image,price,oldprice,price2,oldprice2')
.error(404)
.promise($);
$.callback(plan);
}
});
// Additional CRUD operations...
});
Notice how we handle different operations within the same schema:
- List operation: Retrieves all plans
- Read operation: Gets a specific plan by ID
- Each operation has its own validation rules and business logic
Connecting Schemas to Routes
In Total.js v5, connecting schemas to routes is straightforward. Here's how we do it in controllers/default.js
:
exports.install = function() {
// Route for contact form submission
ROUTE('POST /contact/ --> ContactForm/create');
}
// Route for pricing page that uses Plan schema
async function pricing() {
var plans = await ACTION('Plan --> list').promise();
this.view('pricing', { plans: plans.reverse() });
}
Key points about routing:
- The
-->
syntax indicates schema routing
- Format:
HTTP_METHOD /path/ --> SchemaName/actionName
- Using
ACTION()
to invoke schema actions programmatically
Schema Validation Types
Total.js v5 supports various validation types:
// Common validation types
'*field:String' // Required string
'field:String' // Optional string
'*field:Email' // Required email
'*field:Number' // Required number
'field:Boolean' // Optional boolean
'*field:Date' // Required date
Additional validation features:
- Custom error messages
- Regular expression validation
- Custom validation functions
- Array validation
Feel free to refer to this link to have the whole list of data types and default values.
Best Practices
When working with schemas in Total.js v5:
- Organized Structure:
`javascript
// Group related actions together
NEWSCHEMA('EntityName', function(schema) {
// List operations first
schema.action('list', {...});
// CRUD operations next
schema.action('create', {...});
schema.action('read', {...});
schema.action('update', {...});
schema.action('remove', {...});
// Custom operations last
schema.action('custom', {...});
});
`
- Consistent Naming:
- Use clear, descriptive schema names
- Follow a consistent action naming convention
- Document complex validation rules
- Error Handling:
`javascript
schema.action('create', {
action: function($, model) {
try {
// Your logic here
} catch (err) {
$.invalid(err); // Proper error handling
}
}
});
`
What's Next?
In the next blog post, we'll explore how to integrate payment gateways into our TotalGPT platform, building on the schema knowledge we've gained here.
Remember that proper schema implementation is crucial for maintaining data integrity and creating a robust application. Take time to plan your schemas carefully, considering both current and future needs of your application.
Stay tuned for more Total.js development insights in our next post! 🚀