{"id":18633905,"url":"https://github.com/rheas-io/framework","last_synced_at":"2025-04-11T07:32:44.248Z","repository":{"id":42702516,"uuid":"262553718","full_name":"rheas-io/framework","owner":"rheas-io","description":"A simple and elegant NodeJS framework for web application servers in Typescript.","archived":false,"fork":false,"pushed_at":"2023-03-05T13:33:40.000Z","size":2221,"stargazers_count":7,"open_issues_count":5,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-09-26T17:09:15.560Z","etag":null,"topics":["framework","nodejs","router"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rheas-io.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-05-09T11:14:23.000Z","updated_at":"2024-06-19T07:34:09.000Z","dependencies_parsed_at":"2023-02-17T02:46:02.270Z","dependency_job_id":null,"html_url":"https://github.com/rheas-io/framework","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rheas-io%2Fframework","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rheas-io%2Fframework/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rheas-io%2Fframework/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rheas-io%2Fframework/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rheas-io","download_url":"https://codeload.github.com/rheas-io/framework/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223460566,"owners_count":17148760,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["framework","nodejs","router"],"created_at":"2024-11-07T05:16:42.905Z","updated_at":"2024-11-07T05:16:43.542Z","avatar_url":"https://github.com/rheas-io.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align='left'\u003e\n\u003ca href='https://www.kaysy.io/' target='_blank'\u003e\n\u003cimg src=\"https://raw.githubusercontent.com/rheas-io/framework/master/images/rheas-full.svg\" width=\"250px\"\u003e\n\u003c/a\u003e\n\u003c/p\u003e\n\u003cp align='left'\u003e\n\u003ca href=\"https://www.npmjs.com/package/@rheas/framework\"\u003e\u003cimg src=\"https://img.shields.io/npm/dm/@rheas/framework\" alt=\"Downloads\"\u003e\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/@rheas/framework\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/@rheas/framework\" alt=\"Version\"\u003e\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/@rheas/framework\"\u003e\u003cimg src=\"https://img.shields.io/npm/l/@rheas/framework\" alt=\"License\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cbr\u003e\n\n## About Rheas\n\nRheas is a NodeJs framework created for developing web applications faster using an elegant syntax. This project is heavily inspired from the [Laravel](https://www.laravel.com/) framework.\n\n## Why Rheas?\n\nRheas was created to solve one problem that we faced when working with other NodeJs frameworks - the need to install a handful of dependencies to show a single, functional web page.\n\n- To handle cookies, we had to find and install one cookie middleware package.\n- To handle sessions, we had to find and install another package.\n- To handle authentication, we had to find and install another package.\n\nThe list goes on for many other core features that are essential for serving a website.\n\nRheas solves this for you. Rheas has built in functionality for almost all the features for running a web application like session handling, cookie handling, authentication, cache etc.\n\n## Getting started\n\nCreating a web server using Rheas framework is easy and it does not install any global packages. All the modules including the framework's CLI is scoped to project.\n\n### Project initialization\n\nCreate a blank directory for your new project and initialize your npm project using \n\n```\nnpm init\n```\n\n### Installation\n\n**To install the framework using npm**\n\n```\nnpm install @rheas/framework\n```\n\nThis package installs all the core modules needed to create a fully functional web server application. After installation, the framework will copy a `rheas` file on the current working directory, which acts as an entry point of the command line interface of the framework.\n\n### Create new project\n\nRheas framework comes with a command line interface which can be used to do various operations like creating a new project, creating application keys, creating models, controllers etc.\n\n**To create a new project, run**\n\n```\nnode rheas new \u003cproject-name\u003e\n```\n\nThe above command will create a new project by creating all the core files and directories required for running the server and updates the `package.json` file with some `scripts` and `devDependencies`.\n\n**Note:** The `new` project command won't replace any files if it already exists on the current working directory. So, it is safe to run the command, if you have accidentally deleted some core files in the project directory and want it back.\n\n### Running the server\n\nThe `new` project command will update the `scripts` in the `package.json` file with commands required to build the project and start a server.\n\nRheas uses Typescript and thus the application has to be transpiled first before we can run it.\n\n**Build the project**\n\n```\nnpm run tsc\n```\n\n**Run the server**\n\n```\nnpm run start\n```\n\nIf you are using the default configurations, you will see the following message on your terminal.\n\n`Listening on port 3000`\n\n## Routes and controllers\n\nWhen a new project is created using the CLI, a controllers directory `app/controllers` and a routes directory `routes` containing `api.ts` and `web.ts` files are created on the project root.\n\nThe `web.ts` file looks like\n\n```\nimport { IRoute } from '@rheas/contracts/routes';\n\nconst webRoutes: IRoute[] = [\n    // All the web routes\n];\n\nexport default webRoutes;\n```\n\nAs you can see, the default export is a collection of routes. Add new routes to this collection and it will be registered on the router when the application is loaded.\n\n**Register a GET request to the homepage**\n```\nimport { Route } from '@rheas/routing';\nimport { IRoute } from '@rheas/contracts/routes';\n\nconst webRoutes: IRoute[] = [\n    Route.get('/', 'HomeController@index')\n];\n\nexport default webRoutes;\n```\n\nIn the above example `HomeController@index` is the controller action registered to the route, where `HomeController` depicts a file `HomeController.ts` in the `app/controllers` directory and `index` is the exported function.\n\nWhen a request to the homepage `/` reaches our server, the router will execute the function named `index` on the `app/controllers/HomeController.ts` file.\n\n**`/app/controlers/HomeController.ts`**\n\n```\nimport { IRequest, IResponse } from '@rheas/contracts';\n\nexport async function index(request: IRequest, response: IResponse) {\n  return response.set('Hello world!');\n}\n```\n\nThe above controller sends a **Hello World!** string back to the client as a response.\n\n\u003e A controller and middleware functions should always return the response object. To facilitate this, the content setting methods like `response.set()`, `response.json()` and `response.view()` returns the response object.\n\n**Controller action on the route**\n\nFor something as simple as sending a response as shown in the above example, creating a file for just one controller function is an overkill. So, we support direct function registration on the Route itself.\n\n```\nimport { Route } from '@rheas/routing';\nimport { IRoute } from '@rheas/contracts/routes';\n\nconst webRoutes: IRoute[] = [\n    Route.get('/', async (req, res) =\u003e res.set('Hello World!))\n];\n\nexport default webRoutes;\n```\n\nThe above route will work in the same way as the example shown earlier.\n\n## Architecture\n\nThe architecture of this framework is heavily inspired from Laravel, Symfony and ExpressJs. The API is designed in such a way that the learning curve is low and the developers with experience working on any of the above said framework's can transition smoothly.\n\n### Lifecycle\n\nUnlike PHP Laravel, lifecycle of a Rheas web application can be split into two - application lifecycle and request lifecycle.\n\nProcesses like connecting to the database, listening to each requests, managing queues, managing logs or in short the services that are independent of requests are all happening within the application lifecycle. This lifecycle begins when the Rheas application is statrted and ends when the application is terminated or when the process exits.\n\nWhen it comes to request lifecycle, it manages the cookie handling, session handling, redirect services etc. From a frameworks point of view, we can say that the lifecycle of a Request is from the moment a request reaches our server and ends when a response is sent back.\n\n\u003e The actual request lifecycle begins when a new Request object aka `IncomingMessage` object is created by the node `http` or `https` server and is terminated only when it is garbage collected by NodeJs. So be wary of the references you keep on the Request object.\n\nAll the services/bindings registered on the application instance will persist until the application is terminated. And all the services/bindings registered on a request instance will persists on it until it (the request object) is garbage collected.\n\n### Controller and Middleware design\n\nEven though the framework uses ES6 classes throughout the framework, we have kept the exposed API to use Typescript pure functions only.\n\nMost of the time, developers just have to work with controllers, middlewares and models only. Controllers and middlewares are pure functions, so that developers coming from an ExpressJs background would feel comfortable.\n\n##### Controller function definition\n\n```\nimport { StringObject } from '@rheas/contracts; \nimport { IRequest, IResponse } from '@rheas/contracts';\n\nexport async function index(req: IRequest, res: IResponse, params: StringObject) {\n  \n  // Controler operations\n\n  return res;\n}\n```\n\nA controller function accepts three arguments - `request`, `response` and an optional `params` field. The third argument contains all the route parameter keys mapped to their corresponding value in the request.\n\nMost of the time CRUD operations are written on a single file and thus Rheas resolves controller function using names. This is not the case with middlewares.\n\n##### Middleware function definition\n\n```\nimport { IRequest, IResponse } from '@rheas/contracts';\nimport { IRequestHandler } from '@rheas/contracts/routes; \n\nasync function handle(req: IRequest, res: IResponse, next: IRequestHandler, ...params: any) {\n  \n  // Middleware operations\n\n  return await next(req,res);\n}\n\nexport default handle;\n```\n\nA middleware function accepts the `request` and `response` objects as the first two arguments. The third argument `next` is the next middleware in the pipeline. All the middlewares should **wait** for the next middlewares to finish processing. \n\nIf we don't call the next middleware or if we don't wait for the next middleware promise to resolve, the middleware would return immediately breaking the whole middleware pipeline and sending invalid response to the client.\n\n\u003e Rheas resolves only the **default** export in a middleware file. So each middleware should be in a seperate file.\n\n### Containers\n\nDependency injection is an essential design pattern to follow when we have to work with large number of seperate modules and services. So Rheas also supports dependency injection. That said, Rheas **does not** implement a full fledged dependency injector, yet. Why?\n\nBecause, our exposed API of controllers and middlewares are pure functions and they have a strict definition which accepts the `request` container as a parameter.\n\nAs mentioned in the Lifecycle section, Rheas framework has two lifecycles - an application lifecycle and a request lifecycle. To facilitate this, we have designed our `Application` instance as a container and each Request object is also a container.\n\nWhen a request lifecycle begins, Rheas automatically registers the application container as a binding on the request container. This gives the request container access to all the services/bindings registered on the application container also.\n\nSo a contoller and middleware function can gather the dependencies from the `request` paramater.\n\n```\nimport { IEncrypter } from '@rheas/security';\nimport { IRequest, IResponse } from '@rheas/contracts';\n\nexport async function index(req: IRequest, res: IResponse) {\n  \n  // Gets the encrypter service.\n  const encrypter: IEncrypter = req.get('encrypt');\n\n  const encryptedValue = await encrypter.encrypt('Hello world!');\n\n  return res.set(encryptedValue);\n}\n```\n\nIn the above example, we needed the encryption service registered on the application container to return an encrypted value as response.\n\n`req.get('encrypt');` will try to find any binding on the request container for the alias `encrypt` and if it is not available on the request container, it will search for one on the application container. If no binding is found, an exception will be thrown.\n\n\u003e For testing, you can inject a binding on the request container for the required aliases.\n\n### Services\nServices are the core part or in fact the central unit of Rheas framework. We can add new features and functions using services and the one we don't need can be removed as well. Thus, services provides expandability to the framework.\n\nThe framework comes with many core services like router, sessions handler, encrypter, hasher, cookies handler and several others to quickly create and run a web application server.\n\nYou can also create a new service and add it to the application.\n\nA service can be registered on the application lifecycle or request lifecycle or on both. This is done by updating the config files.\n\n\u003e When you register a service on both the application and request lifecycle, they don't share the same service instance. A new service instance is created for each requests.\n\n**`/configs/app.ts`**\n\n```\nproviders: {\n        // Core services required by the framework.\n        ...\n        ...\n        ...\n        \n        // Register your service providers in here.\n        \n        newService : NewServiceProvider\n}\n```\n\nSimilarly there is a config file for request as well, which contains all the service provider mapped to their alias.\n\n\u003e Only the services available in these config files will be registered on the lifecycles. And, since the service provider list is a Javascript object, no two services can exist with same alias.\n\nEven though we cache the service provider list when the app/request lifecycle begins, we don't initialize them until the application asks for it ie, services are lazy loaded.\n\nSay you have a service `FetchUsers` mapped to the alias `fetch.users` and have it registered on the providers list of `/configs/request.ts`. An instance of the `FetchUsers` service will not be created until you call `request.get('fetch.users')` somewhere in your application. Once called, the instance will be cached and will be re-used throughout the request lifecycle.\n\n#### Service Providers\n\nService providers are responsible for creation of services. Every service provider receives two parameters when they are initialized - service alias and the container in which the service has to be registered. Rheas injects these parameters automatically.\n\n**`Rheas default Cookie service provider`**\n\n```\nimport { IRequest } from '@rheas/contracts';\nimport { CookiesManager } from './cookiesManager';\nimport { ServiceProvider } from '@rheas/services';\nimport { InstanceHandler } from '@rheas/contracts/container';\n\nexport class CookieServiceProvider extends ServiceProvider {\n    /**\n     * Returns the cookies service resolver. The service returns\n     * a cookies manager which parses incoming cookies, adds new\n     * one's to the queue etc. Cookies service is registered on the\n     * request cycle.\n     *\n     * @returns\n     */\n    public serviceResolver(): InstanceHandler {\n        return (request) =\u003e new CookiesManager(request as IRequest);\n    }\n}\n```\n\nRheas frameworks' in-built cookie service provider is shown above. As you can see the base class `ServiceProvider` takes away all the abstraction and only the service resolver is exposed on this class.\n\n**What this service provider do?**\n\nSay, you register this on the request lifecycle with the alias `cookie`. When you executes `request.get('cookie')` for the first time in the current lifecycle, the framework will create a singleton binding on the request container for the `CookieManager` object and maps it to the key `cookie`. The parameter of the `serviceResolver` is the the container in which the service has to be registered, in this case `request`.\n\nThe underlying `register` function of the base `ServiceProvider` class is \n\n```\npublic register(): void {\n    this.container.singleton(this.name, this.serviceResolver());\n}\n```\n\nIf you don't want to use a singleton, override the `register` function on your service provider class.\n\n## Roadmap\n\n- Extensive testing of all the modules before releasing to production.\n- Add commands to the CLI that creates controllers, models, services, middlewares etc.\n- Create a detailed documentation on the framework.\n- Add datastore backed queue handler. In this version, jobs are kept in memory until dispatched.\n- Add datastore backed sessions. In this version files are used to store sessions.\n- Add the authentication module to the framework before release.\n- Move CPU intensive operations like encryption and decryption to worker threads.\n- Add error view files before release.\n- Add logging module before release.\n\n## Sponsor\n\n[Kaysy LLC](https://www.kaysy.io/) sponsors the development of this framework. Kaysy is an internet company working on SaaS products and Open-Source softwares.\n\n## Community\n\n**Author -** [Kalesh Kaladharan](https://twitter.com/kalesh_13/)  \n**Twitter -** [Kaysy LLC](https://twitter.com/kaysyio/)\n\n## License\n\nThe Rheas framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frheas-io%2Fframework","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frheas-io%2Fframework","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frheas-io%2Fframework/lists"}