Type Reuse
Apiwork provides two ways to reuse types: inheritance with extends and composition with merge. For reusable param groups that should not appear in exports, use fragment.
Inheritance with extends
extends creates a type hierarchy. The relationship is preserved in the output.
Basic Inheritance
object :person do
string :name
string :email
end
object :employee do
extends :person
string :employee_id
string :department
endThe :employee object has four properties: name, email, employee_id, and department.
Multiple Inheritance
extends can be called multiple times to inherit from multiple types:
object :contactable do
string :email
string :phone
end
object :timestamped do
datetime :created_at
datetime :updated_at
end
object :customer do
extends :contactable
extends :timestamped
string :name
endGenerated Output
TypeScript:
export interface Employee extends Person {
employeeId: string;
department: string;
}
// Or without own properties:
export type Admin = User;
// Multiple inheritance:
export type Customer = Contactable & Timestamped & { name: string };Zod:
export const EmployeeSchema = PersonSchema.extend({
employeeId: z.string(),
department: z.string()
});
// Or without own properties:
export const AdminSchema = UserSchema;
// Multiple inheritance:
export const CustomerSchema = ContactableSchema.merge(TimestampedSchema).extend({
name: z.string()
});OpenAPI:
Employee:
allOf:
- $ref: '#/components/schemas/Person'
- type: object
properties:
employeeId:
type: string
department:
type: stringComposition with merge
merge includes properties from another type without creating an inheritance relationship. The properties are inlined - no reference appears in the output.
Basic Usage
object :auditable do
datetime :created_at
datetime :updated_at
end
object :invoice do
merge :auditable
string :number
endThe :invoice object has three properties: created_at, updated_at, and number. Unlike extends, the output contains no reference to :auditable.
Multiple Merges
object :entity do
merge :identifiable
merge :timestamped
merge :auditable
string :name
endOwn Properties Override Merged
object :base do
string :name
end
object :child do
merge :base
string :name, description: "Overridden description"
endextends vs merge
| Feature | extends | merge |
|---|---|---|
| Includes properties | Yes | Yes |
| Reference in output | Yes (allOf/extends) | No (inlined) |
| Use case | Type hierarchies | Mixins, composition |
extends is appropriate when:
- You want a visible inheritance relationship
- Types share an "is-a" relationship
merge is appropriate when:
- You want to reuse properties without inheritance
- Types share a "has-properties-of" relationship
- Composing from multiple sources
Fragments
Fragments are types that only exist for merging. They do not appear as standalone types in introspection or exports.
fragment :timestamps do
datetime :created_at
datetime :updated_at
end
object :invoice do
merge :timestamps
string :number
decimal :total
end
object :customer do
merge :timestamps
string :name
string :email
endBoth :invoice and :customer include created_at and updated_at fields. The :timestamps fragment itself does not appear in introspection, OpenAPI, TypeScript, or Zod output.
Contract-scoped Fragments
Fragments defined in a contract follow the same scoping rules as objects:
class InvoiceContract < Contract::Base
fragment :auditable do
string :created_by
string :updated_by
end
object :invoice do
merge :auditable
string :number
end
endContract-scoped fragments are importable via import, just like objects.
When to Use Fragments
| Concept | Use |
|---|---|
object | Standalone type visible in exports |
fragment | Reusable param group, invisible in exports |
extends | Inheritance with visible relationship |
merge | Composition (works with both objects and fragments) |
Fragments provide reusable param groups that do not appear in generated API specs.
Declaration Order
Types can be defined in any order. Apiwork automatically resolves dependencies and outputs types in the correct order.
# This works - child defined before parent
object :employee do
extends :person
string :employee_id
end
object :person do
string :name
endSee also
- Declaration Merging — extending existing types with multiple declarations
- Contract::Base reference — type definition methods