@elysians/resource
v1.0.0
Published
Resource utilities for Elysia.js
Downloads
5
Maintainers
Readme
Elysion Resource
A lightweight, type-safe resource transformation library for TypeScript, inspired by Laravel's API Resources. Easily transform your data structures into JSON:API-compatible responses with support for collections, pagination, and conditional attributes.
Features
- 🚀 Type-safe transformations with TypeScript
- 🔄 Collections & Pagination out of the box
- 🎭 Conditional attributes based on runtime conditions
- 🧩 Composable resources with
mergeandwhenmethods - 🛡️ Null-safe handling throughout
- 📊 Flexible metadata for collections and pagination
Installation
bun add @elysion/resource
# or
npm install @elysion/resource
# or
yarn add @elysion/resourceBasic Usage
Defining a Resource
import { defineResource } from '@elysion/resource';
interface User {
id: number;
username: string;
email: string;
isAdmin: boolean;
posts: Array<{ id: number; title: string }>;
}
const UserResource = defineResource<User, {
id: number;
name: string;
email: string;
is_admin: boolean;
post_count: number;
latest_post?: { id: number; title: string } | null;
}>({
id: (user) => user.id,
name: (user) => user.username,
email: (user) => user.email,
is_admin: (user) => user.isAdmin,
post_count: (user) => user.posts?.length ?? 0,
latest_post: (user) => user.posts[0] ?? null,
});Using the Resource
Transform a Single Resource
const user = {
id: 1,
username: 'johndoe',
email: '[email protected]',
isAdmin: true,
posts: [
{ id: 1, title: 'Hello World' },
{ id: 2, title: 'Getting Started' }
]
};
const transformed = UserResource(user);
/*
{
id: 1,
name: 'johndoe',
email: '[email protected]',
is_admin: true,
post_count: 2,
latest_post: { id: 1, title: 'Hello World' }
}
*/Transform a Collection
const users = [
{ id: 1, username: 'alice', email: '[email protected]', isAdmin: false, posts: [] },
{ id: 2, username: 'bob', email: '[email protected]', isAdmin: true, posts: [{ id: 1, title: 'Hi' }] },
null // null values are handled gracefully
];
const collection = UserResource.collection(users);
/*
{
data: [
{
id: 1,
name: 'alice',
email: '[email protected]',
is_admin: false,
post_count: 0,
latest_post: null
},
{
id: 2,
name: 'bob',
email: '[email protected]',
is_admin: true,
post_count: 1,
latest_post: { id: 1, title: 'Hi' }
},
null
],
meta: {
count: 3,
valid_count: 2
}
}
*/Paginated Responses
const items = Array(15).fill(0).map((_, i) => ({
id: i + 1,
username: `user${i + 1}`,
email: `user${i + 1}@example.com`,
isAdmin: false,
posts: []
}));
const paginated = UserResource.paginate(
items,
45, // total items
2, // current page
15, // per page
undefined, // context (optional)
{ custom_meta: 'value' } // additional metadata (optional)
);
/*
{
data: [
// ... 15 user objects
],
meta: {
current_page: 2,
from: 16,
last_page: 3,
per_page: 15,
to: 30,
total: 45,
valid_count: 15,
custom_meta: 'value'
}
}
*/Advanced Usage
Conditional Attributes
const AdminResource = defineResource<User, {
id: number;
email: string;
is_admin: boolean;
admin_only?: string;
}>({
id: (user) => user.id,
email: (user) => user.email,
is_admin: (user) => user.isAdmin,
admin_only: (user) => 'Sensitive admin data'
});
// Only include admin_only field for admin users
const SecureUserResource = AdminResource
.when(
(user) => user.isAdmin,
{ admin_only: (user) => 'Sensitive admin data' }
);
// For non-admin users, admin_only will be undefined
const regularUser = { id: 1, username: 'user', email: '[email protected]', isAdmin: false, posts: [] };
SecureUserResource(regularUser); // { id: 1, email: '[email protected]', is_admin: false }Merging Resources
const BaseResource = defineResource<User, { id: number; type: string }>({
id: (user) => user.id,
type: () => 'user'
});
const ExtendedResource = BaseResource.merge({
name: (user) => user.username,
email: (user) => user.email
});
ExtendedResource({ id: 1, username: 'test', email: '[email protected]', isAdmin: false, posts: [] });
/*
{
id: 1,
type: 'user',
name: 'test',
email: '[email protected]'
}
*/Context
Pass additional context to your transformers:
const UserWithContextResource = defineResource<
User,
{ id: number; name: string; is_current_user: boolean }
>({
id: (user) => user.id,
name: (user) => user.username,
is_current_user: (user, context) => user.id === context?.currentUserId
});
const user = { id: 1, username: 'test', email: '[email protected]', isAdmin: false, posts: [] };
UserWithContextResource(user, { currentUserId: 2 }); // { id: 1, name: 'test', is_current_user: false }API Reference
defineResource<T, R>(schema: ResourceSchema<T, R>): ResourceTransformer<T, R>
Creates a new resource transformer with the given schema.
Parameters:
schema: An object where keys are output field names and values are either:- A static value
- A function that takes the model and optional context and returns the value
Returns:
A function that can transform a model of type T into an object of type R.
The returned function has additional methods:
.collection(models, context?, meta?): Transform an array of models.paginate(items, total, currentPage, perPage, context?, meta?): Create a paginated response.when(condition, attributes): Conditionally include attributes.merge(attributes): Merge additional attributes into the schema
License
MIT
bun run devOpen http://localhost:3000/ with your browser to see the result.
