
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
FluORM ("Flu{ent}-ORM") is a lightweight and flexible Object-Relational Mapping (ORM) library for TypeScript/JavaScript applications. It provides a simple and intuitive way to interact with your API endpoints while maintaining type safety and following object-oriented principles. FluORM has also a small caching mechinism.
npm install fluorm
Run tests
npm test
In JavaScript, property decorators are not natively supported yet (as of 2025), but they can be enabled using transpilers like Babel or TypeScript with experimental support.
Here’s how to enable and use them in TypeScript, which has the best support for decorators:
In your tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"experimentalDecorators": true,
"useDefineForClassFields": false
}
}
{
"plugins": [
["@babel/plugin-proposal-decorators", { "version": "legacy" }],
["@babel/plugin-proposal-class-properties", { "loose": true }]
]
}
After you need to define an API base URL and optional interceptors:
import { FluORM } from 'fluorm';
FluORM.configure({
baseUrl: 'https://api.example.com',
// headers, ...
options: RequestOptions,
// Optional interceptors
requestInterceptor: (request) => {
// Modify request before sending
return request;
},
responseInterceptor: (response) => {
// Modify response before returning
return response;
},
errorInterceptor: (error) => {
// Handle errors
console.error(error);
},
cacheOptions?: CacheOptions
});
interface RequestOptions {
body?: any,
method?: MethodType,
headers?: Record<string, string>
credentials?: RequestCredentials
mode?: RequestMode
redirect?: RequestRedirect
referrer?: string
referrerPolicy?: ReferrerPolicy
integrity?: string
cache?: RequestCache
keepalive?: boolean
signal?: AbortSignal
}
interface CacheOptions {
enabled: boolean;
ttl?: number; // Time to live in milliseconds
}
By default, FluORM uses the fetch() method.
But you can provide your own request handler like axios.
FluORM.configure({
baseUrl: 'https://api.example.com',
requestHandler: async (request: HttpRequest): HttpResponse => {
// Handle request and return a the response
const response = await fetch(request.url, request.options)
return await response.json() as HttpRequest
}
});
Models are the core of FluORM. Here's how to create a model:
import { Model, Attributes } from 'fluorm';
interface IUser extends Attributes {
name: string;
email: string;
created_at?: string;
updated_at?: string;
}
export class User extends Model<IUser> {
static resource = 'users'; // The API endpoint for this model
}
FluORM provides several decorators to define relationships and type casting:
import { HasOne, HasMany, BelongsTo, BelongsToMany } from 'fluorm';
class User extends Model<IUser> {
@HasOne(() => Profile)
profile!: Relation<Profile>;
@HasMany(() => Post)
posts!: Relation<Post[]>;
// use a custom resource named "libraries"
@HasMany(() => Media, 'libraries')
medias!: Relation<Media[]>;
@BelongsToMany(() => Role)
roles!: Relation<Role[]>;
}
import { Cast } from 'fluorm';
class User extends Model<IUser> {
@Cast(() => Date)
created_at?: Date;
@Cast(() => Thumbnail)
thumbnail?: Thumbnail;
@Cast(() => Thumbnail)
thumbnails?: Thumbnail[];
}
Scopes allow you to define reusable query constraints:
class User extends Model<IUser> {
static scopes = {
active: (query) => query.where({ status: 'active' })
};
}
Models come with several static methods for querying and manipulating data:
Model.all(): Get all recordsModel.find(id): Find a record by IDModel.create(data): Create a new recordModel.update(id, data): Update a recordModel.delete(id): Delete a recordmodel.get(): Fetch the instance using the id, if definedmodel.id(id): Return a new instance with id.model.update(data): Update the instance with datamodel.delete(): Delete the instancemodel.save(): Save the instancequery(): Start a new query builderwhere(conditions): Add where conditionsfilter(filters): Add filter conditionsinclude(relations): Add include query stringExample usage:
// Get all users
const users = await User.all();
// Find user by ID
const user = await User.find(1);
// Create new user
const newUser = await User.create({
name: 'John Doe',
email: 'john@example.com'
});
// Update user
await User.update(1, { name: 'Jane Doe' });
// Delete user
await User.delete(1);
// Query with conditions
const activeUsers = await User.where({ status: 'active' }).all();
// Include related models
const userWithPosts = await User.include('posts').find(1);
// Deep chaining
const thumbails = User.id(1).medias.id(2).thumnails.all();
// Will make a call to /users/1/medias/2/thumbails
Model instances have the following methods:
save(): Create or update the recordupdate(data): Update the recorddelete(): Delete the recordExample usage:
const user = new User({
name: 'John Doe',
email: 'john@example.com'
});
// Save new user
await user.save();
// Update user
user.name = 'Jane Doe';
await user.update({ email: "test@example.com" });
// Delete user
await user.delete();
You can use the relations declared in the model to create API calls.
const user = User.find(1)
// Will create an API call: GET /users/1/medias
user.medias.all()
// Will create an API call: GET /users/1/medias/2
user.medias.find(2)
// Will create an API call: GET /users/1/medias/2/thumbnails
user.medias.id(2).thumbnails.all()
const user = User.find(1) // Will make an API call to /users/1
const user = User.id(1) // return an instance of a new User with id equals 1. Then this instance can be used to query relations.
user.medias.all() // Will create an API call: GET /users/1/medias
The toObject() method converts a model instance and its related models into a plain JavaScript object:
const user = await User.find(1);
const userObject = user.toObject();
// Returns a plain object with all properties and nested related models
There is a built-in cache mechanism. This is disabled by default.
FluORM.configure({
cacheOptions: {
enabled: true,
ttl: 5 * 60 * 1000 // 5 minutes in milliseconds
}
})
Every request is put into a cache using the resource as a key :
const user = User.find(1)
// if you fetch the same user later, it will come from the cache.
Manually accessing the cache
// you can access the cache like this :
const cachedResponse = FluORM.getCache('users/1')
const user = new User(cachedResponse.data)
FluORM includes comprehensive error handling for common scenarios:
Example error handling:
try {
const user = await User.find(1);
} catch (error) {
if (error instanceof Error) {
console.error(`Failed to find user: ${error.message}`);
}
}
try {
await User.update(1, { name: 'John' });
} catch (error) {
if (error instanceof Error) {
console.error(`Failed to update user: ${error.message}`);
}
}
MIT
FAQs
FluORM is a fluent, model-oriented, typed REST client for TypeScript.
We found that fluorm demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.