Skip to Content
AdvancedDecorator-Free API

Decorator-Free API

Use Gluonic without TypeScript decorators - perfect for build tool compatibility or personal preference.

Why Decorator-Free?

Reasons to avoid decorators:

  • Build tool issues (Vite, Webpack configuration complexity)
  • Team preference (some developers dislike decorators)
  • Legacy TypeScript (pre-5.0)
  • JavaScript projects (decorators are TypeScript-only)

Gluonic supports both! Choose what works for you.

Imperative Model Registration

With Decorators (Standard)

import { Model, ClientModel, Property, ManyToOne, LazyReference } from '@gluonic/client' @ClientModel('user') export class User extends Model { @Property() id: string = '' @Property() email: string = '' @Property() name: string = '' }

Without Decorators (Alternative)

import { Model, registerModel } from '@gluonic/client' export class User extends Model { id: string = '' email: string = '' name: string = '' } // Register manually registerModel(User, { type: 'user', properties: { id: { type: 'primitive' }, email: { type: 'primitive' }, name: { type: 'primitive' } } })

Relationships Without Decorators

import { Model, registerModel, LazyReference } from '@gluonic/client' export class Post extends Model { id = '' title = '' authorId = '' author?: User } registerModel(Post, { type: 'post', properties: { id: { type: 'primitive' }, title: { type: 'primitive' }, authorId: { type: 'primitive' }, author: { type: 'object', relationshipType: 'm2o', relatedType: 'user', fk: 'authorId', inverse: 'posts' } } })

Builder Pattern API

Fluent API for model definition:

import { defineModel } from '@gluonic/client/builder' export const User = defineModel('user') .property('id', 'string') .property('email', 'string') .property('name', 'string', { optional: true }) .property('createdAt', 'number', { server: true }) .hasMany('posts', 'Post', { inverse: 'author' }) .computed('displayName', function() { return this.name || this.email.split('@')[0] }) .build()

Benefits:

  • Fluent API (reads like English)
  • Type-safe
  • No decorators needed
  • Works in pure JavaScript

Functional API

Pure functions (no classes):

import { createModel } from '@gluonic/client/functional' export const User = createModel({ type: 'user', fields: { id: 'string', email: 'string', name: 'string' }, relationships: { posts: { type: 'hasMany', model: 'Post' } }, computed: { displayName: (user) => user.name || user.email.split('@')[0] } }) // Usage in components (same!) const user = useModel('user', userId) console.log(user.displayName)

TypeScript Support

All approaches are fully type-safe:

// Decorator approach const user = useModel<User>('user', 'u1') user.email // string ✓ // Imperative approach const user = useModel<User>('user', 'u1') user.email // string ✓ // Builder approach const user = useModel<typeof User>('user', 'u1') user.email // string ✓ // Functional approach const user = useModel<typeof User>('user', 'u1') user.email // string ✓

Choosing an Approach

ApproachProsConsBest For
DecoratorsClean syntax, standard TSBuild config neededModern TS projects
ImperativeNo decorators, flexibleMore verboseBuild tool issues
BuilderFluent API, readableNew pattern to learnPreference
FunctionalNo classes, pureDifferent paradigmFunctional fans

Migration Between Approaches

From Decorators to Imperative

# Auto-convert with CLI npx gluonic convert --from=decorators --to=imperative --dir=./models

From Imperative to Builders

npx gluonic convert --from=imperative --to=builder --dir=./models

Configuration

Tell Gluonic which approach you’re using:

const { store } = SyncClient({ modelStyle: 'decorators', // or 'imperative', 'builder', 'functional' // ... other config })

Next Steps

Last updated on