| ← Back to API Documentation | Main Documentation |
API Integration Examples
Created: Tue 29 Jul 2025 07:29:47 CEST
Document Version: 1.0 - Initial code examples and integration patterns
Security Classification: Public Technical Documentation
Target Audience: Frontend Developers, Backend Developers, Integration Engineers
Author: Paul Wisén
Overview
This document provides practical code examples and integration patterns for working with the Plings GraphQL API. All examples use the production endpoint at https://api.plings.io/graphql.
Authentication Examples
JWT Token Authentication
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
// Create authenticated Apollo client
const httpLink = createHttpLink({
uri: 'https://api.plings.io/graphql',
});
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem('plings_auth_token');
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
}
}
});
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache()
});
Login Flow
import { gql } from '@apollo/client';
const LOGIN_MUTATION = gql`
mutation Login($email: String!, $password: String!) {
login(email: $email, password: $password) {
token
user {
id
email
username
organizations {
id
name
role
}
}
}
}
`;
async function loginUser(email: string, password: string) {
try {
const { data } = await client.mutate({
mutation: LOGIN_MUTATION,
variables: { email, password }
});
// Store token for future requests
localStorage.setItem('plings_auth_token', data.login.token);
return data.login.user;
} catch (error) {
console.error('Login failed:', error);
throw error;
}
}
Object Management Examples
Creating Objects
const CREATE_OBJECT = gql`
mutation CreateObject($input: CreateObjectInput!) {
createObject(input: $input) {
success
object {
id
name
plingsPath
instanceKey
objectClass {
name
description
}
owner {
username
}
}
identifier {
value
qrCodeUrl
}
error {
message
code
}
}
}
`;
async function createNewObject(objectData: CreateObjectInput) {
const { data } = await client.mutate({
mutation: CREATE_OBJECT,
variables: { input: objectData }
});
if (data.createObject.success) {
return {
object: data.createObject.object,
qrCode: data.createObject.identifier.qrCodeUrl
};
} else {
throw new Error(data.createObject.error.message);
}
}
// Example usage
const newObject = await createNewObject({
name: "My Mountain Bike",
description: "Trek X-Caliber 8",
classId: "class-mountain-bike-uuid",
images: ["https://example.com/bike.jpg"]
});
Querying Objects
const GET_OBJECT_BY_ID = gql`
query GetObject($id: ID!) {
object(id: $id) {
id
name
description
status
objectClass {
name
description
}
spatialLocation {
id
name
path
}
currentLocation {
id
name
type
}
owner {
username
organization {
name
}
}
images {
url
altText
}
relationships {
type
relatedObject {
id
name
}
}
}
}
`;
async function getObjectDetails(objectId: string) {
const { data } = await client.query({
query: GET_OBJECT_BY_ID,
variables: { id: objectId }
});
return data.object;
}
Searching Objects
const SEARCH_OBJECTS = gql`
query SearchObjects($query: String!, $filters: ObjectFilters) {
searchObjects(query: $query, filters: $filters) {
total
objects {
id
name
description
objectClass {
name
}
owner {
username
}
images {
url
altText
}
}
}
}
`;
async function searchUserObjects(searchTerm: string, filters?: ObjectFilters) {
const { data } = await client.query({
query: SEARCH_OBJECTS,
variables: {
query: searchTerm,
filters: {
...filters,
ownedByCurrentUser: true
}
}
});
return data.searchObjects;
}
QR Code and Identifier Examples
Resolving Scanned Identifiers
const RESOLVE_IDENTIFIER = gql`
query ResolveIdentifier($instanceKey: String!, $path: String!) {
resolveIdentifier(instanceKey: $instanceKey, path: $path) {
type
objectId
classId
canEdit
object {
id
name
description
status
owner {
username
}
images {
url
}
}
}
}
`;
async function handleQRScan(scannedUrl: string) {
// Parse QR code URL
const url = new URL(scannedUrl);
const instanceKey = url.searchParams.get('i');
const path = url.searchParams.get('p');
if (!instanceKey || !path) {
throw new Error('Invalid QR code format');
}
const { data } = await client.query({
query: RESOLVE_IDENTIFIER,
variables: { instanceKey, path }
});
const result = data.resolveIdentifier;
switch (result.type) {
case 'KNOWN_OBJECT':
return { type: 'object', data: result.object };
case 'UNKNOWN_GENERAL':
return { type: 'create', data: { instanceKey, path } };
case 'UNKNOWN_SPECIFIC':
return { type: 'claim', data: { instanceKey, path, classId: result.classId } };
default:
throw new Error('Unknown identifier type');
}
}
Creating Scan Events
const CREATE_SCAN_EVENT = gql`
mutation CreateScanEvent($input: ScanEventInput!) {
createScanEvent(input: $input) {
id
timestamp
location {
latitude
longitude
}
}
}
`;
async function logScanEvent(instanceKey: string, location?: GeolocationPosition) {
const scanData: ScanEventInput = {
instanceKey,
timestamp: new Date().toISOString(),
context: 'mobile_app'
};
if (location) {
scanData.location = {
latitude: location.coords.latitude,
longitude: location.coords.longitude,
accuracy: location.coords.accuracy
};
}
await client.mutate({
mutation: CREATE_SCAN_EVENT,
variables: { input: scanData }
});
}
Spatial Relationship Examples
Managing Object Relationships
const UPDATE_SPATIAL_LOCATION = gql`
mutation UpdateSpatialLocation($objectId: ID!, $containerId: ID!) {
updateSpatialLocation(objectId: $objectId, containerId: $containerId) {
success
object {
id
spatialLocation {
id
name
path
}
}
}
}
`;
async function moveObjectToContainer(objectId: string, containerId: string) {
const { data } = await client.mutate({
mutation: UPDATE_SPATIAL_LOCATION,
variables: { objectId, containerId }
});
return data.updateSpatialLocation;
}
Querying Spatial Hierarchies
const GET_CONTAINER_CONTENTS = gql`
query GetContainerContents($containerId: ID!) {
object(id: $containerId) {
id
name
containedObjects {
id
name
objectClass {
name
}
images {
url
}
}
subContainers {
id
name
type
containedObjects {
id
name
}
}
}
}
`;
async function getContainerContents(containerId: string) {
const { data } = await client.query({
query: GET_CONTAINER_CONTENTS,
variables: { containerId }
});
return {
container: data.object,
objects: data.object.containedObjects,
subContainers: data.object.subContainers
};
}
Error Handling Patterns
GraphQL Error Handling
import { ApolloError } from '@apollo/client';
function handleGraphQLError(error: ApolloError) {
if (error.networkError) {
// Network connectivity issues
console.error('Network error:', error.networkError);
return { type: 'network', message: 'Connection failed. Please check your internet.' };
}
if (error.graphQLErrors.length > 0) {
const graphqlError = error.graphQLErrors[0];
switch (graphqlError.extensions?.code) {
case 'UNAUTHENTICATED':
// Redirect to login
localStorage.removeItem('plings_auth_token');
return { type: 'auth', message: 'Please log in again.' };
case 'FORBIDDEN':
return { type: 'permission', message: 'You don\'t have permission for this action.' };
case 'NOT_FOUND':
return { type: 'not_found', message: 'The requested item was not found.' };
default:
return { type: 'unknown', message: graphqlError.message };
}
}
return { type: 'unknown', message: 'An unexpected error occurred.' };
}
Retry Logic for Failed Requests
async function withRetry<T>(
operation: () => Promise<T>,
maxRetries: number = 3,
delay: number = 1000
): Promise<T> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
if (attempt === maxRetries) {
throw error;
}
// Exponential backoff
await new Promise(resolve => setTimeout(resolve, delay * Math.pow(2, attempt - 1)));
}
}
throw new Error('Max retries exceeded');
}
// Usage
const objectData = await withRetry(() => getObjectDetails(objectId));
Real-time Updates
GraphQL Subscriptions
const OBJECT_UPDATES = gql`
subscription ObjectUpdates($objectId: ID!) {
objectUpdated(objectId: $objectId) {
id
name
status
spatialLocation {
id
name
}
lastModified
}
}
`;
function subscribeToObjectUpdates(objectId: string, onUpdate: (object: any) => void) {
const subscription = client.subscribe({
query: OBJECT_UPDATES,
variables: { objectId }
}).subscribe({
next: ({ data }) => {
onUpdate(data.objectUpdated);
},
error: (error) => {
console.error('Subscription error:', error);
}
});
return () => subscription.unsubscribe();
}
TypeScript Type Definitions
Generated Types
// Auto-generated from GraphQL schema
export interface CreateObjectInput {
name: string;
description?: string;
classId: string;
images?: string[];
spatialLocationId?: string;
}
export interface ObjectFilters {
classId?: string;
ownedByCurrentUser?: boolean;
status?: ObjectStatus;
spatialLocationId?: string;
}
export enum ObjectStatus {
NORMAL = 'NORMAL',
FOR_SALE = 'FOR_SALE',
FOR_RENT = 'FOR_RENT',
LOST = 'LOST',
LENDABLE = 'LENDABLE'
}
export interface ScanEventInput {
instanceKey: string;
timestamp: string;
context: string;
location?: {
latitude: number;
longitude: number;
accuracy?: number;
};
}
Performance Optimization
Query Optimization with Fragments
const OBJECT_FRAGMENT = gql`
fragment ObjectDetails on Object {
id
name
description
status
objectClass {
id
name
}
owner {
username
}
images {
url
altText
}
}
`;
const GET_OBJECTS_LIST = gql`
query GetObjectsList($filters: ObjectFilters) {
objects(filters: $filters) {
...ObjectDetails
}
}
${OBJECT_FRAGMENT}
`;
Caching Strategies
const cache = new InMemoryCache({
typePolicies: {
Object: {
fields: {
containedObjects: {
merge(existing = [], incoming) {
return incoming;
}
}
}
}
}
});
Related Documentation
- Authentication - Authentication flows and security
- Data Models - Complete data structure specifications
- API Endpoints - GraphQL schema reference
- Error Handling - Comprehensive error handling guide