UNPKG

@triplit/client

Version:
179 lines 6.72 kB
import { createUpdateProxyAndTrackChanges, deserializeEntity, deserializeFetchResult, EntityNotFoundError, queryBuilder, serializeEntity, TriplitError, Type, } from '@triplit/db'; function parseError(error) { try { const jsonError = JSON.parse(error); return TriplitError.fromJson(jsonError); } catch (e) { return new TriplitError(`Failed to parse remote error response: ${error}`); } } // Interact with remote via http api, totally separate from your local database export class HttpClient { options; constructor(options = {}) { this.options = options; } _schemaInitialized = false; // Hack: use schemaFactory to get schema if it's not ready from provider async schema() { if (!this._schemaInitialized) { if (!this.options.schema) { this.options.schema = await (this.options.schemaFactory ? this.options.schemaFactory() : undefined); } this._schemaInitialized = true; } return this.options.schema; } updateOptions(options) { this.options = { ...this.options, ...options }; } async sendRequest(uri, method, body, options = { isFile: false }) { const serverUrl = this.options.serverUrl; if (!serverUrl) throw new TriplitError('No server url provided'); if (!this.options.token) throw new TriplitError('No token provided'); const headers = { Authorization: 'Bearer ' + this.options.token, 'Content-Type': 'application/json', }; const stringifiedBody = JSON.stringify(body); let form; if (options.isFile) { form = new FormData(); form.append('data', stringifiedBody); delete headers['Content-Type']; } const res = await fetch(serverUrl + uri, { method, headers, body: options.isFile ? form : stringifiedBody, }); if (!res.ok) return { data: undefined, error: parseError(await res.text()) }; return { data: await res.json(), error: undefined }; } async fetch(query) { const { data, error } = await this.sendRequest('/fetch', 'POST', { query, }); if (error) throw error; return deserializeFetchResult(query, await this.schema(), data); } async fetchOne(query) { query = { ...query, limit: 1 }; const { data, error } = await this.sendRequest('/fetch', 'POST', { query, }); if (error) throw error; const deserialized = deserializeFetchResult(query, await this.schema(), data); const entity = deserialized[0]; if (!entity) return null; return entity; } async fetchById(collectionName, id) { const query = this.query(collectionName).Id(id); return this.fetchOne(query); } async insert(collectionName, object) { // we need to convert Sets to arrays before sending to the server const schema = await this.schema(); const collectionSchema = schema?.[collectionName].schema; // TODO: we should just be able to use the internal changeset here, which is // already JSON compliant const jsonEntity = collectionSchema ? Type.serialize(collectionSchema, object, 'decoded') : object; const { data, error } = await this.sendRequest('/insert', 'POST', { collectionName, entity: jsonEntity, }); if (error) throw error; return deserializeEntity(collectionSchema, data); } async bulkInsert(bulk) { const schema = await this.schema(); let payload = bulk; if (schema) { const schemaPayload = {}; for (const key in bulk) { const collectionName = key; const data = bulk[collectionName]; const collectionSchema = schema?.[collectionName].schema; if (!data) continue; schemaPayload[collectionName] = data.map((entity) => serializeEntity(collectionSchema, entity)); } payload = schemaPayload; } const { data, error } = await this.sendRequest('/bulk-insert-file', 'POST', payload, { isFile: true }); if (error) throw error; const result = {}; for (const key in data) { const collectionName = key; const collectionSchema = schema?.[collectionName].schema; result[collectionName] = data[key].map((entity) => deserializeEntity(collectionSchema, entity)); } return result; } async update(collectionName, id, update) { let changes = undefined; const schema = await this.schema(); const collectionSchema = schema?.[collectionName]?.schema; if (typeof update === 'function') { const existingEntity = await this.fetchById(collectionName, id); if (!existingEntity) { throw new EntityNotFoundError(id, collectionName); } changes = {}; // one of the key assumptions we're making here is that the update proxy // will take car of the conversion of Sets and Dates. This is mostly // to account for capturing changes to Sets because we need something // that can track deletes and sets to a Set, which a Set itself cannot do await update(createUpdateProxyAndTrackChanges(existingEntity, changes, collectionSchema)); } else { changes = update; } changes = collectionSchema ? Type.encode(collectionSchema, changes) : changes; const { data, error } = await this.sendRequest('/update', 'POST', { collectionName, entityId: id, changes, }); if (error) throw error; return data; } async delete(collectionName, entityId) { const { data, error } = await this.sendRequest('/delete', 'POST', { collectionName, entityId, }); if (error) throw error; return data; } async deleteAll(collectionName) { const { data, error } = await this.sendRequest('/delete-all', 'POST', { collectionName, }); if (error) throw error; return data; } query(collectionName) { return queryBuilder(collectionName); } } //# sourceMappingURL=http-client.js.map