| title | Getting Started |
|---|---|
| description | Build your first ObjectStack application in 5 minutes. |
ObjectStack is a protocol-first platform. The @objectstack/spec package provides Zod schemas and strict TypeScript types to build valid metadata definitions.
You can start from scratch or add ObjectStack to an existing TypeScript project.
mkdir my-app
cd my-app
npm init -y
npm install typescript zod @objectstack/spec
npx tsc --initCreate a file named src/domains/crm/contact.object.ts.
This is where the power of Zod-First Definition comes in. You get autocomplete and validation out of the box.
import { ObjectSchema, Field } from '@objectstack/spec';
export const Contact = ObjectSchema.create({
name: 'contact',
label: 'Contact',
pluralLabel: 'Contacts',
icon: 'user',
description: 'People associated with accounts',
nameField: 'full_name',
fields: {
// Basic Information
first_name: Field.text({
label: 'First Name',
required: true,
maxLength: 100,
}),
last_name: Field.text({
label: 'Last Name',
required: true,
maxLength: 100,
}),
// Formula field - automatically calculated
full_name: Field.formula({
label: 'Full Name',
expression: 'CONCAT(first_name, " ", last_name)',
}),
// Contact Information
email: Field.email({
label: 'Email',
unique: true,
}),
phone: Field.phone({
label: 'Phone',
}),
// Selection with predefined options
type: Field.select({
label: 'Contact Type',
options: [
{ label: 'Customer', value: 'customer', default: true },
{ label: 'Partner', value: 'partner' },
{ label: 'Vendor', value: 'vendor' },
],
}),
// Relationship to Account
account: Field.masterDetail('account', {
label: 'Account',
required: true,
deleteBehavior: 'cascade',
}),
},
enable: {
apiEnabled: true,
trackHistory: true,
}
});Create src/domains/crm/account.object.ts for the parent object:
import { ObjectSchema, Field } from '@objectstack/spec';
export const Account = ObjectSchema.create({
name: 'account',
label: 'Account',
pluralLabel: 'Accounts',
icon: 'building',
nameField: 'name',
fields: {
// Autonumber field
account_number: Field.autonumber({
label: 'Account Number',
format: 'ACC-{0000}',
}),
name: Field.text({
label: 'Account Name',
required: true,
searchable: true,
maxLength: 255,
}),
// Currency field
annual_revenue: Field.currency({
label: 'Annual Revenue',
min: 0,
}),
// Select with custom colors (for kanban boards)
type: Field.select({
label: 'Account Type',
options: [
{ label: 'Prospect', value: 'prospect', color: '#FFA500', default: true },
{ label: 'Customer', value: 'customer', color: '#00AA00' },
{ label: 'Partner', value: 'partner', color: '#0000FF' },
],
}),
},
enable: {
apiEnabled: true,
trackHistory: true,
}
});Create objectstack.config.ts to bundle your objects into an app:
import { defineManifest } from '@objectstack/spec';
import { Account } from './src/domains/crm/account.object';
import { Contact } from './src/domains/crm/contact.object';
export default defineManifest({
name: 'my_crm',
label: 'My CRM',
version: '1.0.0',
description: 'Simple CRM application',
objects: [Account, Contact],
navigation: {
tabs: [
{
label: 'Sales',
items: [
{ type: 'object', object: 'account' },
{ type: 'object', object: 'contact' },
],
},
],
},
});Create a build script src/build.ts to verify your definitions:
import config from '../objectstack.config';
// This will throw a ZodError if your schema is invalid
console.log(`✅ App '${config.label}' is valid!`);
console.log(`📦 Objects: ${config.objects.map(o => o.name).join(', ')}`);
// Export as JSON for runtime use
import fs from 'fs';
fs.writeFileSync(
'dist/manifest.json',
JSON.stringify(config, null, 2)
);Run it:
npx tsx src/build.tsNow that you have a working app, explore advanced features:
validations: [
{
name: 'unique_email',
type: 'unique',
fields: ['email'],
message: 'Email already exists',
},
{
name: 'positive_revenue',
type: 'script',
expression: 'annual_revenue >= 0',
message: 'Revenue must be positive',
},
]workflows: [
{
name: 'update_last_activity',
trigger: 'on_update',
actions: [
{
type: 'field_update',
field: 'last_activity_date',
value: 'TODAY()',
},
],
},
]views: {
list: {
type: 'grid',
columns: ['account_number', 'name', 'type', 'annual_revenue'],
filters: [
{ field: 'type', operator: '=', value: 'customer' }
],
},
kanban: {
type: 'kanban',
groupBy: 'type',
columns: ['name', 'annual_revenue'],
},
}Now that you have valid metadata, you can:
- Explore Examples: Check out the CRM Example for a full-featured implementation
- Learn Field Types: See the Field Types Guide for all 30+ field types
- Build UI: Use the metadata with ObjectStack runtime to generate interfaces
- Deploy: Push to an ObjectStack kernel for production use
Additional Resources: