{"id":28315056,"url":"https://github.com/priolo/julian","last_synced_at":"2026-03-04T17:31:28.466Z","repository":{"id":295168057,"uuid":"903324979","full_name":"priolo/julian","owner":"priolo","description":"Framework NodeJs Server TypeScript","archived":false,"fork":false,"pushed_at":"2025-12-20T09:48:49.000Z","size":974,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-22T15:27:36.020Z","etag":null,"topics":["framework","nodejs","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/priolo.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-12-14T10:06:31.000Z","updated_at":"2025-12-20T09:48:52.000Z","dependencies_parsed_at":"2025-05-24T02:19:15.407Z","dependency_job_id":"67700dbe-24f5-4d69-a19f-909b801c1218","html_url":"https://github.com/priolo/julian","commit_stats":null,"previous_names":["priolo/julian"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/priolo/julian","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/priolo%2Fjulian","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/priolo%2Fjulian/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/priolo%2Fjulian/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/priolo%2Fjulian/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/priolo","download_url":"https://codeload.github.com/priolo/julian/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/priolo%2Fjulian/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30087324,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-04T15:40:14.053Z","status":"ssl_error","status_checked_at":"2026-03-04T15:40:13.655Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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","typescript"],"created_at":"2025-05-24T20:10:12.078Z","updated_at":"2026-03-04T17:31:28.446Z","avatar_url":"https://github.com/priolo.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TYPEXPRESS\n\n![Bob](./img/logo.png)\n\n## DESCRIPTION\n\nInizialmente dovevo creare un semplice microservizio in Express\nDurante lo sviluppo mi sono reso conto che per ogni opzione aggiunta (JWT, DB, Render engine...)\ndovevo studiare diversi approcci.\nAlla fine ho sviluppato un livello che nasconde tutta la complessità\n\n\n## INSTALLATION\n\nistalla \"typexpress\" nel progetto  \n`npm i typexpress`\n\n\n## QUICK START FAQ STYLE\n\n### Voglio un semplice SERVER HTTP  \n*[Bob]:* ...diciamo con un route `/myroute`?\n\n[sandbox](https://codesandbox.io/s/http-router-1-z203w?file=/src/index.js)\n\n```js\nconst {RootService} = require(\"typexpress\")\n\nRootService.Start({\n\tclass: \"http\",\n\tport: 8080,\n\tchildren: [\n\t\t{\n\t\t\tclass: \"http-router\",\n\t\t\tpath: \"/myroute\",\n\t\t\trouters: [{\n\t\t\t\tverb: \"get\",\n\t\t\t\tmethod: (req, res, next) =\u003e {\n\t\t\t\t\tres.json({response: \"hello world\"})\n\t\t\t\t}\n\t\t\t}]\n\t\t},\n\t]\n})\n```\n\u003e **ATTENZIONE!** la funziona lambda va bene se non si usa il `this`!   \n\u003e Se devi usare il `this` usa `function`:  \n```js\nmethod: function (req, res, next) {\n\t...\n}\n```\n\nhttps://z203w.sse.codesandbox.io/myroute\n\n---\n\n### No aspetta! Voglio un SERVER HTTP STATICO  \nche punta alla cartella: `/public_static`  \ne con la rotta: `/pub`  \n\n[sandbox](https://codesandbox.io/s/http-static-1-sj9bz?file=/src/index.js)\n\n```js\nconst {RootService} = require(\"typexpress\")\nconst path = require(\"path\")\n\nRootService.Start([\n\t{\n\t\tclass: \"http\",\n\t\tport: 8080,\n\t\tchildren: [\n\t\t\t{\n\t\t\t\tclass: \"http-static\",\n\t\t\t\t// local directory in file-system\n\t\t\t\tdir: path.join(__dirname, \"../public_static\"),\n\t\t\t\t// path of routing\n\t\t\t\tpath: \"/pub\"\n\t\t\t}\n\t\t]\n\t}\n])\n```\n\nhttps://sj9bz.sse.codesandbox.io/\n\n---\n\n### ... e con visualizzazione della DIRECTORY nella rotta `/index`\n\n[sandbox](https://codesandbox.io/s/http-static-index-682xm?file=/src/index.js)\n\nhttps://682xm.sse.codesandbox.io/index\n\n---\n\n### Ma mettiamo che VOGLIO MANDARE INFORMAZIONI\ncon una form html nella cartella static\n\n[sandbox](https://codesandbox.io/s/http-form-1-cc08y?file=/src/index.js)\n\n```js\nconst {RootService} = require(\"typexpress\")\nconst path = require(\"path\")\n\nRootService.Start([\n\t{\n\t\tclass: \"http\",\n\t\tport: 8080,\n\t\tchildren: [\n\t\t\t{\n\t\t\t\tclass: \"http-static\",\n\t\t\t\tdir: path.join(__dirname, \"../public\"),\n\t\t\t\tpath: \"/\"\n\t\t\t},\n\t\t\t{\n\t\t\t\tclass: \"http-router\",\n\t\t\t\tpath: \"/greet\",\n\t\t\t\trouters: [{\n\t\t\t\t\tverb: \"post\",\n\t\t\t\t\tmethod: async (req, res, next) =\u003e {\n\t\t\t\t\t\tres.send(`\u003cp\u003eHallo ${req.body.name}!\u003c/p\u003e`)\n\t\t\t\t\t}\n\t\t\t\t}]\n\t\t\t},\n\t\t\t\n\t\t]\n\t}\n])\n```\n\nhttps://cc08y.sse.codesandbox.io/\n\n---\n\nBELLO... ma, andiamo, non posso creare pagine HTML cosi !!!   \n### Mi serve un... TEMPLATE ENGINE\n*[Bob]:* Ok ok automaticamente c'e' il supporto a [handlebars](https://github.com/express-handlebars/express-handlebars)  \n*(supporto da migliorare ed estendere)*\n\n[sandbox](https://codesandbox.io/s/http-form-handlebars-z2o31?file=/src/index.js:130-153)\n\n---\n\n### Aspetta! Aspetta! Ma io di solito faccio app in REACT con CRA!\n*[Bob]:* allora puoi creare un entrypoint per SPA\n\n[sandbox](https://codesandbox.io/s/http-static-spa-tbq4l)\n\n```js\nconst {RootService} = require(\"typexpress\")\nconst path = require(\"path\")\n\nRootService.Start({\n\tclass: \"http\",\n\tport: 8080,\n\tchildren: [\n\t\t{\n\t\t\tclass: \"http-static\",\n\t\t\tdir: path.join(__dirname, \"../build\"),\n\t\t\tpath: \"/\",\t\t// ATTENZIONE: definire un path necessita una \"base dir\" nel client!\n\t\t\tspaFile: \"index.html\",\n\t\t}\n\t]\n})\n```\n\nhttps://tbq4l.sse.codesandbox.io\n\n---\n\n### Si vabbe' pero' i DATI poi dove li metto? Il DB dov'è???\n*[Bob]:* Mbeh usi [Typeorm](https://typeorm.io/#/) e li metti... che ne so... facciamo sqlite!?  \nGuarda, prendi sto ToDo e divertiti\n\n[sandbox](https://codesandbox.io/s/typeorm-yith3?file=/src/index.js)  \nhttps://yith3.sse.codesandbox.io/\n\n```js\nRootService.Start([\n\t\n\t// Server HTTP\n\t{ ... },\n\t\t\n\t// SERVICE del DB\n\t{\n\t\t// istanzia un SQLITE\n\t\tclass: \"typeorm\",\n\t\toptions: {\n\t\t\ttype: \"sqlite\",\n\t\t\tdatabase: path.join(__dirname, \"../db/database.sqlite\"),\n\t\t\tsynchronize: true\n\t\t},\n\t\t// i REPOSITORY del DB\n\t\tchildren: [\n\t\t\t{\n\t\t\t\tname: \"todo\", class: \"typeorm/repo\",\n\t\t\t\tmodel: {\n\t\t\t\t\tname: \"Todo\",\n\t\t\t\t\t// https://typeorm.io/#/separating-entity-definition\n\t\t\t\t\tcolumns: {\n\t\t\t\t\t\tid: {type: Number, primary: true, generated: true},\n\t\t\t\t\t\ttitle: {type: String, default: \"\"},\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t]\n\t}\n])\n```\n\u003e Puoi anche usare `http-route/rest`  \n\u003e ti permette di collegare un elemento REST al DB in un colpo solo\n\n```js\nroot = await RootService.Start([\n\t// SERVER HTTP\n\t{\n\t\tclass: \"http\",\n\t\tport: PORT,\n\t\tchildren: [\n\t\t\t// REST HTTP on USER REPOSITORY\n\t\t\t{\n\t\t\t\tname: \"user\",\n\t\t\t\tpath: \"/user\",\n\t\t\t\tclass: \"http-router/repo\",\n\t\t\t\trepository: \"/typeorm/user\",\n\t\t\t}\n\t\t]\n\t},\n\t// DB\n\t{\n\t\tclass: \"typeorm\",\n\t\toptions: {\n\t\t\t\"type\": \"sqlite\",\n\t\t\t\"database\": dbPath,\n\t\t\t\"synchronize\": true,\n\t\t\t\"entities\": [User],\n\t\t},\n\t\tchildren: [\n\t\t\t{ name: \"user\", class: \"typeorm/repo\", model: \"User\" },\n\t\t]\n\t}\n])\n```\n \n\u003e in NodeJS ci sono due librerie principali per l'ORM\n\u003e - [Sequelize](https://sequelize.org/)\n\u003e - [Typeorm](https://typeorm.io/)  \n\u003e \n\u003e Per il momento c'e' solo il supporto a Typeorm (il nome \"Typexpress\" viene da li)\n\n---\n\n### Mi hai annoiato con sti elenchi puntati! Dimmi riguardo le SESSION.\n*[Bob]:* :unamused: c'e' il servizio specifico\n\n[sandbox](https://codesandbox.io/s/session-i10vc?file=/src/index.js)  \nhttps://i10vc.sse.codesandbox.io/sessioned/counter\n\n```js\nRootService.Start([\n\t{\n\t\tclass: \"http\", port: \"8080\",\n\t\tchildren: [\n\t\t\t// SESSION MIDDLEWARE \n\t\t\t{\n\t\t\t\tname: \"typeorm-session\",\n\t\t\t\tclass: \"http-router/session\",\n\t\t\t\ttypeorm: \"/typeorm\",\n\t\t\t\tpath: \"/sessioned\",\n\t\t\t\tchildren: [\n\t\t\t\t\t// ROUTER\n\t\t\t\t\t{\n\t\t\t\t\t\tclass: \"http-router\",\n\t\t\t\t\t\trouters: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tpath: \"/counter\", \n\t\t\t\t\t\t\t\tmethod: (req, res, next) =\u003e {\n\t\t\t\t\t\t\t\t\tif ( req.session.counter==null ) req.session.counter = 0 \n\t\t\t\t\t\t\t\t\telse req.session.counter++\n\t\t\t\t\t\t\t\t\tres.send(`\u003cp\u003eCounter: ${req.session.counter}\u003c/p\u003e`)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t]\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t},\n\t\t]\n\t},\n\t{\n\t\tclass: \"typeorm\",\n\t\toptions: {\n\t\t\t\"type\": \"sqlite\",\n\t\t\t\"database\": path.join(__dirname, \"../db/database.sqlite\"),\n\t\t\t\"synchronize\": true,\n\t\t\t\"entities\": [SessionEntity],\n\t\t},\n\t}\n])\n```\n\n\u003e Quindi tutti i figli di `http-router/session`  \n\u003e avranno la stessa sessione (memorizzata sul DB)  \n\u003e In futuro le session faranno riferimento a specifici REPOSITORY  \n\u003e in maniera da avere diverse session  \n\n---\n\n### Ma chi vuoi fregare!? Intendo JWT SESSION!!!\n*[Bob]:* :triumph: Quanta pazienza!  \nPuoi creare un SERVICE `jwt`   \ncon questo code/decode tramite parola segreta.  \nQuindi usare un middleware specializzato  \nper caricare i dati dell'utente `htt-router/jwt`\n\n[sandbox](https://codesandbox.io/s/session-jwt-i3fgb?file=/src/index.js)  \n\n```js\nRootService.Start([\n\t// HTTP server\n\t{\n\t\tclass: \"http\", port: 8080,\n\t\tchildren: [\n\t\t\t{\n\t\t\t\tclass: \"http-router\",\n\t\t\t\trouters: [\n\t\t\t\t\t// HOME PAGE\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: function (req, res, next) {\n\t\t\t\t\t\t\tres.send(`\u003ca href=\"/login\"\u003elogin\u003c/a\u003e\u003cbr/\u003e\n\t\t\t\t\t\t\t\u003ca href=\"/logout\"\u003elogout\u003c/a\u003e\u003cbr/\u003e\n\t\t\t\t\t\t\t\u003ca href=\"/protect\"\u003eenter protect area\u003c/a\u003e`)\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t// LOGIN\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: \"/login\", method: async function (req, res, next) {\n\t\t\t\t\t\t\tconst token = await new Bus(this, \"/http/route-jwt\").dispatch({\n\t\t\t\t\t\t\t\ttype: RouteJWTUserActions.TOKEN_BY_ID,\n\t\t\t\t\t\t\t\tpayload: 10,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tres.cookie('token', token)\n\t\t\t\t\t\t\tres.send(`\u003cp\u003eLogged in with token: ${token}\u003c/p\u003e`)\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t// LOGOUT\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: \"/logout\", method: async function (req, res, next) {\n\t\t\t\t\t\t\tres.cookie('token', \"\")\n\t\t\t\t\t\t\tres.send(`\u003cp\u003eLogout\u003c/p\u003e`)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t},\n\t\t\t// JWT MIDDLEWARE\n\t\t\t{\n\t\t\t\tclass: \"http-router/jwt\",\n\t\t\t\trepository: \"/typeorm/user\",\n\t\t\t\tjwt: \"/jwt\",\n\t\t\t\tchildren: [\n\t\t\t\t\t{\n\t\t\t\t\t\tclass: \"http-router\",\n\t\t\t\t\t\tpath: \"/protect\",\n\t\t\t\t\t\trouters: [\n\t\t\t\t\t\t\t{ method: (req, res, next) =\u003e res.send(`\u003cp\u003eHi ${req.user.username}\u003c/p\u003e`) },\n\t\t\t\t\t\t]\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t},\n\t\t]\n\t},\n\t// DB\n\t{\n\t\tclass: \"typeorm\",\n\t\toptions: {\n\t\t\t\"type\": \"sqlite\",\n\t\t\t\"database\": `${__dirname}/database.sqlite`,\n\t\t\t\"synchronize\": true,\n\t\t},\n\t\tschemas: [{\n\t\t\tname: \"User\",\n\t\t\tcolumns: {\n\t\t\t\tid: { type: Number, primary: true },\n\t\t\t\tusername: { type: String }\n\t\t\t}\n\t\t}],\n\t\tchildren: [\n\t\t\t{ name: \"user\", class: \"typeorm/repo\", model: \"User\" }\n\t\t]\n\t},\n\t// code/decode JWT\n\t{\n\t\tclass: \"jwt\",\n\t\tsecret: \"secret_word!!!\"\n\t},\n\n])\n```\n\n---\n\n\n\n\n\n\n\n## Ciclo vita events\nChiamata PRIMA della creazione dei PROPRI CHILDREN\n[sostituire con]: onCreate\nprotected async onInit(): Promise\u003cvoid\u003e { }\n\nChiamata DOPO la creazione dei PROPRI CHILDREN\n[sostituire con]: onCreateAfter\nprotected async onInitAfter(): Promise\u003cvoid\u003e { }\n\n[da fare]: chiamato per prima di inizializzare il nodo (e i children)\nprotected async onInitBefore(): Promise\u003cvoid\u003e { }\n\n[da fare]: chiamato per inizializzare il nodo (e i children)\nprotected async onInit(): Promise\u003cvoid\u003e { }\n\nChiamato dopo il comando STOP e prima della rimozione del nodo dall'albero\nprotected async onDestroy(): Promise\u003cvoid\u003e { }\n\n\n## Filosofia\n\nTOP (tree oriented programming)\n\n- Come in OOP ogni oggetto ha una singola responsabilità\nMa non esistono piu' \"service\" o oggetti aggregati\n- Ogni oggetto può essere incapsulato in un parent o di avere un aggregato di children. Quindi l'oggetto diventa un NODE\n- La comunicazione tra NODI è standardizzata (dispatch)\n- L'assenza o il fallimento di un children puo' essere gestita in maniera generica\n- Si puo' accedere a tutte le risorsa dello stesso albero conoscendo la \"path\" relativa o assoluta\n- Lo stato dell'intero è dovuto alla somma degli stati di ogni NODE\n\n## Features\n\n### Tree structure\nI nodi sono strutturati ad albero per cui è sempre possibile recuperare un NODE tramite il suo \"path\"\n\n### Config\nOgni nodo ha un suo \"config\" che sarebbero delle props del nodo \n\n### State\nogni node ha uno stato interno inizialmente il config\n(?history)\n\n### Dispatch\ni nodi hanno un set di ACTIONs che possono essere chiamate tramite la loro path\nquesti messaggi vengono recapitati dal Bus\n\n### Events\nun nodo puo' rimanere in ascolto su un altro nodo sugli eventi che genera quest'ultimo\n\n\n## roadmap\n\n### gestione errori\ngli errori devono essere mandati tutti ad un nodo centralizzato e loggati correttamente\nIMPORTANTE: gli errori dei router devono essere gestiti e bisogna restituire un errore 500! ora invece si blocca\n\n### tipizzare\nfunzioni come dispatch e getNode devono essere parametrizzabili con i generic per restituire il tipo giusto\n\n### auto-npm install\nse un service ha necessità di un  pacchetto npm deve essere possibile istallarlo in automatico\n// installare npm\nhttps://stackoverflow.com/a/57914191/5224029\n\n### Portals\npermettono di creare servizi che possono far comunicare nodi in diverse posizioni sulla rete\nper creare facilmente microservizi\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpriolo%2Fjulian","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpriolo%2Fjulian","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpriolo%2Fjulian/lists"}