Communicate with any REST API in an elegant and object oriented way.
const post = Post.$create({
title: 'What is Elodo?',
body: 'Elodo is an easy way to use resource models in front-end applications!',
});
// POST request to "http://api.com/api/posts"
await post.$store();
post.title = 'What is Elodo? Amazing!';
// PUT request to "http://api.com/api/posts/1"
await post.$update();
// GET request to "http://api.com/api/posts?include=comments,author"
const posts = await Post
.$request()
.$include('comments', 'author')
.$index();
// Each post is tranformed to an instance of Post
const post = posts[0];
await post.$update();
npm install elodo
npm install axios
/src
├── /api
| ├── /resources
| | ├── post.js
| | └── comment.js
| └── client.js
| └── resource.js
| └── router.js
├── ...
└── app.js
api/resource.js
import { createResource } from 'elodo';
import { client } from './client';
import { router } from './router';
export const Resource = createResource({
client,
router,
});
api/router.js
import { createRouter } from 'elodo';
export const router = createRouter();
router.prefix('http://api.com/api/v1/', function (router) {
// Register each crud action
router.index('posts', () => `posts`);
router.store('posts', () => `posts`);
router.show('posts', (post) => `posts/${post.id}`);
router.update('posts', (post) => `posts/${post.id}`);
router.destroy('posts', (post) => `posts/${post.id}`);
// Or register all crud actions at once
// $index GET /posts
// $store POST /posts
// $show GET /posts/{post.id}
// $store POST /posts/{post.id}
// $update PUT /posts/{post.id}
// $destroy DELETE /posts/{post.id}
router.resource('posts');
});
api/client.js
import Axios from 'axios';
export const client = function () {
return Axios.create();
};
api/resources/post.js
import { Resource } from '../resource';
export class Post extends Resource {
get _attributes () {
return {
id: null,
title: null,
body: null,
};
}
get _route () {
return 'posts'; // Used to create the route path
}
}
Now you can use the post resource in app.js
or any other file
import { Post } from './api/resources/post';
const posts = await Post.$index(); // GET request to "/posts"
const post = await Post.$find(1); // GET request to "/posts/1"
await post.$show(); // GET request to "/posts/1"
await post.$store(); // POST request to "/posts"
await post.$update(); // PUT request to "/posts/1"
await post.$destroy(); // DELETE request to "/posts/1"
Fetch filtered list of resources
Fetch list of resources sorted by an attribute
Fetch list of resources with relationships
Fetch list of resources with selected fields
Fetch list of resources with appended fields
Fetch list of resources with specific params
Fetch paginated list of resources
// GET /posts
const posts = await Post.$index();
Find a resource by primary id
// GET /posts/1
const post = await Post.$find(1);
Show a resource by its attributes
// GET /posts/1
const post = Post.$create({ id: 1 });
await post.$show();
You can also use the $refresh
alias
// GET /posts/1
const post = Post.$create({ id: 1 });
await post.$refresh();
// GET /posts?filter[title]=Hello
const posts = await Post
.$request()
.$filter('title', 'Hello')
.$index();
You can also use the where
alias
// GET /posts?filter[title]=Hello
const posts = await Post
.$request()
.$where('title', 'Hello')
.$index();
// GET /posts?sort=title
const posts = await Post
.$request()
.$sort('title')
.$index();
Sort descending
// GET /posts?sort=-title
const posts = await Post
.$request()
.$sortDesc('title')
.$index();
Combine multiple sorts
// GET /posts?sort=id,-name
const posts = await Post
.$request()
.$sort('id')
.$sortDesc('name')
.$index();
// GET /posts?include=comments,author
const posts = await Post
.$request()
.$include('comments', 'author')
.$index();
// GET /posts?fields[posts]=id,title
const posts = await Post
.$request()
.$fields({ 'posts': ['id', 'title'] })
.$index();
// GET /posts?append=published_at
const posts = await Post
.$request()
.$append('published_at')
.$index();
// GET /posts?limit=15
const posts = await Post
.$request()
.$limit(15)
.$index();
// GET /posts?param=value
const posts = await Post
.$request()
.$param('param', 'value')
.$index();
// GET /posts?page[size]=15&page[number]=1
const pagination = await Post
.$request()
.$page({
size: 15,
number: 1,
})
.$index();
// Pagination data is tranformed to instances of Post
const post = pagination.data[0];
await post.$update();
Set the page directly
// GET /posts?page=1
await Post
.$request()
.$page(1)
.$index();
Use with the limit param
// GET /posts?page=1&limit=15
await Post
.$request()
.$page(1)
.$limit(15)
.$index();
// POST /posts
const post = Post.$create({ title: 'Hello' });
await post.$store();
Or use the $save
alias
// POST /posts
const post = Post.$create({ title: 'Hello' });
await post.$save();
// Put /posts/1
const post = await Post.$find(1);
post.title = 'Updated title';
await post.$update();
// DELETE /posts/1
const post = await Post.$find(1);
await post.$destroy();
Or use the $delete
alias
// DELETE /posts/1
const post = await Post.$find(1);
await post.$delete();
Start with defining your relationship routes
import { createRouter } from 'elodo';
export const router = createRouter();
router.prefix('http://api.com/api/v1/', function (router) {
// $index GET /posts
// $store POST /posts
// $show GET /posts/{post.id}
// $store POST /posts/{post.id}
// $update PUT /posts/{post.id}
// $destroy DELETE /posts/{post.id}
router.resource('posts');
// $index GET /posts/{post.id}/comments
// $store POST /posts/{post.id}/comments
// $show GET /posts/{post.id}/comments/{comment.id}
// $store POST /posts/{post.id}/comments/{comment.id}
// $update PUT /posts/{post.id}/comments/{comment.id}
// $destroy DELETE /posts/{post.id}/comments/{comment.id}
router.resource('posts.comments');
});
const post = Post.$find(1);
const comment = Comment.$create({ body: 'Hello' });
// POST /posts/1/comments
await comment.$parent(post).$store();
// POST /comments
await comment.$store();
import { Post } from './resources/post';
import { createSource, isCancel } from 'elodo';
const source = createSource();
Post.$source(source)
.$index()
.then((posts) => {
...
})
.catch((error) => {
if (isCancel(error)) {
// Request was canceled
}
});
source.cancel();
Cancel any crud action
import { Post } from './resources/post';
import { createSource, isCancel } from 'elodo';
const source = createSource();
const post = Post.$create();
post.$source(source)
.$store()
.then((posts) => {
// Render posts
})
.catch((error) => {
if (isCancel(error)) {
// Request was canceled
}
});
source.cancel();
The cast property allows you to convert attributes coming from the server.
Build in cast types are: number
, float
, int
, bigint
, boolean
, string
, date
, json
, and json.parse
.
import { Resource } from '../resource';
export class Post extends Resource {
get _attributes () {
return {
id: null,
title: null,
published_at: null,
};
}
get _casts () {
return {
id: 'int',
published_at: 'date',
};
}
get _route () {
return 'posts';
}
}
import { Resource } from '../resource';
export class Post extends Resource {
get _attributes () {
return {
object: {
prop: null,
},
};
}
get _casts () {
return {
'object.prop': 'boolean',
};
}
get _route () {
return 'posts';
}
}
Add a function to that returns the transformed value.
import { Resource } from '../resource';
export class Post extends Resource {
get _attributes () {
return {
id: null,
title: null,
published_at: null,
};
}
get _casts () {
return {
published_at: (value) => new Date(value),
};
}
get _route () {
return 'posts';
}
}
import { Resource } from '../resource';
import { Comment } from './comment';
export class Post extends Resource {
get _attributes () {
return {
id: null,
title: null,
comments: null,
};
}
get _casts () {
return {
comments: (value) => Comment.$create(value),
};
}
get _route () {
return 'posts';
}
}
Change the content type of the resource to formdata
./api/resources/post.js
import { Resource } from '../resource';
export class Post extends Resource {
get _attributes () {
return {
id: null,
thumbnail: null,
};
}
get _contentType () {
return 'formdata';
}
get _route () {
return 'posts';
}
}
In ./app.js
or any other file
import { Post } from './api/resources/post';
const fileInput = document.querySelector('#fileInput');
const file = fileInput.files[0];
const post = Post.$create({ file });
// POST request to "/posts" with file in formdata
await post.$store();
To add or override options you can create a base resource
import { createResource } from 'elodo';
import { client } from './client';
import { router } from './router';
const BaseResource = createResource({
client,
router,
});
export class Resource extends BaseResource {
/**
* Default attributes
*/
get _attributes () {
return {
id: null,
};
}
/**
* Default route
*/
get _route () {
return 'default.route';
}
/**
* Default primary key
*/
get _primaryKey () {
return 'id';
}
/**
* Default casts
*/
get _casts () {
return {};
}
/**
* Default content type
*/
get _contentType () {
return 'json';
}
/**
* Custom $latest function
*/
static $latest () {
return this.$request()
.$limit(5)
.$sortDesc('created_at')
.$index()
;
}
}