### Fetch Data with Spraypaint ORM (TypeScript) Source: https://context7.com/graphiti-api/spraypaint.js/llms.txt Shows basic data fetching operations using Spraypaint models. Includes examples for fetching all records with pagination metadata, finding a specific record by its ID and accessing its raw JSON:API response, and retrieving the first record in a collection. ```typescript // Fetch all records const { data: people, meta } = await Person.all() console.log(people) // Array of Person instances console.log(meta) // { stats: {}, pagination: { ... } } // Find a single record by ID const { data: person, raw } = await Person.find(1) console.log(person.id) // "1" console.log(person.firstName) // "John" console.log(raw) // Raw JSON:API response // Get the first record const { data: first } = await Person.first() if (first === null) { console.log("No records found") } ``` -------------------------------- ### JWT Authentication and Management in Spraypaint Source: https://context7.com/graphiti-api/spraypaint.js/llms.txt Explains how to manage JSON Web Tokens (JWT) for API authentication within Spraypaint. It covers setting, getting, and clearing the JWT, automatic inclusion in the Authorization header, and configuring custom JWT storage backends like localStorage or in-memory storage. ```typescript // Set JWT token for all requests Person.setJWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...") // Get current JWT const token = Person.getJWT() console.log(token) // Token is automatically included in Authorization header const { data: people } = await Person.all() // Request includes: Authorization: Bearer eyJhbGci... // Clear JWT Person.setJWT(null) // Configure custom JWT storage @Model({ baseUrl: "http://api.example.com", jwtStorage: "custom_token_key" // localStorage key }) class SecureModel extends ApplicationRecord {} // Disable JWT persistence (in-memory only) @Model({ jwtStorage: false, credentialStorageBackend: new InMemoryStorageBackend() }) class EphemeralModel extends ApplicationRecord {} // Generate custom auth header const authHeader = Person.generateAuthHeader(token) console.log(authHeader) // "Bearer eyJhbGci..." ``` -------------------------------- ### Define Models and Fetch Data with Spraypaint (TypeScript) Source: https://github.com/graphiti-api/spraypaint.js/blob/master/README.md This snippet shows how to define models like Person and Pet that extend SpraypaintBase, including attributes and relationships. It also demonstrates fetching data with various query parameters like where, page, per, sort, and includes. The code is written in TypeScript. ```typescript import { SpraypaintBase, Model, Attr, HasMany } from "spraypaint" @Model() class ApplicationRecord extends SpraypaintBase { static baseUrl = "http://localhost:3000" static apiNamespace = "/api/v1" } @Model() class Person extends ApplicationRecord { static jsonapiType = "people" @Attr() firstName: string @Attr() lastName: string @HasMany() pets: Pet[] get fullName() { return `${this.firstName} ${this.lastName}` } } @Model() class Pet extends ApplicationRecord { static jsonapiType = "pets" @Attr() name: string } let { data } = await Person .where({ name: 'Joe' }) .page(2).per(10) .sort('name') .includes("pets") .all() let names = data.map((p) => { return p.fullName }) console.log(names) // ['Joe Blow', 'Joe DiMaggio', ...] console.log(data[0].pets[0].name) // "Fido" ``` -------------------------------- ### Advanced Spraypaint Model Configuration (TypeScript) Source: https://context7.com/graphiti-api/spraypaint.js/llms.txt Illustrates advanced configuration options for Spraypaint models, including custom base URLs, API namespaces, endpoint overrides, key case conversion (snake to camel), strict attribute checking, JWT storage configuration, and custom credential storage backends. ```typescript @Model({ baseUrl: "http://api.example.com", apiNamespace: "/v2", jsonapiType: "users", endpoint: "/members", // Custom endpoint override keyCase: { server: "snake", client: "camel" }, // Key conversion strictAttributes: true, // Only allow defined attributes jwtStorage: "auth_token", // Custom JWT storage key credentialStorageBackend: new InMemoryStorageBackend() // Custom storage }) class User extends ApplicationRecord { @Attr() userName: string @Attr({ persist: false }) computedField: string @Attr({ dirtyChecker: (prior, current) => { return JSON.stringify(prior) !== JSON.stringify(current) } }) complexData: object @HasMany({ type: "articles", persist: false }) articles: Article[] @Link() selfLink: string @Link() profileUrl: string } ``` -------------------------------- ### Resource Identifiers and Metadata in Spraypaint.js Source: https://context7.com/graphiti-api/spraypaint.js/llms.txt This snippet illustrates how to create a model instance, save it, and retrieve its resource identifier. It also shows how to access metadata and links from API responses, and how to set custom metadata on an instance. This is crucial for managing resource state and interacting with API-provided information. ```typescript import { ApplicationRecord, Model } from "@graphiti/jsonapi"; @Model() class Person extends ApplicationRecord { static jsonapiType = "people" // ... other configurations } const person = new Person({ firstName: "John" }) // await person.save() // Assuming save method exists and works // Get resource identifier const identifier = person.resourceIdentifier console.log(identifier) // { id: "1", type: "people" } (example output) // Access metadata from API response // const { data: person2, meta } = await Person.find(1) // console.log(meta) // { stats: { ... }, pagination: { ... } } (example output) // Set metadata on instance person.setMeta({ customField: "value" }) console.log(person.meta) // { customField: "value" } // Access links // console.log(person.links) // { self: "...", related: "..." } (example output) ``` -------------------------------- ### Create New Records with Relationships Source: https://context7.com/graphiti-api/spraypaint.js/llms.txt Shows how to create and save new records using spraypaint.js. This includes instantiating a new model, assigning attributes, saving the record, and checking the save status and errors. It also demonstrates creating associated records and linking them before saving the parent record, ensuring relationships are persisted. ```typescript // Create and save a new record const person = new Person({ firstName: "Jane", lastName: "Doe", age: 28 }) const success = await person.save() if (success) { console.log(`Created with ID: ${person.id}`) console.log(`Persisted: ${person.isPersisted}`) // true } else { console.log("Save failed") console.log(person.errors) } // Create with relationships const pet = new Pet({ name: "Fido", breed: "Golden Retriever" }) const person2 = new Person({ firstName: "John", lastName: "Smith" }) person2.pets = [pet] await person2.save({ with: { pets: {} } }) console.log(person2.pets[0].id) // Pet was created and has ID ``` -------------------------------- ### Enable Client-Side Store and Sync Mode in Spraypaint Source: https://context7.com/graphiti-api/spraypaint.js/llms.txt Covers enabling real-time data synchronization and managing the client-side identity map in Spraypaint. It shows how to set models to sync, listen for changes on individual records, access and manipulate the central store, and stop listening for updates. ```typescript // Enable sync mode for real-time updates @Model({ baseUrl: "http://api.example.com" }) class SyncedModel extends ApplicationRecord { static sync = true } // Create an instance and listen for changes const { data: person } = await Person.find(1) person.listen() // When the store updates, the instance updates automatically // (useful for WebSocket integrations or optimistic updates) // Stop listening person.unlisten() // Access the identity map const store = Person.store console.log(store.count) // Number of cached instances // Find stored attributes const stored = store.find(person) console.log(stored) // Original attributes when loaded // Clear store entry store.destroy(person) ``` -------------------------------- ### Configure Key Case Conversion in Spraypaint Models Source: https://context7.com/graphiti-api/spraypaint.js/llms.txt Explains how to configure Spraypaint models to automatically convert keys between client-side conventions (e.g., camelCase) and server-side conventions (e.g., snake_case). It demonstrates setting the `keyCase` option in the `@Model` decorator and manual key conversion methods. ```typescript @Model({ keyCase: { server: "snake", // API uses snake_case client: "camel" // Client uses camelCase } }) class Person extends ApplicationRecord { @Attr() firstName: string // Sent as "first_name" @Attr() lastName: string // Sent as "last_name" @Attr() phoneNumber: string // Sent as "phone_number" } // Manual key conversion const serverKey = Person.serializeKey("firstName") // "first_name" const clientKey = Person.deserializeKey("first_name") // "firstName" ``` -------------------------------- ### Build Advanced Queries with Filtering, Pagination, and Sorting Source: https://context7.com/graphiti-api/spraypaint.js/llms.txt Demonstrates building complex queries using spraypaint.js. This includes filtering by multiple criteria, paginating results, specifying the number of items per page, ordering results by single or multiple fields, selecting specific fields, including related data, calculating statistics, and applying custom API filters and fetch options. It also shows how to reuse query scopes. ```typescript // Complex query with multiple parameters const { data: filtered } = await Person .where({ firstName: "John", age: { gte: 18, lte: 65 } }) .page(2) .per(25) .order({ lastName: "asc", age: "desc" }) .select(["firstName", "lastName", "age"]) .includes("pets") .stats({ total: "count", average_age: "average" }) .all() console.log(filtered.length) // Up to 25 records console.log(filtered[0].pets) // Loaded relationship console.log(filtered[0].fullName) // "John Doe" // Sparse fieldsets with multiple types const { data: users } = await Person .select({ people: ["firstName", "lastName"], pets: ["name"] }) .includes("pets") .all() // Nested includes const { data: author } = await Author .includes({ books: ["publisher", "reviews"] }) .find(1) console.log(author.books[0].publisher.name) console.log(author.books[0].reviews) // Extra parameters for custom API filters const { data: active } = await Person .extraParams({ active: true, search: "john" }) .all() // Custom fetch options (headers, credentials, etc.) const { data: secured } = await Person .extraFetchOptions({ headers: { "X-Custom-Header": "value" }, credentials: "include" }) .all() // Reusable scopes const activeScope = Person .where({ active: true }) .order("lastName") const { data: activePage1 } = await activeScope.page(1).per(10).all() const { data: activePage2 } = await activeScope.page(2).per(10).all() ``` -------------------------------- ### Define Spraypaint Models with Attributes and Relationships (TypeScript) Source: https://context7.com/graphiti-api/spraypaint.js/llms.txt Demonstrates defining base application records with API configuration and concrete models like Person, Pet, and Bio using Spraypaint decorators. It shows how to declare attributes, specify data types, and define relationships like HasMany, HasOne, and BelongsTo. ```typescript import { SpraypaintBase, Model, Attr, HasMany, HasOne, BelongsTo } from "spraypaint" // Define base application record with API configuration @Model({ baseUrl: "http://localhost:3000", apiNamespace: "/api/v1" }) class ApplicationRecord extends SpraypaintBase {} // Define a Person model with attributes @Model() class Person extends ApplicationRecord { static jsonapiType = "people" @Attr() firstName: string @Attr() lastName: string @Attr({ type: Number }) age: number @HasMany() pets: Pet[] @HasOne() bio: Bio get fullName() { return `${this.firstName} ${this.lastName}` } } @Model() class Pet extends ApplicationRecord { static jsonapiType = "pets" @Attr() name: string @Attr() breed: string @BelongsTo() person: Person } @Model() class Bio extends ApplicationRecord { static jsonapiType = "bios" @Attr() description: string @Attr() yearsActive: number } ``` -------------------------------- ### Configure Logging and Debugging in Spraypaint Source: https://context7.com/graphiti-api/spraypaint.js/llms.txt Illustrates how to configure logging levels and implement custom logger instances for Spraypaint models. It shows setting the global logger level and providing a custom logger class that overrides default logging behavior for debugging, info, warnings, and errors. ```typescript import { Logger, LogLevel } from "spraypaint" // Configure global logger Person.logger.level = "debug" // Custom logger implementation class CustomLogger { level = "info" debug(message: any) { console.log("[DEBUG]", message) } info(message: any) { console.log("[INFO]", message) } warn(message: any) { console.warn("[WARN]", message) } error(message: any) { console.error("[ERROR]", message) } } @Model({ logger: new CustomLogger() }) class LoggedModel extends ApplicationRecord {} ``` -------------------------------- ### Type Registry and Polymorphism in Spraypaint.js Source: https://context7.com/graphiti-api/spraypaint.js/llms.txt This section explains how to register models within Spraypaint's type registry, access the registry, and check class relationships. It also covers polymorphic queries where the API might return mixed types, ensuring that instances are correctly instantiated based on their JSON:API type. ```typescript import { ApplicationRecord, Model } from "@graphiti/jsonapi"; // Assuming Person, Author, Book are defined elsewhere and extend ApplicationRecord // class Person extends ApplicationRecord { static jsonapiType = "people" } // class Author extends ApplicationRecord { static jsonapiType = "authors" } // class Book extends ApplicationRecord { static jsonapiType = "books" } // Register models in type registry Person.registerType() Author.registerType() Book.registerType() // Access type registry const registry = Person.typeRegistry const PersonClass = registry.get("people") // Check type relationships console.log(Person.isSubclassOf(ApplicationRecord)) // true // Polymorphic queries (API returns mixed types) // const { data: people } = await Person.all() // If API returns type "authors", instance will be Author class // console.log(people[0] instanceof Author) // true // console.log(people[0].isType("authors")) // true ``` -------------------------------- ### Custom Endpoints and URLs in Spraypaint.js Source: https://context7.com/graphiti-api/spraypaint.js/llms.txt This snippet demonstrates how to override the default endpoint for a model and how to generate full URLs for resources, including those with specific IDs. It also shows how to retrieve the base API path. This functionality is useful for defining custom API routes and accessing resources programmatically. ```typescript import { ApplicationRecord, Model } from "@graphiti/jsonapi"; @Model() class Person extends ApplicationRecord { static jsonapiType = "people" static endpoint = "/users" // Override endpoint } // Get full URL for a resource const url = Person.url() // "http://localhost:3000/api/v1/users" const urlWithId = Person.url(1) // "http://localhost:3000/api/v1/users/1" // Get base path const basePath = Person.fullBasePath() // "http://localhost:3000/api/v1" ``` -------------------------------- ### Delete Records and Mark for Destruction Source: https://context7.com/graphiti-api/spraypaint.js/llms.txt Explains how to delete records using spraypaint.js. This includes fetching a record and then calling the `destroy()` method. It also covers how to mark related records for destruction within a nested save operation, ensuring that associated data is cleaned up. ```typescript // Delete a record const { data: person } = await Person.find(1) const success = await person.destroy() console.log(`Deleted: ${success}`) // Mark for destruction in nested saves const { data: author } = await Author.includes("books").find(1) author.books[0].isMarkedForDestruction = true await author.save({ with: { books: {} } }) // Book will be deleted ``` -------------------------------- ### Check and Manage Record Changes with Dirty Tracking Source: https://context7.com/graphiti-api/spraypaint.js/llms.txt Covers dirty tracking functionality in spraypaint.js for managing record state. It shows how to check if a record has been modified using `isDirty()`, view the specific changes made with `changes()`, revert modifications using `rollback()`, and reset the record's state to clean without losing current edits using `reset()`. It also includes how to duplicate an existing record instance. ```typescript const { data: person } = await Person.find(1) console.log(person.isDirty()) // false person.firstName = "Updated" console.log(person.isDirty()) // true // Get detailed changes const changes = person.changes() console.log(changes) // { firstName: ["Original", "Updated"] } // Rollback changes person.rollback() console.log(person.firstName) // "Original" console.log(person.isDirty()) // false // Reset to clean state (keeps current values) person.firstName = "New Value" person.reset() console.log(person.isDirty()) // false console.log(person.firstName) // "New Value" // Duplicate an instance const copy = person.dup() console.log(copy.id) // undefined (new instance) console.log(copy.firstName) // "New Value" console.log(copy.isPersisted) // false ``` -------------------------------- ### Implement Request and Response Middleware in Spraypaint Source: https://context7.com/graphiti-api/spraypaint.js/llms.txt Details how to implement custom middleware for intercepting API requests and responses in Spraypaint. This includes defining functions to modify request headers or log response details, and applying them globally to all models using a MiddlewareStack. ```typescript import { MiddlewareStack } from "spraypaint" // Define before-request middleware const addTimestamp = (url: string, options: RequestInit) => { options.headers = { ...options.headers, "X-Request-Time": new Date().toISOString() } console.log(`Fetching: ${url}`) } // Define after-response middleware const logResponse = (response: Response, json: any) => { console.log(`Response status: ${response.status}`) if (response.status === 401) { console.error("Unauthorized - token may be expired") } } // Apply middleware to models const middleware = new MiddlewareStack([addTimestamp], [logResponse]) ApplicationRecord.middlewareStack = middleware // Now all requests will use these hooks const { data: people } = await Person.all() // Console: "Fetching: http://localhost:3000/api/v1/people" // Console: "Response status: 200" ``` -------------------------------- ### Track Relationship Changes with Dirty State Source: https://context7.com/graphiti-api/spraypaint.js/llms.txt Focuses on how spraypaint.js handles dirty tracking for relationships. It demonstrates adding a new related record, checking if the relationship is dirty using `isDirty("relationshipName")` and `hasDirtyRelation()`, and how these changes are persisted upon saving the parent record. ```typescript const { data: person } = await Person.includes("pets").find(1) // Add a new pet const newPet = new Pet({ name: "Buddy" }) person.pets.push(newPet) console.log(person.isDirty("pets")) // true console.log(person.hasDirtyRelation("pets", newPet)) // true // Save with relationship await person.save({ with: { pets: {} } }) console.log(person.isDirty()) // false ``` -------------------------------- ### Update Existing Records and Relationships Source: https://context7.com/graphiti-api/spraypaint.js/llms.txt Details how to update existing records fetched from the API using spraypaint.js. This involves fetching a record, modifying its attributes, checking for changes using `isDirty()`, and saving the modifications. It also covers updating nested relationships and specifying custom return scopes for the updated data. ```typescript // Fetch and update const { data: person } = await Person.find(1) person.firstName = "Janet" person.age = 29 if (person.isDirty()) { console.log(person.changes()) // { firstName: ["Jane", "Janet"], age: [28, 29] } const success = await person.save() console.log(`Updated: ${success}`) } // Update with nested relationships const { data: author } = await Author .includes("books") .find(1) author.books[0].title = "Updated Title" author.books.push(new Book({ title: "New Book" })) await author.save({ with: { books: {} } }) // Save with custom return scope const { data: updated } = await person.save({ returnScope: Person.includes(["pets", "bio"]) }) console.log(updated.pets) // Reloaded with relationships ``` -------------------------------- ### Handle Save Failures and Validation Errors in Spraypaint Source: https://context7.com/graphiti-api/spraypaint.js/llms.txt Demonstrates how to handle validation errors when saving records in Spraypaint. It shows checking for errors on specific attributes and base errors, clearing errors, and retrying the save operation after fixing invalid data. This is crucial for providing user feedback on input errors. ```typescript const person = new Person({ firstName: "", // Invalid: required field age: -5 // Invalid: must be positive }) const success = await person.save() console.log(success) // false // Check for errors on specific attributes if (person.errors.firstName) { console.log(person.errors.firstName.code) // "blank" console.log(person.errors.firstName.title) // "Validation Error" console.log(person.errors.firstName.message) // "cannot be blank" console.log(person.errors.firstName.fullMessage) // "First name cannot be blank" console.log(person.errors.firstName.attribute) // "firstName" } if (person.errors.age) { console.log(person.errors.age.message) // "must be positive" } // Check for base errors (not tied to specific attribute) if (person.errors.base) { console.log(person.errors.base.fullMessage) } // Clear errors person.clearErrors() console.log(person.errors) // {} // Fix and retry person.firstName = "Valid Name" person.age = 30 const retrySuccess = await person.save() console.log(retrySuccess) // true ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.