Foscia
Type safe, modular and intuitive API client.
REST/JSON:API ready
REST or JSON:API backend are already covered and you may add your own implementations for any other data sources.
Modular
Foscia is composed of many independent functions, fully tree-shakable and lightweight.
Type safe
The API provides you a safe typing experience, even with edge cases such as dot relations used for eager loading.
Define your models
Foscia provides a simple and expressive way to define your models, which may combine attributes, relations, custom properties/methods and even composable definitions.
import { makeModel, attr, hasOne } from 'foscia/core';
import publishable from './composables/publishable';
import type User from './User';
import type Tag from './Tag';
export default class Post extends makeModel('posts', {
...publishable,
title: attr<string>(),
content: attr<string>(),
author: hasOne<User>(),
tags: hasMany<Tag>(),
}) {
get fullTitle() {
return `${this.title} by ${this.author.name} on ${this.publishedAt}`;
},
}
Discover actions' capabilities
The action()
factory provides an easy way to interact with any data source. Possibilities are endless and provided set of functions is wide.
Not a fan of functional programming? You can even plug functional capabilities onto your actions to get a class alike call style!
- Builder pattern style
- Functional style
import Post from './post';
import action from './action';
// The good old builder pattern way!
const post = await action()
.find(Post, 123)
.include('author', 'tags')
.oneOrFail();
fill(post, { title: 'Hello World!' });
const updatedPost = await action()
.update(post)
.oneOrCurrent();
import { find, update, include, oneOrFail, oneOrCurrent } from 'foscia/core';
import Post from './post';
import action from './action';
// The functional way!
const post = await action()
.use(find(Post, 123))
.use(include('author', 'tags'))
.run(oneOrFail());
fill(post, { title: 'Hello World!' });
const updatedPost = await action()
.use(update(post))
.run(oneOrCurrent());