@triplit/client
Version:
179 lines • 6.72 kB
JavaScript
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