Gathering detailed insights and metrics for skeleto
Gathering detailed insights and metrics for skeleto
Gathering detailed insights and metrics for skeleto
Gathering detailed insights and metrics for skeleto
npm install skeleto
Typescript
Module System
Node Version
NPM Version
TypeScript (100%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
7 Stars
53 Commits
1 Watchers
1 Branches
1 Contributors
Updated on Jun 16, 2025
Latest Version
0.0.1044
Package Id
skeleto@0.0.1044
Unpacked Size
117.99 kB
Size
31.54 kB
File Count
42
NPM Version
10.9.0
Node Version
20.12.2
Published on
Jan 20, 2025
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
1
7
Skeleto is a lightweight, function-based backend framework for TypeScript applications. It leverages dependency injection, aspect-oriented programming, and the decorator pattern to organize and structure your codebase efficiently.
Skeleto aims to provide a flexible and modular approach to building backend applications, focusing on functions rather than classes. It uses JSDoc comments as decorators and employs the concept of closures as the main construction method for functions.
@Action
, @Config
, and @Wrapper
@Wrapper
functionsYou can install Skeleto using npm:
1npm i skeleto
This quick start guide will walk you through creating a simple "Hello World" application using Skeleto.
First, we'll create our main application logic in src/app/helloworld.ts
:
This file defines two actions: a secondary action for finding a city by name, and a primary action for creating a greeting.
1import { ActionHandler } from "skeleto"; 2 3export type FindCityByName = ActionHandler<{ name: string }, { city: string } | null>; 4 5/** 6 * This is the simple database demonstration 7 * @Action 8 */ 9export function implFindCityByName(): FindCityByName { 10 return async (ctx, req) => { 11 if (req.name === "ade") return { city: "Jakarta" }; 12 if (req.name === "asep") return { city: "Bandung" }; 13 if (req.name === "anto") return { city: "Yogyakarta" }; 14 return null; 15 }; 16} 17 18export type HelloWorld = ActionHandler<{ name: string }, { message: string }>; 19 20/** 21 * This is the core logic application 22 * @Action 23 */ 24export function implHelloWorld(findCity: FindCityByName): HelloWorld { 25 return async (ctx, req) => { 26 // try to find user from database 27 const result = await findCity(ctx, req); 28 29 // if not found then just print hello 30 if (!result) return { message: `Hello ${req.name}` }; 31 32 // but if found, print hello followed by mentioning the city name 33 return { message: `Hello ${req.name}, you are from ${result.city}` }; 34 }; 35}
Next, we'll create our application entry point in src/index.ts
:
This file initializes Skeleto, retrieves our HelloWorld action, and runs it with different inputs.
1import { ActionHandler, newContext, Skeleto } from "skeleto"; 2 3async function main() { 4 const application = await Skeleto.start("./src/app"); 5 const heloworld = application.getContainer().get("HelloWorld")?.getInstance() as ActionHandler; 6 7 const response1 = await heloworld(newContext(), { name: "asep" }); 8 console.log(response1.message); 9 10 const response2 = await heloworld(newContext(), { name: "john" }); 11 console.log(response2.message); 12} 13 14main();
Run the application to see the output
1$ ts-node src/index.ts 2 3Hello asep, you are from Bandung 4Hello john
The example demonstrates:
@Action
): Both implFindCityByName
and implHelloWorld
are decorated with @Action
, showing how to define functions in Skeleto.implFindCityByName
acts as a secondary action, simulating a database lookup or external API call. It's a reusable component that can be injected into other actions.implHelloWorld
serves as the main business logic, demonstrating how to compose actions by using the findCity
function.ActionHandler<Request, Response>
type ensures type safety for inputs and outputs of each action.FindCityByName
action into implHelloWorld
, showcasing its dependency resolution capabilities.This basic example focuses on core concepts. More advanced features like Configuration Management (@Config
) and Cross-Cutting Concerns (@Wrapper
) are covered in the full documentation.
Skeleto is built around three main concepts: @Action
, @Config
, and @Wrapper
. These concepts work together to create a flexible, modular, and easily maintainable application structure. Let's explore each of these in detail:
@Action
The @Action
decorator is used to create main logic functions. The outer closure function of an @Action
decorated function can have parameters. These parameters can be injected by @Config
functions and Other @Action
functions. Cyclic dependencies between @Action
functions are not allowed and will result in an error.
1/** @Action */ 2export function implUserRepository(dbConfig: DatabaseConfig): UserRepository { 3 return async (ctx, req) => // Implementation using dbConfig 4} 5 6/** @Action */ 7export function implUserService(userRepo: UserRepository): UserService { 8 return async (ctx, req) => // Implementation using userRepo 9}
@Config
The @Config
decorator is used to create global configuration objects that can be used by all other functions, including those decorated with @Action
and @Wrapper
. The outer closure function of a @Config
decorated function can have parameters. These parameters can be injected by other @Config
functions. Cyclic dependencies between @Config
functions are not allowed and will result in an error. For example it can be used to create the Database
config object
1/** @Config */ 2export function implDatabaseConfig(): DatabaseConfig { 3 return { url: "postgres://localhost/mydb" }; 4} 5 6/** @Config */ 7export function implAppConfig(dbConfig: DatabaseConfig): AppConfig { 8 return { name: "MyApp", databaseUrl: dbConfig.url }; 9}
@Wrapper
The @Wrapper
decorator functions as middleware. It's used to wrap functions decorated with @Action
to add capabilities such as logging, transactions, error handling, and more. The outer closure function of a @Wrapper
decorated function can have parameters. These parameters can be injected by @Config
and @Action
functions. @Wrapper
functions cannot be injected into other functions. This is the example to demonstrate the logging:
1export type Logging = WrapperHandler; 2 3/** @Wrapper */ 4export function implLogging(logConfig: LogConfig, metrics: MetricsService): Logging { 5 return (actionHandler, metadata) => { 6 return async (ctx, req) => { 7 // Implementation using logConfig and metrics 8 }; 9 }; 10}
When using Skeleto, consider the following best practices:
Clear Separation: When writing a function with the @Action decorator, create a clear separation between PayloadRequest, PayloadResponse, function type definition, and function type implementation.
Payload Request
1type HelloRequest = { name: string };
Payload Response
1type HelloResponse = { message: string };
Function Type Definition
1type Hello = ActionHandler<HelloRequest, HelloResponse>;
Function Type Implementation
1/** @Action */ 2export function implHello(): Hello { 3 return async (ctx, req) => { 4 return { 5 message: `Hello ${req.name}`, 6 }; 7 }; 8}
Primary and Secondary Functions: Conceptually separate your functions into primary and secondary functions:
Naming Convention: Use a prefix impl
at the beginning of the function name, followed by the return type:
1type Hello = ActionHandler<HelloRequest, HelloResponse>; 2 3/** @Action */ 4export function implHello(): Hello { 5 return async (ctx, req) => ({ message: `Hello ${req.name}` }); 6}
Understanding the internal workings of Skeleto can help you leverage its full power and debug your applications more effectively. Here's a step-by-step breakdown of how Skeleto operates:
Project Analysis:
The framework performs a comprehensive scan of the TypeScript project at initialization, identifying all functions decorated with @Action
, @Config
, and @Wrapper
. It extracts rich metadata including dependencies, type information, and custom decorators.
Dependency Resolution: Using a sophisticated dependency resolver, the framework sorts all identified functions to prevent circular dependencies and determine the correct initialization order.
Function Instantiation:
The framework systematically instantiates each function, starting with @Config
, then @Wrapper
, and finally @Action
functions. During this process, it injects the required dependencies based on the function's parameter types.
Type Argument Analysis:
For @Action
functions marked with readTypeArguments: true
, the framework performs deep analysis of the request and response types, extracting detailed structure and decorator information.
Wrapper Application:
After instantiation, the framework applies any relevant @Wrapper
functions to the @Action functions, enhancing their behavior as specified.
Container Population: All instantiated functions, along with their rich metadata, are stored in a central container. This container serves as the core of the dependency injection system, allowing for runtime access and manipulation of the application's components.
While the Quick Start guide provides a simple example, Skeleto is capable of handling more complex scenarios. Here's an advanced example that demonstrates integration with a database, custom error handling, and logging:
This example showcases:
@Config
@Controller
decorator@Wrapper
Skeleto is designed to work with:
It's recommended to use the latest LTS versions of Node.js and TypeScript for the best experience.
Contributions are welcome! Please feel free to submit a Pull Request.
No vulnerabilities found.
No security vulnerabilities found.