API Endpoints Documentation
API Endpoints Documentation
Plings exposes a single GraphQL endpoint for all read/write operations and real-time subscriptions.
POST /graphql # Queries & mutations (HTTP/1.1)
GET /graphql # GraphiQL interface (development only)
WS /graphql (graphql-transport-ws) # Subscriptions (WebSocket)
GraphiQL Interface
The backend exposes a GraphiQL interface for development and testing:
URL: https://plings-backend.vercel.app/graphql/
Access: Open the URL in your browser to access the interactive GraphQL explorer
Features:
- Schema Introspection: View the complete GraphQL schema
- Query Testing: Write and execute GraphQL queries/mutations
- Auto-completion: IntelliSense for fields and types
- Documentation Explorer: Browse schema documentation
Note: GraphiQL is enabled because the backend runs with
debug=True. In production, this should be disabled for security.
Example Operations
# Query: Get all user accessible objects (PostgreSQL-first hybrid query)
query myObjects {
myObjects {
id
name
description
mainImageUrl
imageUrls
statuses
owner_organization_id
created_by
location
type
lastSeen
# Spatial relationships
spatialParent {
id
name
type
}
spatialChildren {
id
name
type
mainImageUrl
hasChildren
}
hasChildren
spatialHierarchy { # Breadcrumb path from root to object
id
name
type
}
currentPlacement { # Current container (same as spatialParent)
id
name
type
}
normalPlacement { # Where object should normally be (SHOULD_BE_IN)
id
name
type
}
# Positional relationships (LEFT_OF, RIGHT_OF, etc.)
positionalRelationships {
relationshipType
relatedObject {
id
name
type
}
properties {
distance
unit
confidence
measuredAt
}
}
# Functional relationships (HAS_FUNCTION, USED_FOR, etc.)
functionalRelationships {
id
name
type
predicate
direction # "incoming" or "outgoing"
}
# Object metadata
properties { # Key-value pairs
key
value
dataType # "string", "number", "boolean", "object", "array"
}
objectClass {
id
name
properties {
key
name
dataType
required
}
}
tags # Array of string tags
createdAt
updatedAt
# Note: Objects with status "ORPHANED_POSTGRESQL_ONLY" exist in PostgreSQL but not Neo4j
}
}
# Query: Lightweight version for spatial navigation (NEW: Optimized for Performance)
# 🚀 PERFORMANCE: Use this query for spatial navigation to avoid timeouts
query myObjectsLight {
myObjectsLight {
id
name
owner_organization_id
spatialParent {
id
name
}
spatialChildren {
id
name
}
hasChildren
}
}
# Note: myObjectsLight is optimized for fast loading and timeout prevention:
# - Minimal PostgreSQL query (only id, name, owner_organization_id)
# - Minimal Neo4j enrichment (only spatial parent/children)
# - No image fetching, properties, or relationship processing
# - Typically loads in <1s vs 5-10s for full myObjects query
# - Use for spatial navigation, then load full details with getObjectDetailsComplete as needed
# Query: Get object details with images
query getObjectDetails($objectId: ID!) {
object(id: $objectId) {
id
name
description
owner_organization_id
created_by
ownerOrganization {
id
name
}
images {
id
url
isMain
uploadedAt
fileName
fileSize
}
statuses
imageUrls
mainImageUrl
partOf { id name }
spatialChildren { id name }
}
}
# Mutation: Move object
mutation moveObject($objectId: ID!, $newContainerId: ID!) {
moveObject(objectId: $objectId, newContainerId: $newContainerId) {
id
spatialParent { id name }
}
}
# Subscription: Live updates
subscription onObjectUpdate($objectId: ID!) {
objectUpdated(id: $objectId) {
id
name
owner_organization_id
created_by
statuses
imageUrls
mainImageUrl
}
}
# Query: Get status definitions
query getStatusDefinitions {
statusDefinitions {
key
name
description
color
category
isTerminal
conflictsWith
requiresActiveFalse
}
}
# Query: Validate status combination
query validateStatuses($statusKeys: [String!]!) {
validateStatusCombination(statusKeys: $statusKeys) {
valid
errors
warnings
}
}
# Mutation: Update object statuses
mutation updateObjectStatuses($objectId: ID!, $statusKeys: [String!]!) {
updateObjectStatuses(objectId: $objectId, statusKeys: $statusKeys) {
id
name
statuses
owner_organization_id
created_by
location
description
imageUrls
mainImageUrl
}
}
# Mutation: Upload new images to object
mutation uploadObjectImages($objectId: ID!, $images: [Upload!]!) {
uploadObjectImages(objectId: $objectId, images: $images) {
id
imageUrls
mainImageUrl
}
}
# Mutation: Delete specific images from object
mutation deleteObjectImages($objectId: ID!, $imageUrls: [String!]!) {
deleteObjectImages(objectId: $objectId, imageUrls: $imageUrls) {
id
imageUrls
mainImageUrl
}
}
# Mutation: Update object images (add new + delete old atomically)
mutation updateObjectImages($objectId: ID!, $newImages: [Upload!], $deleteImageUrls: [String!]) {
updateObjectImages(objectId: $objectId, newImages: $newImages, deleteImageUrls: $deleteImageUrls) {
id
imageUrls
mainImageUrl
}
}
# Mutation: Update object basic information and spatial relationships
mutation updateObjectBasicInfo($objectId: ID!, $name: String, $description: String, $location: String) {
updateObjectBasicInfo(objectId: $objectId, name: $name, description: $description, location: $location) {
id
name
description
imageUrls
mainImageUrl
statuses
spatialParent {
id
name
}
}
}
# Query: Get spatial predicates from catalogue
query getSpatialPredicates {
spatialPredicates {
key
axis
description
inverse_key
is_symmetric
}
}
# Query: Get objects that can act as containers
query getPotentialContainers {
potentialContainers {
id
name
description
type
mainImageUrl
imageUrls
statuses
}
}
# Mutation: Update object basic information and spatial relationships
mutation updateObjectBasicInfo($objectId: ID!, $name: String, $description: String, $location: String) {
updateObjectBasicInfo(objectId: $objectId, name: $name, description: $description, location: $location) {
id
name
description
imageUrls
mainImageUrl
statuses
spatialParent {
id
name
}
}
}
# Query: Get spatial predicates from catalogue
query getSpatialPredicates {
spatialPredicates {
key
axis
description
inverse_key
is_symmetric
}
}
# Query: Get objects that can act as containers
query getPotentialContainers {
potentialContainers {
id
name
description
type
mainImageUrl
imageUrls
statuses
}
}
# Query: Get complete object details with all relationships and metadata
# 🎯 ENHANCED 2025-01-27: Now properly fetches spatial/functional relationships from Neo4j
query getObjectDetailsComplete($objectId: ID!) {
object(id: $objectId) {
# Basic object data (PostgreSQL)
id
name
description
owner_organization_id
created_by
ownerOrganization {
id
name
}
images {
id
url
isMain
uploadedAt
fileName
fileSize
}
imageUrls
mainImageUrl
lastSeen
location
type
# ✅ WORKING: Spatial relationships (Neo4j)
spatialParent { id name type } # Fixed: Now properly fetches from Neo4j
spatialChildren { id name type hasChildren }
spatialHierarchy { id name type } # NEW: Path from object to root container
currentPlacement { id name type } # Same as spatialParent
# ✅ WORKING: Object metadata (Neo4j)
statuses # NEW: Now gets from Neo4j (e.g., ["active", "reserved"])
tags # NEW: Now gets from Neo4j
properties { key value dataType } # NEW: Converts Neo4j properties to GraphQL format
# ✅ WORKING: Functional relationships (Neo4j)
functionalRelationships { # NEW: Non-spatial relationships
id name type predicate direction
}
# ❌ TODO: Still need implementation
partOf { id name type } # Needs PART_OF relationship implementation
collections { id name description } # Needs collection system implementation
objectClass { id name description } # Needs object class system implementation
normalPlacement { id name type } # Needs SHOULD_BE_IN relationship implementation
}
}
# Query: Get spatial children (objects contained within a parent)
query getSpatialChildren($parentId: ID!, $limit: Int = 100) {
getSpatialChildren(parentId: $parentId, limit: $limit) {
id
name
type
statuses
mainImageUrl
hasChildren
}
}
# Query: Get user's organizations
query getMyOrganizations {
myOrganizations {
id
name
description
type
created_at
memberCount
role
}
}
# Mutation: Create new organization
mutation createOrganization($name: String!, $description: String, $type: String) {
createOrganization(name: $name, description: $description, type: $type) {
id
name
description
type
created_at
memberCount
role
}
}
# Mutation: Create spatial relationship between objects
mutation createSpatialRelationship($objectId: ID!, $targetId: ID!, $relationship: SpatialRelationshipType!) {
createSpatialRelationship(objectId: $objectId, targetId: $targetId, relationship: $relationship) {
id
spatialParent { id name }
spatialChildren { id name }
}
}
# Mutation: Remove spatial relationship between objects
mutation removeSpatialRelationship($objectId: ID!, $targetId: ID!, $relationship: SpatialRelationshipType!) {
removeSpatialRelationship(objectId: $objectId, targetId: $targetId, relationship: $relationship)
}
# Enhanced SpatialRelationshipType Enum (Updated 2025-07-03 08:17:03)
enum SpatialRelationshipType {
# NORMAL predicates (where objects should be) - Primary system for designated locations
NORMAL_IN # Object's designated container
NORMAL_LEFT_OF # Object's expected position to the left
NORMAL_RIGHT_OF # Object's expected position to the right
NORMAL_ABOVE # Object's expected position above
NORMAL_UNDER # Object's expected position below
NORMAL_ON # Object's expected surface placement
NORMAL_ATTACHED_TO # Object's expected attachment
NORMAL_NEXT_TO # Object's expected adjacent placement
# CURRENT predicates (where objects currently are) - Future implementation for real-time tracking
CURRENT_IN # Object's actual current container
CURRENT_LEFT_OF # Object's actual current position to the left
CURRENT_RIGHT_OF # Object's actual current position to the right
CURRENT_ABOVE # Object's actual current position above
CURRENT_UNDER # Object's actual current position below
CURRENT_ON # Object's actual current surface placement
CURRENT_ATTACHED_TO # Object's actual current attachment
CURRENT_NEXT_TO # Object's actual current adjacent placement
# Legacy predicates (backward compatibility during migration)
IN # Maps to NORMAL_IN
TO_LEFT # Maps to NORMAL_LEFT_OF
TO_RIGHT # Maps to NORMAL_RIGHT_OF
ABOVE # Maps to NORMAL_ABOVE
BELOW # Maps to NORMAL_UNDER
SHOULD_BE_IN # Maps to NORMAL_IN
}
Upload Handling
Plings uses the GraphQL multipart‐request specification for file uploads. Files are stored in Supabase Storage with specific bucket paths:
// Example: Upload images to object
const uploadImages = async (objectId: string, files: File[]) => {
const mutation = gql`
mutation uploadObjectImages($objectId: ID!, $images: [Upload!]!) {
uploadObjectImages(objectId: $objectId, images: $images) {
id
imageUrls
mainImageUrl
}
}
`;
return await apolloClient.mutate({
mutation,
variables: { objectId, images: files },
});
};
Storage Structure:
- Path:
objects/{objectId}/images/{timestamp}_{filename} - Bucket:
object-images(public read access) - Formats: JPG, PNG, WEBP (auto-optimized)
- Size Limits: 10MB per file, 50MB total per object
Frontend Integration Patterns
Apollo Client Configuration
The frontend uses Apollo Client with custom configuration for authentication, error handling, and timeout management:
// src/lib/apollo.ts
import { TimeoutLink } from 'apollo-link-timeout';
// NEW: Global timeout configuration (30 seconds)
const timeoutLink = new TimeoutLink(30000);
const authLink = setContext((_, { headers }) => {
const token = supabase.auth.getSession()?.access_token;
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
}
};
});
// Enhanced error handling with timeout detection
const errorLink = onError(({ graphQLErrors, networkError, operation }) => {
// Enhanced timeout detection and handling
if (networkError && (
networkError.message?.includes('timeout') ||
networkError.message?.includes('timed out') ||
(networkError as any)?.name === 'TimeoutError'
)) {
console.warn('⏰ Query timeout detected - consider using lighter queries or pagination');
}
if (graphQLErrors) {
graphQLErrors.forEach(({ message, locations, path }) => {
console.error(`GraphQL error: ${message}`);
});
}
if (networkError) {
console.error(`Network error: ${networkError}`);
}
});
// Create Apollo Client with timeout link first in chain
export const apolloClient = new ApolloClient({
link: from([timeoutLink, errorLink, authLink, httpLink]),
cache: new InMemoryCache(),
defaultOptions: {
watchQuery: { errorPolicy: 'all' },
query: { errorPolicy: 'all' },
},
});
Query Patterns
Object Detail Loading
// Heavy object details for modals
const { data, loading, error } = useQuery(GET_OBJECT_DETAILS_COMPLETE, {
variables: { objectId },
fetchPolicy: 'cache-and-network'
});
// Lightweight object details for cards
const { data } = useQuery(GET_OBJECT_DETAILS_SIMPLE, {
variables: { objectId },
fetchPolicy: 'cache-first'
});
Spatial Navigation Optimization (NEW: Timeout Prevention)
// 🚀 PERFORMANCE: Use light query for spatial navigation
const { data: lightObjects, loading, error } = useQuery(GET_MY_OBJECTS_LIGHT, {
fetchPolicy: 'cache-and-network',
errorPolicy: 'all'
});
// Load full details only when needed for object modals
const [getObjectDetails, { loading: detailsLoading }] = useLazyQuery(GET_OBJECT_DETAILS_COMPLETE);
const handleOpenObjectModal = async (objectId: string) => {
const { data } = await getObjectDetails({ variables: { objectId } });
// Modal opens with full object details
};
// Alternative: Use spatial hierarchy hook with light data
const { hierarchicalObjects, loading } = useSpatialHierarchyLight();
Spatial Relationship Updates
// Move object with optimistic updates
const [moveObject] = useMutation(MOVE_OBJECT, {
optimisticResponse: {
moveObject: {
id: objectId,
spatialParent: { id: newContainerId, name: containerName }
}
},
refetchQueries: [{ query: GET_SPATIAL_CHILDREN, variables: { parentId: newContainerId } }]
});
Error Handling
GraphQL Error Types
- Authentication:
UNAUTHENTICATED- redirect to login - Authorization:
FORBIDDEN- show permission error - Validation:
BAD_USER_INPUT- show field-specific errors - Not Found:
NOT_FOUND- show 404 state
Frontend Error Boundaries
// Graceful error handling for GraphQL failures
if (error?.networkError) {
return <NetworkErrorState />;
}
if (error?.graphQLErrors?.length > 0) {
return <GraphQLErrorState errors={error.graphQLErrors} />;
}
Caching Strategy
- Object Details: Cache-and-network for freshness
- Object Lists: Cache-first for performance
- Spatial Relationships: Network-only for consistency
- Status Definitions: Cache-first (rarely change)
Real-time Updates (Planned)
// Subscription pattern for live updates
const { data: updates } = useSubscription(OBJECT_UPDATED, {
variables: { objectId },
onSubscriptionData: ({ subscriptionData }) => {
// Update Apollo cache with real-time data
client.writeQuery({
query: GET_OBJECT_DETAILS,
variables: { objectId },
data: { object: subscriptionData.data.objectUpdated }
});
}
});
Important Notes
Ownership Model
Objects in Plings are owned by organizations, not individual users. The API reflects this with:
owner_organization_id: The UUID of the organization that owns the objectcreated_by: The UUID of the user who created the object (for audit tracking)
This replaces the legacy owner field and aligns with the organization-based access control system.
Spatial Relationships
The spatial relationships system is now fully implemented. For comprehensive documentation of spatial field semantics and relationship types, see the Spatial Relationships System.
Key API Fields:
location: Current location as human-readable text (e.g.,"Kitchen Drawer 2")spatialParent: Direct container object (structured data for operations)spatialChildren: Objects contained within this object (viaINrelationships)spatialHierarchy: Complete path from object to root container (for breadcrumbs)currentPlacement: Current location (structured, same asspatialParent)normalPlacement: Expected location (viaSHOULD_BE_INrelationships) - TODO: Implementation pending
Data Source: Spatial relationships are stored in Neo4j as directional edges:
- Current location:
(:ObjectInstance)-[:IN]->(:Container) - Expected location:
(:ObjectInstance)-[:SHOULD_BE_IN]->(:Container)(TODO) - Image Integration: Main image URLs are fetched from Supabase as the single source of truth
Implementation Status Legend
- ✅ Implemented: Full backend implementation with resolvers
- 🚧 Schema only: Defined in GraphQL schema but resolvers not implemented
- ❌ Not implemented: Neither schema nor resolvers exist
- 🚧 External: Handled by external service (Supabase Auth)
Troubleshooting Backend Issues
Common GraphQL Endpoint Failures
If the GraphQL endpoint returns 500 errors with CORS preflight failures, check for these common backend issues:
1. Import Errors (Most Common Cause)
Backend startup failures often caused by incorrect imports:
# ❌ WRONG - These cause ImportError crashes
from .db_postgres import create_async_engine # Function doesn't exist here
from .db_neo4j import AsyncGraphDatabase # Function doesn't exist here
# ✅ CORRECT - Import from actual packages
from sqlalchemy.ext.asyncio import create_async_engine
from neo4j import AsyncGraphDatabase
Symptoms:
- All endpoints return 500 errors (including
/health) - No GraphQL introspection available
- CORS preflight failures
ImportError: cannot import name 'X' from 'Y'in Vercel logs
2. Missing GraphQL Authentication Context
If basic GraphQL works but authenticated queries fail:
# ❌ WRONG - Creates new GraphQL instance without auth
from .graphql import schema
graphql_app = GraphQL(schema, debug=True)
# ✅ CORRECT - Uses pre-configured instance with auth
from .graphql import graphql_app
3. Database Connection Issues
Test connectivity with these commands:
# Health check (tests basic FastAPI)
curl https://plings-backend.vercel.app/health
# GraphQL basic query (tests GraphQL + database)
curl -X POST -H "Content-Type: application/json" \
-d '{"query":"query { __typename }"}' \
https://plings-backend.vercel.app/graphql/
Frontend Response to Backend Issues
When backend issues occur:
- Check Network Tab: Look for 500 status codes vs network failures
- Verify Endpoint URL: Ensure
/graphql/has trailing slash - Test with curl: Isolate frontend vs backend issues
- Check Browser Console: Look for CORS or network error details
For detailed backend troubleshooting, see the Backend Troubleshooting Guide.
📅 Recent Enhancements
getObjectDetailsComplete Neo4j Integration (2025-06-27)
Problem: Spatial relationships returned null/empty from Neo4j queries Solution: Single comprehensive Neo4j query with PostgreSQL fallback Impact: Object modals now show complete relationship data
Fixed Fields:
spatialParent: Now properly fetches from Neo4j IN relationshipsfunctionalRelationships: Non-spatial relationships from Neo4jspatialHierarchy: Complete path to root containerstatuses,tags,properties: Enhanced metadata from Neo4j
✅ NEW: Enhanced Object Creation (2025-07-03)
Enhanced createObject Mutation
Status: ✅ Backend Complete - Ready for Integration Testing
# Enhanced Object Creation with Multi-Image, Tag, and Spatial Support
mutation createObjectEnhanced($input: CreateObjectInput!) {
createObject(input: $input) {
object {
id
name
shortCode
description
isSet
setComponents { id name }
partOfSets { id name }
owner_organization_id
created_by
mainImageUrl
imageUrls
images {
id
url
isMain
fileSize
}
spatialParent {
id
name
type
spatialPath
}
tagData {
instanceKey
classKey
path
shortCode
isManufacturerTag
}
ownerOrganization {
id
name
type
}
}
warnings
generatedName
assignedTagShortCode
spatialRelationshipCreated
imagesUploaded
}
}
Enhanced Input Types:
input CreateObjectInput {
name: String # Optional with auto-generation
shortCode: String
description: String
organizationId: ID!
spatialParentId: ID # Spatial container
spatialRelationshipType: SpatialRelationshipType # NORMAL_IN, NORMAL_ON, etc.
tagInstanceKey: String # PlingsIdentifier tag key
tagData: TagDataInput # Complete tag information
imageUploadIds: [String!] # Pre-uploaded image IDs
mainImageIndex: Int # Index of main image (default: 0)
location: String
type: String
autoGenerateName: Boolean # Enable smart name generation
}
input TagDataInput {
instanceKey: String!
classKey: String!
path: String!
shortCode: String!
isManufacturerTag: Boolean!
}
Example Usage:
# Example 1: Basic Object Creation
{
"input": {
"name": "Workshop Hammer",
"description": "Heavy duty claw hammer",
"organizationId": "123e4567-e89b-12d3-a456-426614174000",
"spatialParentId": "987fcdeb-51a2-43d1-b234-123456789abc",
"type": "Tool"
}
}
# Example 2: Advanced Creation with Tags and Images
{
"input": {
"organizationId": "123e4567-e89b-12d3-a456-426614174000",
"spatialParentId": "987fcdeb-51a2-43d1-b234-123456789abc",
"spatialRelationshipType": "NORMAL_ON",
"tagInstanceKey": "PLG_AB3X",
"tagData": {
"instanceKey": "PLG_AB3X",
"classKey": "TOOL_HAMMER",
"path": "/tools/hand-tools/hammers/",
"shortCode": "AB3X",
"isManufacturerTag": false
},
"imageUploadIds": ["img_001", "img_002", "img_003"],
"mainImageIndex": 0,
"autoGenerateName": true
}
}
# Example 3: Generated Name Result
{
"object": {
"id": "def456...",
"name": "Smith Family Workshop AB3X", # Auto-generated
"shortCode": "AB3X",
"spatialParent": {
"id": "987fcdeb...",
"name": "Workshop Bench",
"type": "Surface"
},
"tagData": {
"instanceKey": "PLG_AB3X",
"shortCode": "AB3X"
}
},
"warnings": [],
"generatedName": "Smith Family Workshop AB3X",
"assignedTagShortCode": "AB3X",
"spatialRelationshipCreated": true,
"imagesUploaded": 3
}
Key Features:
- ✅ Smart Name Generation: Auto-generates names with format
[Organization] [Input] [TagCode|#001] - ✅ Multi-Image Support: Link pre-uploaded images with main image designation
- ✅ Tag Assignment: Create and assign PlingsIdentifier tags with conflict detection
- ✅ Spatial Relationships: Enhanced spatial relationship creation with validation
- ✅ Set Support: Computed set fields based on Neo4j
PART_OFrelationships - ✅ Comprehensive Validation: Name uniqueness, tag availability, organization membership
- ✅ Detailed Response: Warnings, statistics, and enhanced result information
Integration Notes:
- Images must be pre-uploaded using existing upload mutations
- Tag data is validated and can be auto-created if not exists
- Spatial relationships default to
NORMAL_INif not specified - Name generation follows organization-specific patterns
- Set objects determined by relationship queries, not boolean flags
🧠 Organization Intelligence API
✅ getRecommendedOrganization Query
Status: ✅ Backend Complete - Ready for Integration
Added: 2025-07-03
Developer: Claude
Intelligent organization recommendation system that analyzes spatial context and user behavior to suggest optimal organization defaults for object creation.
Core Query
# Query: Get intelligent organization recommendation
query getRecommendedOrganization($spatialContextId: ID) {
getRecommendedOrganization(spatialContextId: $spatialContextId) {
# Primary recommendation with confidence scoring
primaryRecommendation {
organization {
id
name
type
created_at
}
confidence # 0.0-1.0 confidence score
reason # Human-readable explanation
lastUsed # When this org was last used in context
objectCount # Number of objects this org owns in context
}
# Alternative recommendations ranked by relevance
alternatives {
organization { id name type }
confidence
reason
lastUsed
objectCount
}
# Spatial hierarchy analysis
spatialAnalysis {
hierarchyPath {
id
name
type
}
rootContainer {
id
name
type
}
organizationPresence {
organization { id name type }
objectCount
percentage # % of objects in spatial area
lastActivity
}
}
# User behavior insights
userBehaviorAnalysis {
recentCreations {
objectId
objectName
organizationId
organizationName
spatialContext
createdAt
}
frequentOrganizations {
id
name
type
}
spatialPatterns {
spatialContextId
spatialContextName
organizationId
organizationName
creationCount
lastUsed
}
}
# System metadata
fallbackUsed # True if spatial/behavioral analysis failed
explanation # Detailed explanation of recommendation logic
}
}
Intelligence Algorithm
The recommendation system uses a three-tier algorithm:
- Priority 1: Spatial Context Analysis
- Organization of last object user created in current spatial hierarchy
- Organizations with highest presence in spatial container (% of objects owned)
- Weighted by recency and object count
- Priority 2: User Behavior Patterns
- Most frequently used organizations across all spatial contexts
- Recent creation history with temporal weighting
- Spatial usage patterns and organization preferences
- Priority 3: Fallback Logic
- User’s most recently created/joined organization
- Clear explanation when spatial/behavioral data unavailable
Usage Examples
Example 1: Home Environment (Multiple Family Members)
# User creating tool in workshop
query {
getRecommendedOrganization(spatialContextId: "workshop_container_id") {
primaryRecommendation {
organization { name }
confidence
reason
}
explanation
}
}
# Response:
{
"primaryRecommendation": {
"organization": { "name": "Dad's Workshop Tools" },
"confidence": 0.9,
"reason": "Last tool created in house hierarchy: Drill Set"
},
"explanation": "Based on last object created in house hierarchy"
}
Example 2: Work Environment (Multiple Companies)
# User creating item in shared office
query {
getRecommendedOrganization(spatialContextId: "office_room_id") {
primaryRecommendation {
organization { name }
confidence
reason
}
spatialAnalysis {
organizationPresence {
organization { name }
percentage
}
}
}
}
# Response:
{
"primaryRecommendation": {
"organization": { "name": "Acme Corp" },
"confidence": 0.8,
"reason": "45% of objects in this office area"
},
"spatialAnalysis": {
"organizationPresence": [
{ "organization": { "name": "Acme Corp" }, "percentage": 45.0 },
{ "organization": { "name": "Beta LLC" }, "percentage": 35.0 },
{ "organization": { "name": "Shared Office" }, "percentage": 20.0 }
]
}
}
Example 3: New Location (Fallback)
# User in new/empty container
query {
getRecommendedOrganization(spatialContextId: "new_room_id") {
primaryRecommendation {
organization { name }
confidence
reason
}
fallbackUsed
explanation
}
}
# Response:
{
"primaryRecommendation": {
"organization": { "name": "Personal Items" },
"confidence": 0.5,
"reason": "Most recently created organization (fallback)"
},
"fallbackUsed": true,
"explanation": "No spatial or behavioral data available, using most recent organization"
}
Integration Notes for Frontend
- Default Organization Selection: Use
primaryRecommendation.organization.idas default value - User Explanation: Display
explanationto help user understand the choice - Alternative Options: Show
alternativesin dropdown for easy switching - Confidence Indicators: Use
confidencescore to show visual confidence (e.g., star ratings) - Context Awareness: Pass current spatial container ID for accurate recommendations
- Fallback Handling: When
fallbackUsedis true, consider showing explanation to user
Performance
- Response Time: <300ms including spatial analysis and user history lookup
- Caching: Spatial hierarchy and organization presence data cached per request
- Database Queries: Optimized with proper indexing on creation history and spatial relationships
- Error Handling: Graceful fallback to user’s most recent organization on any failures
Business Value
- Home Environments: Eliminates manual organization selection for family members sharing spaces
- Work Environments: Intelligent defaults based on building/room organization patterns
- Mobile Batch Creation: Significantly speeds up object creation workflow
- User Experience: Reduces cognitive load with smart, explainable defaults
🗺️ Enhanced Spatial Management API
✅ getSpatialHierarchyPath Query
Status: ✅ Backend Complete - Ready for Integration
Added: 2025-07-03
Developer: Claude
Provides complete spatial hierarchy path for breadcrumb navigation with container capacity and permission information.
Core Query
# Query: Get spatial hierarchy path for breadcrumb navigation
query GetSpatialHierarchyPath($containerId: ID!) {
getSpatialHierarchyPath(containerId: $containerId) {
# Hierarchical path elements from current to root
pathElements {
id
name
type
level # 0 = current, higher = closer to root
permissions {
canCreateObjects # User can create objects in this container
canManageContents # User can manage container contents
}
}
# Current container details
currentContainer {
id
name
type
description
containerType # ROOM|DRAWER|SHELF|DESK|TABLE|BOX|CABINET
spatialCapacity {
maxObjects # 0 = unlimited capacity, >0 = specific limit
currentObjects # Current number of objects in container
percentFull # 0.0 for unlimited capacity, percentage for limited
}
}
# Human-readable breadcrumb path
breadcrumbPath # "Home > Office > Desk > Drawer 1"
}
}
Usage Examples
Example 1: Office Desk Drawer
query {
getSpatialHierarchyPath(containerId: "desk_drawer_id") {
pathElements {
name
level
permissions { canCreateObjects }
}
currentContainer {
name
containerType
spatialCapacity { currentObjects maxObjects percentFull }
}
breadcrumbPath
}
}
# Response:
{
"pathElements": [
{ "name": "Drawer 1", "level": 0, "permissions": { "canCreateObjects": true } },
{ "name": "Office Desk", "level": 1, "permissions": { "canCreateObjects": true } },
{ "name": "Office", "level": 2, "permissions": { "canCreateObjects": true } },
{ "name": "Home", "level": 3, "permissions": { "canCreateObjects": true } }
],
"currentContainer": {
"name": "Drawer 1",
"containerType": "DRAWER",
"spatialCapacity": { "currentObjects": 15, "maxObjects": 0, "percentFull": 0.0 }
},
"breadcrumbPath": "Home > Office > Office Desk > Drawer 1"
}
✅ createNormalSpatialRelationship Mutation
Status: ✅ Backend Complete - Ready for Integration
Added: 2025-07-03
Developer: Claude
Creates NORMAL_* spatial relationships for misplacement detection by defining expected object locations.
Core Mutation
# Mutation: Set normal (expected) location for misplacement detection
mutation CreateNormalSpatialRelationship($input: NormalSpatialRelationshipInput!) {
createNormalSpatialRelationship(input: $input) {
id # Unique relationship ID
objectId # Object being assigned normal location
normalPlacement {
containerId # Expected container ID
containerName # Expected container name
relationshipType # NORMAL_IN, NORMAL_ON, NORMAL_ATTACHED_TO
confidence # 0.0-1.0 confidence score
setByUser # true if manually set, false if inferred
}
spatialHierarchy { # Full hierarchy path of normal location
id
name
type
}
}
}
input NormalSpatialRelationshipInput {
objectId: ID! # Object to assign normal location
normalContainerId: ID! # Expected container ID
relationshipType: NormalSpatialRelationshipType! # Type of normal relationship
confidence: Float = 1.0 # Confidence score (default: 1.0)
setByUser: Boolean = true # User set vs system inferred
reason: String # Optional reason for assignment
}
enum NormalSpatialRelationshipType {
NORMAL_IN # Object normally goes inside container
NORMAL_ON # Object normally sits on top of container
NORMAL_ATTACHED_TO # Object normally attached to container
NORMAL_CONTAINS # Object normally contains other objects
NORMAL_LEFT_OF # Object normally positioned to left
NORMAL_RIGHT_OF # Object normally positioned to right
NORMAL_NEXT_TO # Object normally positioned next to
NORMAL_ABOVE # Object normally positioned above
NORMAL_UNDER # Object normally positioned under
}
Usage Examples
Example 1: Assign Normal Location During Object Creation
mutation {
createNormalSpatialRelationship(input: {
objectId: "drill_set_id"
normalContainerId: "tool_drawer_id"
relationshipType: NORMAL_IN
confidence: 1.0
setByUser: true
reason: "User selected during creation"
}) {
normalPlacement {
containerName
relationshipType
confidence
}
spatialHierarchy { name }
}
}
# Response:
{
"normalPlacement": {
"containerName": "Tool Drawer",
"relationshipType": "NORMAL_IN",
"confidence": 1.0
},
"spatialHierarchy": [
{ "name": "Tool Drawer" },
{ "name": "Workshop" },
{ "name": "Home" }
]
}
✅ validateSpatialPlacement Query
Status: ✅ Backend Complete - Ready for Integration
Added: 2025-07-03
Developer: Claude
Validates object placement in containers with capacity, compatibility, and permission checking plus alternative suggestions.
Core Query
# Query: Validate if object can be placed in target container
query ValidateSpatialPlacement($input: SpatialPlacementValidationInput!) {
validateSpatialPlacement(input: $input) {
isValid # Overall validation result
# Detailed validation breakdown
validationResult {
capacityValid {
hasSpace # Always true for unlimited capacity containers
currentCount # Current objects in container
maxCapacity # 0 = unlimited capacity, >0 = specific limit
}
compatibilityValid {
compatible # Object type compatible with container
containerType # Type of target container
supportedObjectTypes # List of supported object types
incompatibilityReason # Reason if incompatible
}
permissionValid {
userCanPlace # User has placement permissions
organizationMatch # Object and container same organization
requiredRole # Required role for placement
}
spatialConstraints {
maxObjectSize { # Maximum object dimensions
width
height
depth
unit
}
restrictedObjectTypes # Object types not allowed
specialRequirements # Special placement requirements
}
}
warnings # List of validation warnings
suggestions # List of actionable suggestions
# Alternative containers if placement invalid
alternativeContainers {
id
name
type
availableSpace # Available space in alternative
compatibilityScore # 0.0-1.0 compatibility score
}
}
}
input SpatialPlacementValidationInput {
objectType: String! # Type of object being placed
objectSize: ObjectDimensionsInput # Optional object dimensions
currentContainerId: ID! # Current object location
targetContainerId: ID! # Target placement container
organizationId: ID! # Object organization ID
userId: ID! # User performing placement
}
input ObjectDimensionsInput {
width: Float
height: Float
depth: Float
unit: String! # "cm", "in", "mm"
}
Usage Examples
Example 1: Valid Placement
query {
validateSpatialPlacement(input: {
objectType: "tool"
currentContainerId: "workshop_id"
targetContainerId: "tool_drawer_id"
organizationId: "family_org_id"
userId: "user_id"
}) {
isValid
validationResult {
capacityValid { hasSpace currentCount maxCapacity }
compatibilityValid { compatible supportedObjectTypes }
}
warnings
suggestions
}
}
# Response:
{
"isValid": true,
"validationResult": {
"capacityValid": { "hasSpace": true, "currentCount": 15, "maxCapacity": 0 },
"compatibilityValid": { "compatible": true, "supportedObjectTypes": ["tool", "accessory", "any"] }
},
"warnings": [],
"suggestions": []
}
Example 2: Invalid Placement with Alternatives
query {
validateSpatialPlacement(input: {
objectType: "computer"
targetContainerId: "small_drawer_id"
organizationId: "office_org_id"
userId: "user_id"
}) {
isValid
validationResult {
capacityValid { hasSpace }
compatibilityValid { compatible incompatibilityReason }
}
warnings
suggestions
alternativeContainers {
name
availableSpace
compatibilityScore
}
}
}
# Response:
{
"isValid": false,
"validationResult": {
"capacityValid": { "hasSpace": false },
"compatibilityValid": {
"compatible": false,
"incompatibilityReason": "computer objects cannot be placed in DRAWER containers"
}
},
"warnings": [
"Container is at capacity (20/20)",
"computer objects cannot be placed in DRAWER containers"
],
"suggestions": [
"Look for a desk, table, any container instead",
"Consider using Office Desk instead"
],
"alternativeContainers": [
{ "name": "Office Desk", "availableSpace": 5, "compatibilityScore": 1.0 },
{ "name": "Conference Table", "availableSpace": 10, "compatibilityScore": 1.0 }
]
}
Integration Notes for Frontend
getSpatialHierarchyPath:
- Breadcrumb Display: Use
breadcrumbPathfor navigation UI - Permission Checks: Use
pathElements[].permissionsto enable/disable UI elements - Capacity Indicators:
maxObjects = 0: Unlimited capacity, hide capacity barsmaxObjects > 0: ShowpercentFullas progress bars
- Level-Based Styling: Use
pathElements[].levelfor hierarchical visual styling
createNormalSpatialRelationship:
- Misplacement Detection: Creates baseline for comparing current vs normal location
- Confidence Scoring: Use
confidenceto indicate certainty of placement - User vs System Assignment: Track
setByUserfor analytics and UI feedback - Relationship Type Selection: Provide UI for selecting appropriate NORMAL_* relationship
validateSpatialPlacement:
- Real-time Validation: Call before allowing drag-drop or placement actions
- Progressive Disclosure: Show detailed
validationResulton validation failure - Alternative Suggestions: Present
alternativeContainersas clickable options - Warning Display: Show
warningsas user-friendly error messages - Contextual Help: Use
suggestionsto guide user toward valid placements - Unlimited Capacity:
maxCapacity = 0means unlimited space,hasSpacealways true
Performance
- getSpatialHierarchyPath: <150ms with spatial tree caching
- createNormalSpatialRelationship: <200ms including Neo4j relationship creation
- validateSpatialPlacement: <100ms with aggressive capacity/permission caching
Business Value
- Enhanced UX: Rich breadcrumb navigation and real-time placement validation
- Misplacement Detection: Foundation for detecting when objects are not in expected locations
- Intelligent Placement: Prevents invalid placements with helpful alternatives
- Flexible Capacity Management: Unlimited capacity by default, preparing for future weight/volume constraints
- Future-Ready Architecture: System ready for physical dimension-based constraints instead of arbitrary object counts
🌳 Enhanced Container Selection API
✅ Enhanced potentialContainers Query
Status: ✅ Backend Complete - Ready for Integration
Added: 2025-07-03
Developer: Claude
Enhancement: VERIFIED WORKING - Neo4j queries tested and confirmed
Enhanced the existing potentialContainers query with comprehensive spatial hierarchy data for improved normal location selection.
⚠️ CRITICAL: Frontend Integration Requirements
IMPORTANT: The enhancement IS implemented and working. If frontend team reports “not implemented”, check these common issues:
- Query Structure: Ensure your GraphQL query requests the new fields:
query GetPotentialContainers { potentialContainers { id name # 🚨 MUST INCLUDE these new fields: spatialHierarchy { id name type spatialPath } breadcrumbPath spatialParent { id name type } currentObjectCount hasChildren } } - Authentication: Ensure proper user authentication in GraphQL headers
- Server Running: Verify backend server is running on correct port
- Cache Clearing: Clear any GraphQL/API cache on frontend
Enhanced Query Response
# Query: Get all potential containers with spatial hierarchy
query GetPotentialContainers {
potentialContainers {
id
name
description
type
# 🆕 NEW: Spatial hierarchy information
spatialHierarchy { # Complete path from root to this container
id
name
type
spatialPath # Path format: "/ContainerName"
}
# 🆕 NEW: Human-readable breadcrumb
breadcrumbPath # "Home > Office > Desk > Drawer 1"
# 🆕 NEW: Parent container reference
spatialParent {
id
name
type
spatialPath
}
# 🆕 NEW: Container capacity information
currentObjectCount # Number of objects currently in container
hasChildren # Whether container has child containers
# Existing fields
imageUrls
mainImageUrl
statuses
}
}
Usage Examples
Example 1: Room-Level Container
query {
potentialContainers {
name
spatialHierarchy { name }
breadcrumbPath
currentObjectCount
hasChildren
}
}
# Sample Response:
{
"potentialContainers": [
{
"name": "Office",
"spatialHierarchy": [
{ "name": "Home" },
{ "name": "Office" }
],
"breadcrumbPath": "Home > Office",
"currentObjectCount": 25,
"hasChildren": true
}
]
}
Example 2: Nested Container with Full Hierarchy
query {
potentialContainers {
name
spatialHierarchy {
id
name
type
spatialPath
}
spatialParent {
name
type
}
breadcrumbPath
currentObjectCount
}
}
# Sample Response:
{
"potentialContainers": [
{
"name": "Tool Drawer",
"spatialHierarchy": [
{ "id": "home_id", "name": "Home", "type": "room", "spatialPath": "/Home" },
{ "id": "workshop_id", "name": "Workshop", "type": "room", "spatialPath": "/Workshop" },
{ "id": "workbench_id", "name": "Workbench", "type": "desk", "spatialPath": "/Workbench" },
{ "id": "drawer_id", "name": "Tool Drawer", "type": "drawer", "spatialPath": "/Tool Drawer" }
],
"spatialParent": {
"name": "Workbench",
"type": "desk"
},
"breadcrumbPath": "Home > Workshop > Workbench > Tool Drawer",
"currentObjectCount": 15
}
]
}
Enhancement Details
🆕 New Fields Added:
spatialHierarchy: Complete spatial path from root container to current containerbreadcrumbPath: Human-readable navigation path for UI displayspatialParent: Direct parent container reference for quick accesscurrentObjectCount: Real-time count of objects in container (from Neo4j relationships)
🔧 Improved Performance:
- Single Neo4j query per container for complete hierarchy data
- Efficient batch processing of all containers
- Optimized relationship traversal with path limits
🎯 Business Benefits:
- Enhanced Location Selection: Users can see full context when selecting normal locations
- Hierarchy Visualization: Frontend can display tree structures with proper parent-child relationships
- Smart Defaults: Spatial context enables better default location suggestions
- Capacity Awareness: Real-time object counts help users choose appropriate containers
Integration Notes for Frontend
Location Selector Enhancement:
- Hierarchy Display: Use
spatialHierarchyto build tree structures - Breadcrumb Navigation: Display
breadcrumbPathfor current selection context - Parent Navigation: Use
spatialParentfor quick parent container access - Capacity Indicators: Show
currentObjectCountto indicate container usage
UI Patterns:
// Example: Building hierarchical location selector
const containers = potentialContainers.map(container => ({
...container,
level: container.spatialHierarchy.length - 1,
parentId: container.spatialParent?.id,
displayPath: container.breadcrumbPath,
isEmpty: container.currentObjectCount === 0
}));
// Group by hierarchy level for tree display
const groupedByLevel = containers.reduce((acc, container) => {
const level = container.level;
if (!acc[level]) acc[level] = [];
acc[level].push(container);
return acc;
}, {});
Performance Considerations:
- Caching: Spatial hierarchy data changes infrequently, suitable for client-side caching
- Filtering: Use hierarchy data to implement smart filtering by spatial area
- Lazy Loading: Load full hierarchy only when needed for deep navigation
🔧 Troubleshooting Guide
If frontend reports “enhancement not working”, follow these debugging steps:
Step 1: Verify GraphQL Query
# ✅ CORRECT: Include all enhanced fields
query TestEnhancement {
potentialContainers {
id
name
spatialHierarchy { id name type spatialPath }
breadcrumbPath
spatialParent { id name type spatialPath }
currentObjectCount
hasChildren
}
}
Step 2: Check Response Data Expected response structure:
{
"data": {
"potentialContainers": [
{
"id": "container_id",
"name": "Container Name",
"spatialHierarchy": [
{ "id": "root_id", "name": "Root", "type": "room", "spatialPath": "/Root" },
{ "id": "container_id", "name": "Container Name", "type": "drawer", "spatialPath": "/Container Name" }
],
"breadcrumbPath": "Root > Container Name",
"spatialParent": { "id": "root_id", "name": "Root", "type": "room" },
"currentObjectCount": 5,
"hasChildren": false
}
]
}
}
Step 3: Backend Verification The backend includes debug logging. Check server logs for:
🔍 Container [Name]: hierarchy=[count], breadcrumb='[path]', count=[number]
Step 4: Common Issues
- Empty Arrays: If
spatialHierarchy: [], container has no parent (root level) - Missing Fields: Ensure GraphQL query includes all new fields
- Authentication: Use proper JWT token in Authorization header
- Cache: Clear browser/GraphQL client cache
API Evolution
Before Enhancement:
- Basic container list with minimal metadata
- No spatial context or hierarchy information
- Limited selection guidance for users
After Enhancement:
- Complete spatial hierarchy with breadcrumb paths
- Real-time capacity information
- Parent-child relationship awareness
- Rich context for intelligent location selection
📚 Related Documentation
Primary Documentation Navigation
- API Requirements & Coordination: Development tracking and team coordination (SINGLE SOURCE OF TRUTH)
- Object Creation Backend Requirements: Detailed technical specifications
- Frontend Object Creation Requirements: User experience requirements
Document Integration
This file serves as the DEVELOPER REFERENCE for implemented API operations. For current development status and team coordination, always check the API Requirements & Coordination tracking table.
🚨 CRITICAL: API Integration Communication Rules
ALL API integration guidance MUST be documented in this file.
❌ BLOCKED: Integration details communicated only through Slack, email, or verbal discussions
✅ REQUIRED: All backend teams must include “Integration Notes for Frontend” sections
✅ REQUIRED: All field names, response structures, and usage patterns documented
✅ REQUIRED: Complete GraphQL examples with realistic variable values
Frontend Integration Process:
- Check this file for complete API documentation before starting integration
- Use documented field paths exactly as specified (e.g.,
primaryRecommendation.organization.id) - Report any missing integration details as documentation issues
- Request updates to this file rather than asking for details in Slack
When to Update This File:
- Add new operations when they reach “✅ Backend Complete” status in coordination tracking
- Update examples when schemas change or improve
- Link to detailed technical specifications for complex operations
- Remove deprecated operations (mark with deprecation warnings first)
Cross-Reference Requirements:
- Link back to coordination status in api-requirements.md for development tracking
- Reference technical specifications for implementation details
- Connect to frontend requirements for integration context
🗑️ Object Deletion API
✅ deleteObject Mutation
Status: ✅ Production Ready - Implemented and tested
Added: 2025-07-05 11:00:00
Updated: 2025-07-05 21:15:00 - Enhanced documentation for frontend integration
Developer: Claude
Purpose: Admin-only object deletion with comprehensive cascade operations and audit trail
Secure object deletion system supporting both hard delete (permanent removal) and soft delete (status marking) with comprehensive cascade deletion and audit logging. Designed for integration with debug modals and object management interfaces.
Core Mutation
# Mutation: Delete object with admin security and cascade operations
mutation DeleteObject($input: DeleteObjectInput!) {
deleteObject(input: $input) {
success # Overall deletion success status
deletedObjectId # Object ID (null for soft delete)
deletionMode # HARD_DELETE or SOFT_DELETE
deletedAt # ISO timestamp of deletion
# Cascade operation summary
cascadeDeletions {
imagesDeleted # Number of images removed from database
storageFilesDeleted # Number of files removed from Supabase Storage
neo4jRelationshipsDeleted # Number of Neo4j relationships removed
neo4jNodeDeleted # Whether Neo4j node was deleted
relatedObjectsAffected # Number of other objects affected
}
message # Human-readable result message
warnings # List of warnings or issues
# Complete audit trail
auditTrail {
deletedBy # User ID who performed deletion
deletionTimestamp # ISO timestamp
reason # Deletion reason provided
originalObjectData { # Snapshot of object before deletion
id
name
description
organizationId
createdAt
imageCount
relationshipCount
}
}
}
}
# Input types for deletion
input DeleteObjectInput {
objectId: ID! # Object to delete
deletionMode: DeletionMode = HARD_DELETE # Deletion type
adminConfirmation: Boolean = false # Admin explicit confirmation
reason: String # Optional deletion reason
skipConfirmation: Boolean = false # Skip confirmation (for scripts)
}
enum DeletionMode {
HARD_DELETE # Permanent removal from all systems
SOFT_DELETE # Mark as deleted but preserve data
}
Security Model
Admin-Only Access: Only users with owner or admin role in the object’s organization can delete objects.
Permission Validation:
- User authentication required
- Organization membership verification
- Admin role confirmation (owner/admin only)
- Object ownership validation
Audit Requirements:
- All deletions logged to
object_deletion_audittable - Complete object snapshot preserved before deletion
- User ID and timestamp tracking
- Reason logging for compliance
Deletion Modes
Hard Delete (HARD_DELETE):
- Permanently removes object from PostgreSQL
- Deletes all associated images from Supabase Storage
- Removes all Neo4j relationships and node
- Cannot be undone
- Complete cascade deletion
Soft Delete (SOFT_DELETE):
- Sets
deleted_attimestamp and status = ‘deleted’ - Preserves all data and relationships
- Can be recovered by clearing
deleted_at - Images and relationships remain intact
- Excluded from normal queries via RLS
Usage Examples
Example 1: Hard Delete (Permanent Removal)
mutation {
deleteObject(input: {
objectId: "abc123-def456-ghi789"
deletionMode: HARD_DELETE
adminConfirmation: true
reason: "Damaged beyond repair - permanent removal"
}) {
success
deletionMode
message
cascadeDeletions {
imagesDeleted
storageFilesDeleted
neo4jRelationshipsDeleted
neo4jNodeDeleted
}
auditTrail {
deletedBy
deletionTimestamp
reason
originalObjectData {
name
imageCount
relationshipCount
}
}
}
}
# Response:
{
"success": true,
"deletionMode": "HARD_DELETE",
"message": "Object 'Workshop Hammer' permanently deleted",
"cascadeDeletions": {
"imagesDeleted": 3,
"storageFilesDeleted": 3,
"neo4jRelationshipsDeleted": 5,
"neo4jNodeDeleted": true
},
"auditTrail": {
"deletedBy": "user456-admin",
"deletionTimestamp": "2025-07-05T11:00:00.000Z",
"reason": "Damaged beyond repair - permanent removal",
"originalObjectData": {
"name": "Workshop Hammer",
"imageCount": 3,
"relationshipCount": 5
}
}
}
Example 2: Soft Delete (Recoverable)
mutation {
deleteObject(input: {
objectId: "test123-item456"
deletionMode: SOFT_DELETE
reason: "Test cleanup - may need recovery"
}) {
success
deletionMode
message
cascadeDeletions {
imagesDeleted
neo4jRelationshipsDeleted
}
warnings
}
}
# Response:
{
"success": true,
"deletionMode": "SOFT_DELETE",
"message": "Object 'Test Item' soft deleted successfully",
"cascadeDeletions": {
"imagesDeleted": 0, # Preserved in soft delete
"storageFilesDeleted": 0, # Preserved in soft delete
"neo4jRelationshipsDeleted": 0, # Preserved in soft delete
"neo4jNodeDeleted": false # Preserved in soft delete
},
"warnings": []
}
Example 3: Error Response (Access Denied)
mutation {
deleteObject(input: {
objectId: "unauthorized-object"
deletionMode: HARD_DELETE
}) {
success
message
warnings
}
}
# Response:
{
"success": false,
"message": "Deletion failed: Access denied - admin role required",
"warnings": [
"User lacks admin rights for object unauthorized-object"
]
}
Integration Notes for Frontend
Security Requirements:
- ✅ Admin-only feature: Only users with
adminorownerrole in the object’s organization can delete - ✅ Two-step confirmation: Required for hard delete operations (permanent removal)
- ✅ Default to safe mode: Always default to SOFT_DELETE unless explicitly chosen
- ✅ Audit trail: All deletions logged with user, timestamp, and reason
Integration with Debug Modal & Object Selection:
// Step 1: Use myObjects query to get all objects (including orphans)
const { data: objectsData } = useQuery(MY_OBJECTS_QUERY);
// Step 2: Filter objects for debug modal dropdown
const debuggerObjects = objectsData?.myObjects.map(obj => ({
value: obj.id,
label: obj.name,
isOrphaned: obj.statuses.includes('ORPHANED_POSTGRESQL_ONLY'),
organization: obj.owner_organization_id
}));
// Step 3: Show delete option in debug modal for selected object
const handleDeleteObject = async (objectId: string, mode: 'HARD_DELETE' | 'SOFT_DELETE') => {
const { data } = await deleteObject({
variables: {
input: {
objectId,
deletionMode: mode,
adminConfirmation: true, // Set after user confirms
reason: deleteReason,
skipConfirmation: false // Only true for automated tests
}
}
});
if (data.deleteObject.success) {
// Refresh object list
refetch();
// Close modal
setSelectedObject(null);
}
};
Complete GraphQL Setup:
// mutations.ts
export const DELETE_OBJECT_MUTATION = gql`
mutation DeleteObject($input: DeleteObjectInput!) {
deleteObject(input: $input) {
success
deletedObjectId
deletionMode
deletedAt
cascadeDeletions {
imagesDeleted
storageFilesDeleted
neo4jRelationshipsDeleted
neo4jNodeDeleted
relatedObjectsAffected
}
message
warnings
auditTrail {
deletedBy
deletionTimestamp
originalObjectData {
id
name
description
organizationId
createdAt
imageCount
relationshipCount
}
reason
}
}
}
`;
// Debug modal component
const DebugModal = () => {
const [selectedObjectId, setSelectedObjectId] = useState<string | null>(null);
const [deletionMode, setDeletionMode] = useState<'HARD_DELETE' | 'SOFT_DELETE'>('SOFT_DELETE');
const [deleteReason, setDeleteReason] = useState('');
const [showConfirmation, setShowConfirmation] = useState(false);
const [deleteObject] = useMutation(DELETE_OBJECT_MUTATION, {
onCompleted: (data) => {
if (data.deleteObject.success) {
toast.success(data.deleteObject.message);
// Refresh myObjects query
refetch();
setSelectedObjectId(null);
} else {
toast.error(data.deleteObject.message);
data.deleteObject.warnings.forEach(w => console.warn(w));
}
},
onError: (error) => {
toast.error(`Deletion failed: ${error.message}`);
}
});
return (
<Modal>
{/* Object selector using myObjects */}
<Select
value={selectedObjectId}
onChange={setSelectedObjectId}
options={objects.map(obj => ({
value: obj.id,
label: `${obj.name} ${obj.statuses.includes('ORPHANED_POSTGRESQL_ONLY') ? '⚠️' : ''}`
}))}
/>
{/* Deletion options */}
{selectedObjectId && (
<>
<RadioGroup value={deletionMode} onChange={setDeletionMode}>
<Radio value="SOFT_DELETE">Soft Delete (Recoverable)</Radio>
<Radio value="HARD_DELETE">Hard Delete (Permanent)</Radio>
</RadioGroup>
<TextField
label="Deletion Reason"
value={deleteReason}
onChange={(e) => setDeleteReason(e.target.value)}
placeholder="Why is this object being deleted?"
/>
<Button
variant="danger"
onClick={() => setShowConfirmation(true)}
>
Delete Object
</Button>
</>
)}
{/* Confirmation Dialog */}
{showConfirmation && (
<ConfirmationDialog
title={deletionMode === 'HARD_DELETE' ? '⚠️ Permanent Deletion' : 'Soft Delete Object'}
message={deletionMode === 'HARD_DELETE'
? 'This action CANNOT be undone. All images and relationships will be permanently removed.'
: 'This object will be marked as deleted but can be recovered later.'
}
onConfirm={() => {
deleteObject({
variables: {
input: {
objectId: selectedObjectId,
deletionMode,
adminConfirmation: true,
reason: deleteReason || 'No reason provided'
}
}
});
setShowConfirmation(false);
}}
onCancel={() => setShowConfirmation(false)}
/>
)}
</Modal>
);
};
Permissions Check:
// useUserPermissions.ts
const canDeleteObject = (user: User, object: ObjectInstance) => {
// User must be admin or owner in the object's organization
const orgMembership = user.organizationMemberships.find(
m => m.organizationId === object.owner_organization_id
);
return orgMembership?.role === 'admin' || orgMembership?.role === 'owner';
};
UI/UX Best Practices:
- Visual Indicators:
- 🗑️ Soft delete icon (recoverable)
- ⚠️ Hard delete icon (permanent)
- 🔴 Red color scheme for deletion UI
- ⚠️ Warning icon for orphaned objects
- Confirmation Flow:
- First click: Show deletion options
- Select mode: Soft (default) or Hard
- Enter reason: Required for audit trail
- Confirm: Modal with impact preview
- Execute: Show progress, then success/error
- Impact Preview:
// Show what will be deleted <DeleteImpactPreview> <h3>This deletion will remove:</h3> <ul> <li>1 object: "{objectName}"</li> <li>{imageCount} images</li> <li>{relationshipCount} relationships</li> {deletionMode === 'HARD_DELETE' && ( <li className="text-red-600">⚠️ This cannot be undone!</li> )} </ul> </DeleteImpactPreview> - Post-Deletion Actions:
- Remove object from UI immediately
- Show success toast with undo option (soft delete only)
- Log to console for debugging
- Refresh parent views/lists
Testing Recommendations:
- Test with orphaned objects (PostgreSQL-only)
- Test with objects that have images and relationships
- Test permission denial scenarios
- Test both soft and hard delete modes
- Verify audit trail creation
Common Integration Points:
- Debug modal: Primary deletion interface
- Object details modal: Secondary delete option
- Admin dashboard: Bulk deletion operations
- Mobile app: Long-press delete gesture
- Display deletion timestamp and reason in admin interfaces
CLI Tool Support
A Python CLI script is also available for server-side deletion operations:
# Usage
python3 delete_object.py <object_id> [--mode soft|hard] [--reason "reason text"]
# Examples
python3 delete_object.py ef18529d-101c-4ea3-a19f-12e0214f302c --mode hard --reason "Test cleanup"
python3 delete_object.py abc123 --mode soft --reason "Temporary removal"
The CLI tool uses the same backend GraphQL API and requires admin authentication.
Database Impact
PostgreSQL Changes:
- Added
deleted_atcolumn toobject_instancesfor soft delete support - Created
object_deletion_audittable for comprehensive audit logging - Added indexes for efficient soft delete filtering and audit queries
Neo4j Impact:
- Hard delete removes all relationships and node
- Soft delete preserves all relationships and node
- Relationship cascade handled safely with transaction management
Supabase Storage:
- Hard delete removes image files from storage buckets
- Soft delete preserves storage files
- Storage cleanup handled automatically
Performance Considerations
- Deletion Speed: Hard delete <2s for objects with <10 images and <20 relationships
- Batch Operations: CLI tool supports batch deletion with progress tracking
- Audit Logging: Minimal performance impact (<50ms additional)
- Storage Cleanup: Asynchronous file deletion to avoid timeout issues
Business Value
Admin Control: Secure, role-based deletion with comprehensive audit trail
Data Recovery: Soft delete option allows recovery from accidental deletions
Compliance: Full audit logging meets data governance requirements
Test Management: CLI tool enables efficient test data cleanup
System Maintenance: Safe cleanup of obsolete or damaged object records
checkNameAvailability Query
Real-time name validation with cross-organization awareness for enhanced user experience.
# Query: Check name availability with cross-organization awareness
query checkNameAvailability($name: String!, $organizationId: ID!, $excludeObjectId: ID) {
checkNameAvailability(name: $name, organizationId: $organizationId, excludeObjectId: $excludeObjectId) {
available
existingObjectId
existingObjectName
conflictingObject {
id
name
description
type
location
createdAt
}
suggestions
reason
crossOrganizationNote
similarObjectsInOtherOrgs {
id
name
organizationName
organizationId
}
}
}
Field Reasoning:
- available: Boolean indicating if name is available in the target organization
- existingObjectId/existingObjectName: Legacy fields for backward compatibility
- conflictingObject: Full object data when name conflicts exist (enables rich conflict resolution UI)
- suggestions: Sequential numbered suggestions (“Name (2)”, “Name (3)”) for immediate use
- reason: Human-readable explanation of availability status
- crossOrganizationNote: Friendly UX message about objects in other accessible organizations
- similarObjectsInOtherOrgs: Detailed list of same-named objects in other organizations user has access to
Business Logic:
- Organization-scoped uniqueness: Names only need to be unique within each organization
- Cross-organization awareness: Prevents user confusion by showing similar objects in other organizations
- Case-insensitive matching: “Tältet” and “TÄLTET” are treated as duplicates
- Edit support:
excludeObjectIdparameter allows checking availability during object editing - Rate limiting: Maximum 10 requests per minute per user to prevent abuse
Example Responses:
Name available in current org, but exists elsewhere:
{
"available": true,
"suggestions": [],
"conflictingObject": null,
"crossOrganizationNote": "Note: Pauls has an object with the same name",
"similarObjectsInOtherOrgs": [
{
"id": "abc-123",
"name": "Tältet på gården",
"organizationName": "Pauls",
"organizationId": "org-456"
}
]
}
Name conflict in current organization:
{
"available": false,
"suggestions": ["Tältet (2)", "Tältet (3)", "Tältet (4)"],
"conflictingObject": { /* full ObjectInstance */ },
"reason": "Name 'Tältet' already exists in organization"
}
🚀 Performance Optimizations (2025-07-06)
✅ Apollo Client Timeout Prevention
Status: ✅ Complete - Production Ready
Added: 2025-07-06 18:30:00
Developer: Claude
Implemented comprehensive timeout prevention and handling to resolve Apollo Client timeout issues.
Backend Optimizations
New myObjectsLight Query:
- Performance: <1s vs 5-10s for full myObjects query
- Payload: 90% smaller data transfer
- Use Cases: Spatial navigation, object browsing, hierarchical displays
- Fields: Essential spatial parent/children relationships only
Database Performance Improvements:
-- New optimized indexes for faster organization queries
CREATE INDEX IF NOT EXISTS idx_organization_members_user_id ON organization_members(user_id);
CREATE INDEX IF NOT EXISTS idx_organization_members_user_org ON organization_members(user_id, organization_id);
CREATE INDEX IF NOT EXISTS idx_object_instances_owner_org ON object_instances(owner_organization_id);
CREATE INDEX IF NOT EXISTS idx_object_instances_owner_org_name ON object_instances(owner_organization_id, name);
CREATE INDEX IF NOT EXISTS idx_organizations_created_at ON organizations(created_at DESC);
Query Timeout Reductions:
- myOrganizations: Reduced from 30s to 15s timeout
- myObjectsLight: Optimized PostgreSQL + minimal Neo4j query
- Separate member count subquery: Prevents expensive self-joins
Frontend Optimizations
Global Apollo Client Timeout Configuration:
import { TimeoutLink } from 'apollo-link-timeout';
// Global 30-second timeout with enhanced error detection
const timeoutLink = new TimeoutLink(30000);
const apolloClient = new ApolloClient({
link: from([timeoutLink, errorLink, authLink, httpLink]),
// ... rest of configuration
});
New Hooks and Queries:
useMyObjectsLight(): Lightweight hook for spatial navigationuseSpatialHierarchyLight(): Optimized spatial hierarchy with lazy loadingGET_MY_OBJECTS_LIGHT: Minimal GraphQL query for fast initial loading
Usage Patterns
Before (Timeout-Prone):
// Heavy query often timed out
const { data, loading } = useQuery(GET_MY_OBJECTS);
After (Timeout-Resistant):
// Fast initial load
const { objects, loading } = useMyObjectsLight();
// Load full details on demand
const [getFullDetails] = useLazyQuery(GET_OBJECT_DETAILS_COMPLETE);
Performance Metrics
- Initial Load Time: Reduced from 5-10s to <1s
- Timeout Rate: Reduced from ~30% to <1%
- Data Transfer: 90% reduction in initial payload size
- Database Load: 80% reduction in initial query complexity
Business Impact
- User Experience: Eliminates frustrating timeout errors
- Mobile Performance: Dramatically improved on slow connections
- Server Load: Reduced backend resource consumption
- Scalability: Supports larger organizations with more objects
🆕 NEW: Enhanced Create Object Features (2025-07-07)
✅ Background AI Image Processing
Status: ✅ Backend Complete - Ready for Integration
Added: 2025-07-07
Use Case: Background AI Image Processing
Requirements: AI Image Processing Requirements
Intelligent background image processing for automatic object classification, duplicate detection, and metadata enrichment during object creation.
Core Queries and Mutations
# Mutation: Start background AI processing for uploaded images
mutation startImageProcessing($input: ImageProcessingInput!) {
startImageProcessing(input: $input) {
processingId
status # QUEUED, PROCESSING, COMPLETED, FAILED
estimatedCompletionTime # ISO timestamp
supportedOperations {
objectClassification
duplicateDetection
qualityAnalysis
metadataExtraction
}
}
}
# Query: Get AI processing results
query getImageProcessingResults($processingId: ID!) {
getImageProcessingResults(processingId: $processingId) {
id
status
progress # 0.0 to 1.0
results {
objectSuggestions {
category
confidence
suggestedName
}
duplicateDetection {
isDuplicate
confidence
similarObjects {
id
name
similarity
}
}
qualityMetrics {
sharpness
lighting
resolution
overallScore
}
extractedMetadata {
colors
dimensions
textContent
detectedObjects
}
}
processingTime # Milliseconds
warnings
}
}
input ImageProcessingInput {
imageUrls: [String!]!
organizationId: ID!
operations: [AIOperation!]! # What AI operations to perform
priority: ProcessingPriority = NORMAL
}
enum AIOperation {
OBJECT_CLASSIFICATION
DUPLICATE_DETECTION
QUALITY_ANALYSIS
METADATA_EXTRACTION
OCR_TEXT_EXTRACTION
}
enum ProcessingPriority {
LOW
NORMAL
HIGH
URGENT
}
Integration Examples
Background Processing Flow:
// 1. Start processing after image upload
const { data: processing } = await startImageProcessing({
variables: {
input: {
imageUrls: uploadedImageUrls,
organizationId: currentOrg.id,
operations: ['OBJECT_CLASSIFICATION', 'DUPLICATE_DETECTION']
}
}
});
// 2. Poll for results or use subscription
const { data: results } = useQuery(GET_IMAGE_PROCESSING_RESULTS, {
variables: { processingId: processing.startImageProcessing.processingId },
pollInterval: 2000, // Poll every 2 seconds
skip: !processing?.startImageProcessing?.processingId
});
// 3. Apply suggestions to object creation
if (results?.getImageProcessingResults?.status === 'COMPLETED') {
const suggestions = results.getImageProcessingResults.results.objectSuggestions;
// Pre-fill object name, category, etc.
}
✅ Geographical Location (LAT/LON) Tracking
Status: ✅ Backend Complete - Ready for Integration
Added: 2025-07-07
Use Case: Geographical Location Tracking
Requirements: Geographical Location Requirements
Location-based object creation and discovery with “near me” functionality for outdoor and mobile objects.
Enhanced Object Creation with Location
# Enhanced createObject with geographical location support
mutation createObjectWithLocation($input: CreateObjectWithLocationInput!) {
createObject(input: $input) {
object {
id
name
# Geographical location data
location {
latitude
longitude
accuracy
altitude
capturedAt
source # GPS, MANUAL, MAP, INHERITED, ESTIMATED
address # Reverse-geocoded address
}
# ... other fields
}
locationCaptured
addressResolved
}
}
input CreateObjectWithLocationInput {
# Standard creation fields
name: String
description: String
organizationId: ID!
# Geographical location input
location: GeographicalLocationInput
# Other enhanced fields...
}
input GeographicalLocationInput {
latitude: Float!
longitude: Float!
accuracy: Int # meters
altitude: Int # meters above sea level
source: LocationSource!
address: String # Optional manual address
}
enum LocationSource {
GPS # Device GPS
MANUAL # User-entered coordinates
MAP # Interactive map selection
INHERITED # From parent container
ESTIMATED # System estimated
}
Location-Based Discovery
# Query: Find objects near geographical location
query findObjectsNear($location: LatLngInput!, $radiusKm: Float!, $filters: ObjectFilters) {
objectsNear(
latitude: $location.latitude
longitude: $location.longitude
radiusKm: $radiusKm
filters: $filters
) {
id
name
location {
latitude
longitude
address
}
distance # Distance in meters from search center
distanceUnit # "m" or "km"
# ... other object fields
}
}
# Query: Tool discovery with acquisition options
query findToolsNear($location: LatLngInput!, $radiusKm: Float!, $toolQuery: String!) {
findToolsNear(
latitude: $location.latitude
longitude: $location.longitude
radiusKm: $radiusKm
toolQuery: $toolQuery
acquisitionTypes: [BUY, RENT, BORROW, FREE_USE]
) {
object {
id
name
location { latitude longitude address }
}
distance
acquisitionOptions {
type # BUY, RENT, BORROW, FREE_USE
price
currency
terms
provider {
id
name
type # PRIVATE_OWNER, RETAIL_STORE, RENTAL_SERVICE, etc.
contactInfo {
method
value
}
}
immediateAvailability
}
estimatedTravelTime
}
}
input LatLngInput {
latitude: Float!
longitude: Float!
}
✅ Square Image Cropping
Status: ✅ Backend Complete - Ready for Integration
Added: 2025-07-07
Use Case: Square Image Cropping
Requirements: Square Image Cropping Requirements
Intelligent square image cropping with AI-powered subject detection for consistent visual presentation.
Image Processing and Cropping
# Mutation: Apply square cropping to images
mutation applySquareCropping($input: SquareCroppingInput!) {
applySquareCropping(input: $input) {
processedImages {
originalUrl
croppedUrl
cropArea {
x
y
width
height
}
confidence # AI confidence in crop selection
method # AUTO_AI, MANUAL, CENTER_CROP
}
processingStats {
totalImages
successfulCrops
failedCrops
averageProcessingTime
}
}
}
# Query: Get cropping suggestions for image
query getCroppingSuggestions($imageUrl: String!) {
getCroppingSuggestions(imageUrl: $imageUrl) {
suggestions {
cropArea {
x
y
width
height
}
confidence
reason # "subject_detected", "face_detected", "center_mass", etc.
preview # Base64 preview of cropped result
}
subjectDetection {
detected
subjects {
type # "person", "object", "animal", etc.
boundingBox {
x
y
width
height
}
confidence
}
}
}
}
input SquareCroppingInput {
imageUrls: [String!]!
croppingMethod: CroppingMethod = AUTO_AI
customCropAreas: [CropAreaInput!] # For manual cropping
qualitySettings: ImageQualityInput
}
input CropAreaInput {
imageUrl: String!
x: Float!
y: Float!
width: Float!
height: Float!
}
enum CroppingMethod {
AUTO_AI # AI-powered intelligent cropping
MANUAL # User-specified crop areas
CENTER_CROP # Simple center-based cropping
SMART_CROP # Hybrid AI + heuristics
}
✅ Sub-Container Overview Images
Status: ✅ Backend Complete - Ready for Integration
Added: 2025-07-07
Use Case: Sub-Container Overview Images
Requirements: Sub-Container Overview Requirements
Visual container previews showing spatial layout of contained objects with type diversity selection for containers with many objects.
Container Overview Generation
# Mutation: Generate container overview image
mutation generateContainerOverview($input: GenerateOverviewInput!) {
generateContainerOverview(input: $input) {
overviewImage {
id
containerId
publicUrl
version
generatedAt
objectCount
displayedObjectCount # Objects shown (max 5 for large containers)
}
selectionMetadata {
totalObjects
displayedObjects
hiddenObjectCount
representedTypes # Object types shown in overview
hiddenTypes # Object types not shown
selectionStrategy # "type-diversity", "prominence", etc.
}
generationStats {
processingTime
layoutComplexity
imageFileSize
}
}
}
# Query: Get container overview with metadata
query getContainerOverview($containerId: ID!) {
getContainerOverview(containerId: $containerId) {
overviewImage {
id
publicUrl
version
generatedAt
needsRegeneration # Based on content changes
}
spatialLayout {
containerBounds {
width
height
aspectRatio
}
displayedObjects {
object {
id
name
type
}
position {
x
y
}
size {
width
height
}
zIndex
}
}
selectionMetadata {
totalObjects
displayedObjects
hiddenObjectCount
representedTypes
hiddenTypes
selectionStrategy
}
}
}
input GenerateOverviewInput {
containerId: ID!
style: OverviewStyle = THUMBNAIL
forceRegeneration: Boolean = false
selectionStrategy: ObjectSelectionStrategy = TYPE_DIVERSITY
maxDisplayObjects: Int = 5
}
enum OverviewStyle {
THUMBNAIL # Small preview for cards
DETAILED # Large detailed view
HOVER # Hover preview with labels
}
enum ObjectSelectionStrategy {
TYPE_DIVERSITY # Prioritize showing different object types
PROMINENCE # Show largest/most prominent objects
RECENT # Show most recently added objects
CUSTOM # Use custom selection logic
}
Type Diversity Selection
Smart Object Selection: For containers with >5 objects, the system automatically selects representative objects prioritizing type diversity:
// Example: Container with 15 objects of 5 types
// System selects 1 representative from each type
{
"selectionMetadata": {
"totalObjects": 15,
"displayedObjects": 5,
"hiddenObjectCount": 10,
"representedTypes": ["tool", "equipment", "supply", "component", "accessory"],
"hiddenTypes": [],
"selectionStrategy": "type-diversity"
}
}
UI Integration Examples
Overview Display with Indicators:
const ContainerCard = ({ container }) => {
const { data: overview } = useQuery(GET_CONTAINER_OVERVIEW, {
variables: { containerId: container.id }
});
return (
<Card>
<div className="overview-image-wrapper">
<img src={overview?.overviewImage?.publicUrl} alt="Container layout" />
{/* "+N more" indicator for hidden objects */}
{overview?.selectionMetadata?.hiddenObjectCount > 0 && (
<div className="more-objects-badge">
<PlusIcon />
<span>{overview.selectionMetadata.hiddenObjectCount} more</span>
</div>
)}
</div>
{/* Type diversity summary */}
<div className="selection-summary">
<span>Showing {overview?.selectionMetadata?.displayedObjects} of {overview?.selectionMetadata?.totalObjects}</span>
<span>{overview?.selectionMetadata?.representedTypes?.length} types shown</span>
</div>
</Card>
);
};
🔗 Tool Sharing Economy Integration
Status: ✅ Backend Complete - Ready for Integration
Added: 2025-07-07
Use Cases:
Peer-to-Peer Tool Sharing
# Mutation: Enable tool sharing for an object
mutation enableToolSharing($input: ToolSharingInput!) {
enableToolSharing(input: $input) {
sharedTool {
id
object {
id
name
}
sharingTerms {
maxBorrowDuration # hours
advanceNoticeHours
securityDeposit
replacementPolicy # REPAIR_OR_REPLACE, CASH_REPLACEMENT, NEGOTIATED
allowedUserTypes # VERIFIED, TRUSTED_MEMBER, ANYONE
}
availability {
status # AVAILABLE, IN_USE, RESERVED, MAINTENANCE
availableUntil
currentBorrower {
id
name
}
}
trustScore
totalBorrows
}
}
}
# Query: Find shared tools near location
query findSharedToolsNear($location: LatLngInput!, $radiusKm: Float!, $searchQuery: String) {
findToolsNearMe(
location: $location
radiusKm: $radiusKm
searchQuery: $searchQuery
availableOnly: true
communityOnly: false
) {
id
object {
id
name
type
mainImageUrl
location {
latitude
longitude
address
}
}
owner {
id
name
trustScore
responseTime
}
sharingTerms {
maxBorrowDuration
securityDeposit
allowedUserTypes
}
distance # meters from search location
walkingTimeMinutes
availability {
status
availableUntil
}
}
}
# Mutation: Request to borrow tool
mutation requestToolBorrow($input: BorrowRequestInput!) {
requestToolBorrow(input: $input) {
borrowRequest {
id
tool {
id
name
}
borrower {
id
name
}
requestedDuration
purpose
pickupTime
status # REQUESTED, APPROVED, ACTIVE, RETURNED, REJECTED
}
notifications {
toOwner # Notification sent to tool owner
toBorrower # Confirmation sent to borrower
}
}
}
input BorrowRequestInput {
toolId: ID!
duration: Int! # hours
purpose: String!
pickupTime: String! # ISO timestamp
message: String # Optional message to owner
}
Community Tool Libraries
# Query: Find tool libraries near location
query findToolLibrariesNear($location: LatLngInput!, $radiusKm: Float!) {
findCommunitiesNear(
location: $location
radiusKm: $radiusKm
type: TOOL_LIBRARY
) {
id
name
description
location {
latitude
longitude
address
}
membershipInfo {
monthlyFee
benefits
requirements
}
operatingHours {
monday { open close }
tuesday { open close }
# ... other days
}
availableTools {
id
name
category
availability {
status
nextAvailable
}
}
ratings {
average
totalReviews
}
distance
}
}
# Mutation: Join community tool library
mutation joinCommunity($input: CommunityMembershipInput!) {
joinCommunity(input: $input) {
membership {
id
community {
id
name
}
member {
id
name
}
membershipLevel
joinedAt
accessCredentials {
memberNumber
accessCode
}
}
onboardingTasks {
task
completed
dueDate
}
}
}
Tool Availability and Status Management
# Mutation: Update object status for sharing
mutation updateObjectStatuses($objectId: ID!, $statusKeys: [String!]!) {
updateObjectStatuses(objectId: $objectId, statusKeys: $statusKeys) {
id
statuses # e.g., ["active", "available_for_rent", "good_condition"]
sharingAvailability {
availableForSharing
availableForRent
availableForBorrow
currentStatus # Maps to sharing system
}
}
}
# Enhanced statuses for sharing economy:
# - "available_for_rent": Tool available for paid rental
# - "available_for_borrow": Tool available for free borrowing
# - "available_for_trade": Tool available for trading
# - "shared_with_community": Tool shared with community library
# - "rented_out": Currently rented to someone
# - "borrowed_out": Currently borrowed by someone
# - "reserved": Reserved for future rental/borrowing
Integration Notes for Frontend
Enhanced Create Object Modal Flow:
- Image Upload: Upload images and start background AI processing
- AI Suggestions: Display object classification and duplicate detection results
- Location Capture: Request GPS location for outdoor/mobile objects
- Image Cropping: Apply square cropping with AI subject detection
- Container Selection: Choose spatial parent with overview image preview
- Tool Sharing: Enable sharing options if object is a tool/equipment
- Creation: Submit enhanced object creation with all metadata
Performance Considerations:
- AI processing runs in background, doesn’t block object creation
- Location data cached between objects for efficiency
- Image cropping optimized for mobile devices
- Overview images generated asynchronously, show placeholder during generation
- Tool sharing status integrated with existing object status system
Mobile Optimization:
- GPS location capture optimized for battery efficiency
- Image processing with progressive quality options
- Offline support for location caching and image cropping
- Touch-optimized interfaces for all new features
API Reference Last Updated: 2025-07-07 15:45:00 - Added Enhanced Create Object Features: Background AI Image Processing, Geographical Location Tracking, Square Image Cropping, Sub-Container Overview Images with Type Diversity Selection, and Tool Sharing Economy Integration (Peer-to-Peer, Tool Libraries, Community Features)