Appearance
Quick Start
Get started with Quickback definitions in minutes. This guide walks you through the folder structure and shows a complete working example.
Folder Structure
definitions/
├── schema/ # Database table definitions
│ ├── users.ts
│ └── rooms.ts
├── features/
│ └── {feature-name}/
│ ├── resource.ts # Firewall, Guards, CRUD access, Masking
│ ├── actions.ts # Custom actions/routes
│ └── handlers/ # Complex action handlers (optional)
│ └── my-action.tsComplete Example
Here's a complete example defining a rooms resource with all security layers configured.
Schema Definition
First, define your database table using Drizzle ORM (see Database Schema for details):
typescript
// definitions/schema/rooms.ts
import { pgTable, text, integer, boolean, timestamp } from 'drizzle-orm/pg-core';
export const rooms = pgTable('rooms', {
id: text('id').primaryKey(),
name: text('name').notNull(),
description: text('description'),
capacity: integer('capacity').notNull().default(10),
roomType: text('room_type').notNull(),
isActive: boolean('is_active').notNull().default(true),
deactivationReason: text('deactivation_reason'),
// Ownership - Quickback auto-detects this for the firewall
organizationId: text('organization_id').notNull(),
});Resource Definition
typescript
// definitions/features/rooms/resource.ts
import { rooms } from '../../schema';
import { defineResource } from '@quickback/core';
export default defineResource(rooms, {
guards: {
createable: ["name", "description", "capacity", "roomType"],
updatable: ["name", "description", "capacity"],
protected: {
isActive: ["activate", "deactivate"],
},
},
crud: {
list: {
access: { roles: ["member", "admin"] },
pageSize: 25,
},
get: {
access: { roles: ["member", "admin"] },
},
create: {
access: { roles: ["admin"] },
},
update: {
access: { roles: ["admin"] },
},
delete: {
access: { roles: ["admin"] },
mode: "soft",
},
},
});Actions Definition
typescript
// definitions/features/rooms/actions.ts
import { rooms } from '../../schema';
import { defineActions } from '@quickback/core';
import { eq } from 'drizzle-orm';
import { z } from 'zod';
export default defineActions(rooms, {
activate: {
description: "Activate a deactivated room",
input: z.object({}),
guard: {
roles: ["admin"],
record: { isActive: { equals: false } },
},
execute: async ({ db, record }) => {
const [updated] = await db
.update(rooms)
.set({ isActive: true })
.where(eq(rooms.id, record.id))
.returning();
return updated;
},
},
deactivate: {
description: "Deactivate a room",
input: z.object({
reason: z.string().optional(),
}),
guard: {
roles: ["admin"],
record: { isActive: { equals: true } },
},
execute: async ({ db, record, input }) => {
const [updated] = await db
.update(rooms)
.set({ isActive: false, deactivationReason: input.reason })
.where(eq(rooms.id, record.id))
.returning();
return updated;
},
},
});What This Example Does
- Firewall: Quickback auto-detects
organizationIdin the schema and isolates data by organization. Users in Org A can never see Org B's data. - Guards: Users can create rooms with
name,description,capacity,roomType. Onlyname,description,capacitycan be updated. TheisActivefield is protected and can only be changed via theactivateanddeactivateactions. - Access: Members and admins can list and get rooms. Only admins can create, update, or delete.
- Actions: Two custom actions to activate/deactivate rooms with preconditions.
Next Steps
Dive deeper into each topic: