{"id":15493456,"url":"https://github.com/sinclairzx81/appex","last_synced_at":"2025-04-22T19:49:51.180Z","repository":{"id":57182736,"uuid":"11097896","full_name":"sinclairzx81/appex","owner":"sinclairzx81","description":"develop nodejs web applications with typescript","archived":false,"fork":false,"pushed_at":"2013-11-24T00:00:25.000Z","size":2499,"stargazers_count":16,"open_issues_count":3,"forks_count":4,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-10-19T11:12:07.058Z","etag":null,"topics":[],"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/sinclairzx81.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"license.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-07-01T14:51:17.000Z","updated_at":"2024-03-16T18:19:00.000Z","dependencies_parsed_at":"2022-09-11T22:40:31.456Z","dependency_job_id":null,"html_url":"https://github.com/sinclairzx81/appex","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinclairzx81%2Fappex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinclairzx81%2Fappex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinclairzx81%2Fappex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinclairzx81%2Fappex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sinclairzx81","download_url":"https://codeload.github.com/sinclairzx81/appex/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250313333,"owners_count":21410201,"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":[],"created_at":"2024-10-02T08:06:49.277Z","updated_at":"2025-04-22T19:49:51.158Z","avatar_url":"https://github.com/sinclairzx81.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![](https://raw.github.com/sinclairzx81/appex/master/artifacts/logo.jpg)\n\n### develop nodejs web applications with [typescript](http://www.typescriptlang.org/)\n\n\n```typescript\n//----------------------------------------------\n// app.js\n//----------------------------------------------\n\nvar appex = require('appex');\n\nvar app = appex({ program : './program.ts' });\n\napp.listen(3000);\n\n//----------------------------------------------\n// program.ts\n//----------------------------------------------\n\n/// \u003creference path=\"node_modules/appex/appex.d.ts\" /\u003e\n\nexport function index(context:appex.web.IContext) {\n\t\n\tcontext.response.send('home page');\n}\n\nexport function about(context:appex.web.IContext) {\n\t\n\tcontext.response.send('about page');\n}\n\nexport function wildcard (context:appex.web.IContext, path:string) {\n    \n    context.response.send(404, path + \" not found\");\n}\n\n```\n### install\n\n```\nnpm install appex\n```\n### contents\n\n* [getting started](#getting_started)\n\t* [create a application](#application)\n\t* [start up options](#options)\n\t* [running on an existing http server](#http_server)\n\t* [running as express middleware](#express_middleware)\n* [http handlers](#http_handlers)\n\t* [context](#context)\n\t* [request](#request)\n\t* [response](#response)\n\t* [routing](#routing)\n\t* [signatures](#signatures)\n\t* [named handlers](#named_handlers)\n\t* [index handlers](#index_handlers)\n\t* [wildcard handlers](#wildcard_handlers)\n\t* [attributes](#attributes)\n\t* [verbs](#verbs)\n\t* [url rewrite](#url_rewrite)\n\t* [middleware](#middleware)\n\t* [exporting functions](#exporting_functions)\n\t* [handling 404](#handling_404)\n\t* [serving static files](#serving_static_files)\n* [templating](#templating)\n\t* [overview](#template_overview)\n\t* [context](#template_context)\t\n\t* [syntax](#template_syntax)\n\t* [layouts and sections](#template_layouts_and_sections)\n\t* [render](#template_render)\n\t* [caching and devmode](#caching_and_devmode)\n* [sitemaps](#sitemaps)\n\t* [generating](#sitemap_generate)\n\t* [metadata](#sitemap_metadata)\n* [json schema](#json_schema)\n\t* [generating schema](#generating_schema)\n\t* [validating json](#validating_json)\n\t* [web service descriptions](#web_service_descriptions)\n* [reflection](#reflection)\n\t* [reflect everything](#reflect_everything)\n\t* [reflect specific types](#reflect_specific_types)\n* [developing with appex](#developing_with_appex)\n\t* [appex.d.ts declaration](#appex_declaration)\n\t* [structuring projects](#structuring_projects)\n* [additional resources](#resources)\n* [license](#license)\n\n\u003ca name=\"getting_started\" /\u003e\n## getting started\n\nThe following sections outline creating appex applications and configuration.\n\n\u003ca name=\"application\" /\u003e\n### create a application\n\nThe following code will create a standalone appex application and \nhttp server and listen on port 3000.\n\n```typescript\nvar appex   = require('appex');\n\nvar app   = appex({ program : './program.ts', \n                    devmode : true,\n                    logging : true });\n\napp.listen(3000);\n```\n\nnote: devmode and logging are optional. however, when developing \nwith appex, it is helpful to have these enabled.\n\n\u003ca name=\"options\" /\u003e\n### start up options\n\nappex accepts the following start up options.\n\n```typescript\nvar options = {\n\n\t// (required) location of source file.\n\tprogram    : './program.ts', \n\n\t// (optional) recompile on request. (default:false) \n\tdevmode    : true,          \n\n\t// (optional) log to stdout. (default:false) \n\tlogging    : true,\n\n\t// (optional) user defined objects added to the app context.\n\tcontext    : {}\n};\n\nvar app = appex( options );\n```\n\n\u003ca name=\"http_server\" /\u003e\n### running on an existing http server\n\nThe following demonstrates setting up appex on an existing nodejs http server. In \nthis example, appex will attempt to handle incoming requests, and if appex cannot\nroute the request, will fire the callback.\n\n```typescript\nvar http  = require('http');\n\nvar appex = require('appex');\n\nvar app = appex({ program : './program.ts' });\n\nvar server = http.createServer(function(req, res){\n\n    app(req, res, function() { // appex handler...\n\t\t\n\t\t// not handled.\n\n\t}); \n});\n\nserver.listen(3000);\n```\n\n\u003ca name=\"express_middleware\" /\u003e\n### running as express middleware\n\nappex allows developers to augment existing express / connect applications by \nway of middleware. The following demonstrates setting up appex as express middleware.\n\n```typescript\nvar express = require('express');\n\nvar appex = require('appex');\n\nvar app = express();\n\napp.use( appex({ program : './program.ts' }) ); \n\napp.get('/', function(req, res) {\n\n  res.send('Hello World');\n  \n});\n\napp.listen(3000);\n```\n\nLike in the \"running on an existing http server\" example above, appex will attempt to intercept incoming requests. \nif appex cannot find a matching route for the request, it will automatically call the \"next\" function to pass the request \non to the next middleware or express handler.\n\nin addition to this, appex may also act as traditional express middleware. In the example below, a appex wildcard\nfunction is created which will match \"all\" incoming requests, the wildcard function simply prints hello world to \nthe console and then calls context.next(), which passes the request on the express handler.\n\n```typescript\n\n//----------------------------------------------\n// program.ts\n//----------------------------------------------\n\n// http:[host]:[port]/(.*)\nexport function wildcard(context, path) {\n\n\tconsole.log('hello world!!');\n\n\tcontext.next(); // pass it on!\n}\n\n//----------------------------------------------\n// app.js\n//----------------------------------------------\n\nvar express = require('express');\n\nvar appex = require('appex');\n\nvar app = express();\n\napp.use( appex({ program : './program.ts' }) );\n\napp.get('/', function(req, res) {\n\n  res.send('Hello World');\n  \n});\n\napp.listen(3000);\n```\n\nJust like traditional express middleware, appex will also inheriate the characteristics of the request.\n\nconsider the following example in which the jade view engine is configured for use. appex will inheritate the \nresponse.render() method, which is passed to the appex handler as context.response.render()\n\n```typescript\n//----------------------------------------------\n// app.js\n//----------------------------------------------\n\napp.configure(function(){\n\n  app.set('views', __dirname + '/views');\n  \n  // set up the jade engine.\n  app.set('view engine', 'jade'); \n  \n  // bind appex\n  app.use( appex({program:'./program.ts', devmode:true} ));  \n});\n\n//----------------------------------------------\n// program.ts\n//----------------------------------------------\n\n// http:[host]:[port]/\nexport function index(context) {\n\t\n\t// jade renderer works!\n\tcontext.response.render('index', { title: 'Express' }); \n}\n```\n\n\u003ca name=\"http_handlers\" /\u003e\n## http handlers\n\nThe following sections describe how to create http accessible handlers with appex.\n\n\u003ca name=\"context\" /\u003e\n### context\n\nAll appex functions are passed a application context object as their first argument. The app context object \nencapulates the http request and response objects issued by the underlying http server, as well as\nadditional objects specific to appex. These are listed below:\n\n```typescript\n// the app context\nexport function method(context) {\n\t\n\t// context.request    - the http request object.\n\n\t// context.response   - the http response object.\n\n\t// context.attribute    - appex attribute.\n\n\t// context.next       - the next function (express middleware)\n\t\n\t// context.router     - the appex router\n\n\t// context.sitemap    - the appex sitemap api\n\n\t// context.template   - the appex template engine.\n\t\n\t// context.module     - the module being run (this module)\n\n\t// context.schema     - json schema api.\n\n\t// context.mime       - a http mime type utility.\n}\n```\n\nit is possible to extend the default objects passed on the context by adding them on the appex startup options. The \nfollowing will attach the async module to the context. \n\n```typescript\n//----------------------------------------------\n// app.js\n//----------------------------------------------\n\nvar appex   = require('appex');\n\nvar app = appex({ program : './program.ts', \n\t\t\t\t  devmode : true, \n\t\t\t      context: {\n\t\t\t\t\t\tasync : require('async')\n\t\t\t\t  }});\n\napp.listen(3000);\n\n//----------------------------------------------\n// program.ts\n//----------------------------------------------\n\nexport function index(context) {\n\n\t// context.async = passed on the context.\n\n\tcontext.response.send('home page');\n}\n```\n\u003ca name=\"request\" /\u003e\n### request\n\nThe appex request is a nodejs http request issued by the underlying node http server. \nappex extends the request with convenience methods for reading http request data. These\nare outlined below.\n\nreading a posted string. \n```typescript\n//----------------------------------------------\n// receive request as a string\n//----------------------------------------------\nexport function submit(context) {\n\n\tcontext.request.body.recv((str) =\u003e {\n\n\t\t// do something with str\n\t})\n}\n```\nreading posted form data as json object.\n```typescript\n//----------------------------------------------\n// receive a form post\n//----------------------------------------------\nexport function submit(context) {\n\n\tcontext.request.body.form((obj) =\u003e {\n\n\t\t// do something with obj\n\t})\n}\n```\nreading posted json data as a json object.\n```typescript\n//----------------------------------------------\n// receive a json post\n//----------------------------------------------\nexport function submit(context) {\n\n\tcontext.request.body.json((obj) =\u003e {\n\t\t\n\t\t// do something with obj\n\t})\n}\n```\n\nnote: if appex detects that express or connect middleware has already been applied\nto the request object, appex will use those instead.\n\n\u003ca name=\"response\" /\u003e\n### response\n\nThe appex response is a nodejs http response issued by the underlying node http server. \nappex provides some utility methods for writing http responses. These are outlined below.\n\n```js\n//----------------------------------------------\n// the nodejs response has been extended with the following\n// signatures.\n//----------------------------------------------\nexport interface IResponse extends http.ServerResponse {\n\t\n\tsend (data     : string): void;\n\n\tsend (data     : NodeBuffer): void;\n\n\tsend (status   : number, data : string): void;\n\n\tserve (filepath: string): void;\n\n\tserve (root : string, filepath: string): void;\n\n\tserve (root : string, filepath: string, mime:string): void;\n\n\tjson (obj      : any): void;\n\n\tjson (status   : number, obj : any): void;\n\n\tjsonp (obj     : any): void;\n\n\tjsonp (status  : number, obj : any): void;\n\n\tjsonp (status  : number, obj : any, callback: string): void;\n}\n```\n\nnote: if appex detects that express or connect middleware has already been applied\nto for any of the following response methods, appex will use those instead.\n\n\u003ca name=\"routing\" /\u003e\n### routing\n\nappex creates routes based on module scope and function name. consider the following:\n\n```typescript\nexport module services.customers {\n\t\n\t// url: http://[host]:[port]/services/customers/insert\n\texport function insert(context) {\n\n\t\tcontext.response.send('services.customers.insert')\n    }\n\t\n\t// url: http://[host]:[port]/services/customers/update\n\texport function update(context) { \n\t\t\n\t\tcontext.response.send('services.customers.update')\n    }\n\t\n\t// url: http://[host]:[port]/services/customers/delete\n\texport function delete(context) { \n\n\t\tcontext.response.send('services.customers.delete')\n\t}\n}\n\n// url: http://[host]:[port]/\nexport function index   (context) { \n\n\tcontext.response.send('home page')\n}\n\n// url: http://[host]:[port]/about\nexport function about   (context) { \n\n\tcontext.response.send('about page')\n}\n\n// url: http://[host]:[port]/contact\nexport function contact (context) { \n\n\tcontext.response.send('contact page')\n}\n\n// url: http://[host]:[port]/(.*)\nexport function wildcard (context, path) {\n\n\tcontext.response.send(404, path + ' not found')\n}\n\n```\n\n\u003ca name=\"signatures\" /\u003e\n### signatures\n\nappex supports three function signatures for http routing (named, index and wildcard). Functions that\ndo not apply these signatures will not be routed.\n\n\u003ca name=\"named_handlers\" /\u003e\n### named handlers\n\nNamed handlers resolve urls to their current module scope + the name of the function.\n\nNamed handlers require the following signature:\n\n* name        - 'anything'\n* argument[0] - app context\n* returns     - void (optional)\n\n```typescript\n\n// http://[host]:[port]/about\nexport function about(context) {\n\n\tcontext.response.send('about page');\n}\n\n// http://[host]:[port]/users/login\nexport module users {\n\n\texport function login(context) {\n\t\t\n\t\tcontext.response.send('handle login');\t\n\t}\n}\n\n```\n\n\u003ca name=\"index_handlers\" /\u003e\n### index handlers\n\nIndex handlers resolve urls to their current module scope.\n\nIndex handlers require the following signature:\n\n* name        - 'index'\n* argument[0] - app context\n* returns     - void (optional)\n\n```typescript\n// url: http://[host]:[port]/\nexport function index(context) { \n\n\tcontext.response.send('home page');\n}\n\nexport module blogs {\n\t\n\t// url: http://[host]:[port]/blogs\n\texport function index  (context) \n\t{\t\n\t\tcontext.response.send('blog index');\n\t}\n}\n```\n\n\u003ca name=\"wildcard_handlers\" /\u003e\n### wildcard handlers\n\nWildcard handlers resolve their urls to their current module scope + url.\n\nappex wildcard handlers allow for wildcard routing at a given module scope. Wildcard handlers\nsupport 'typed' url argument mapping, as denoted by the arguments annotation.\n\nIn addition, wildcard handlers also support optional arguments which can be specified with TypeScript's '?' \non argument names.\n\nappex wildcard handlers require the following signature:\n\n* name        - 'wildcard'\n* argument[0] - app context\n* argument[n] - 1 or more arguments to be mapped from the url\n* returns     - void (optional)\n\n```typescript\ndeclare var console;\n\nexport module blogs {\n\t\n\t// url : http://[host]:[port]/blogs/2013/1/11   - matched\n\n\t// url : http://[host]:[port]/blogs/2013/01/11  - matched\n\n\t// url : http://[host]:[port]/blogs/2013/01/3rd - not matched - (see number annotation)\n\n\t// url : http://[host]:[port]/blogs/2013/01     - matched     - (see ? annotation)\n\n\t// url : http://[host]:[port]/blogs/2013        - not matched - (month is required)\n\t\n    export function wildcard(context, year:number, month:number, day?:number) {\n\n        context.response.json({ year: year, month: month, day: day})\n    }\n}\n\n// url : http://[host]:[port]/\nexport function index(context) {\n\n\tcontext.response.send('home');\n}\n\n// url : http://[host]:[port]/(.*) \nexport function wildcard(context, path) {\n\n\tcontext.response.send(404, 'not found');\n}\n\n```\nnote: appex supports boolean, number, string and any annotations on wildcard arguments. if no annotation\nis specified, appex interprets the argument as a string. the type 'any' is also interpreted as string.\n\nnote: wildcard functions should be declared last in any module scope. this ensures other routes\nwill be matched first.\n\n\u003ca name=\"attributes\" /\u003e\n### attributes\n\nappex supports a attribute scheme which developers can use to decorate modules and functions with \ndeclaritive metadata. appex attributes can set by calling the attribute('qualifier', data)\nfunction which is passed to the appex module on the global scope.\n\nunlike traditional attributes (in languages like C sharp) appex attributes have a cascading behaviour\nwhich allows developers to apply metadata at a lexical scope, and have it cascade through to descendant scopes.\n\nThe following outlines this behavour.\n\n```typescript\n/// \u003creference path=\"node_modules/appex/appex.d.ts\" /\u003e\n\nattribute({a: 10}); // global.\n\nattribute('foo', {b : 20})\nexport module foo {\n\n    attribute('foo.bar', {c : 30})\n    export module bar {\n            \n        attribute('foo.bar.index', {d : 40})\n        export function index(context) {\n        \n            //context.attribute\n            //{\n            //    \"a\": 10,\n            //    \"b\": 20,\n            //    \"c\": 30,\n\t\t\t//    \"d\": 40\n            //}\n\n            context.response.json( context.attribute );       \n        }\n    }\n}\n\n```\n\nin addition, appex recognizes three types of attributes. developers can use these to override the default \nbahavour of the appex router and apply url rewriting (urls), verb matching (verbs) and middleware (use),\nas demonstrated below.\n\n```typescript\n/// \u003creference path=\"node_modules/appex/appex.d.ts\" /\u003e\n\nfunction logger(context) {\n\tconsole.log('logging')\n\tcontext.next()\n}\n\n// invoke 'logger' middleware.\nattribute('index', {use   : [logger]})   \n\n// override the default route.\nattribute('index', {urls  : ['/', '/home']})  \n\n// only accept GET requests.\nattribute('index', {verbs : ['GET']})    \n\nexport function index(context:appex.web.IContext) {\n\t\n\tcontext.response.send('home page')\n}\n```\n\n\u003ca name=\"verbs\" /\u003e\n### verbs\n\nappex handles http verb matching with attributes. appex will recognise the \n'verbs' property applied to the attribute to match against http verbs.\n\n```typescript\nattribute('index', { verbs: ['GET'] })\nexport function index (context) { \n        \n    // only allow HTTP GET requests\n    context.response.send('index')\n}\n\nattribute('submit', { verbs: ['POST', 'PUT'] })\nexport function submit (context) { \n    \n    // only allow HTTP POST and PUT requests\n    context.response.send('submit')\n}\n```\n\n\u003ca name=\"url_rewrite\" /\u003e\n### url rewrite\n\ndevelopers can rewrite the default route given to exported functions with the 'urls' property applied\nto the attribute. \n\n```typescript\nattribute('index', { urls: ['/', '/home', 'home.html'] })\nexport function index (context) { \n    \n    context.response.send('index')\n}\n```\nnote: url rewriting is only available on index and named routes.\n\nnote: rewriting with regular expressions is currently not supported.\n\n\u003ca name=\"middleware\" /\u003e\n### middleware\n\nappex supports middleware with attributes. appex middleware defined with attributes allows\ndevelopers to scope middleware on single functions, or entire module scopes. appex will \nrecognise the 'use' property applied to the attribute to invoke middleware.\n\nthe following demonstrates how one might use middleware to secure a site admin.\n\nnote: middleware 'must' call next or handle the request. \n\n```typescript\ndeclare function attribute (qualifier:string, obj:any);\n\ndeclare var console;\n\nfunction authenticate(context) {\n\n    console.log('authenticate')\n\n\t// call next() if authenticated, otherwise, handle the response.\n    context.next(); \n}\n\nfunction authorize(context) {\n\n    console.log('authorize')\n\n\t// call next() if authorized, otherwise, handle the response.\n    context.next(); \n}\n\n// apply security middleware to admin scope.\nattribute('admin', {use: [authenticate, authorize]}) \nexport module admin {\n\n    export function index(context) {\n        \n        console.log(context.attribute); // view attribute\n\n        context.response.send('access granted!')\n    }\n}\n\n// index handler has no middleware applied.\nexport function index (context) { \n    \n    console.log(context.attribute); // view attribute\n\n    context.response.send('home')\n}\n```\n\n\n\u003ca name=\"exporting_functions\" /\u003e\n### exporting functions\n\nappex will only route functions prefixed with the TypeScript 'export' declarer. This rule\nalso applied to modules. Developers can use this to infer notions of public and private \nat the http level.\n\nconsider the following example:\n\n```typescript\n\n// module is not exported, and is \n// therefore private.\nmodule private_module {\n\t\n\t// function is exported, yet private \n\t// as a http endpoint due to the \n\t// parent module being private.\n\texport function public_method () { }\n\t\n\t// function is not exported, and is \n\t// private to this module.\n\tfunction private_method() { }\n}\n\n// function is not exported, and \n// is therefore private.\nfunction private_function() { }\n\n// function is exported, and therefore \n// publically accessible.\nexport function public_function   (context) { \n\t\n\t// this function can invoke \n\t// private functions.\n\tprivate_function(); // ok\n\t\n\t// calling exported method in \n\t// private module\n\tprivate_module.public_method(); // ok\n\n\t// calling non exported method \n\t// in private module\n\t// private_module.private_method(); // bad\n\n\tcontext.response.send('public_function');\n}\n```\n\n\u003ca name='handling_404' /\u003e\n### handling 404\n\nUse wildcard functions to catch unhandled routes.\n\n```typescript\n// http:[host]:[port]/\nexport function index (context) { \n\n\tcontext.response.send('home page');\n}\n\n// http:[host]:[port]/(.*)\nexport function wildcard (context, path) {\n\n\tcontext.response.send(404, path + ' not found');\n}\n```\n\u003ca name=\"serving_static_files\" /\u003e\n## serving static files\n\nUse wildcard functions with context.response.serve() to serve static content.\n\n```typescript\nexport module static {\n\t\n\t// http:[host]:[port]/static/(.*)\n\texport function wildcard(context, path) {\n\n\t\tcontext.response.serve('./static/', path);\n\t}\n}\n\n// http:[host]:[port]/\nexport function index (context) {\n\n\tcontext.response.send('home page');\n}\n\n// http:[host]:[port]/(.*)\nexport function wildcard(context, path) {\n\n\tcontext.response.send(404, path + ' not found');\n}\n```\n\n\u003ca name=\"templating\" /\u003e\n## templating\n\nappex comes bundled with a built in template engine which is modelled on the Microsoft \nRazor templating engine. The following sections outline its use.\n\n\u003ca name=\"template_overview\" /\u003e\n### overview\n\nThe appex template engine is available to all handlers by default. it is accessible\non the context.template property. the following is an example of its use.\n\n```\n//----------------------------------------------\n// view.txt\n//----------------------------------------------\n\n\u003cul\u003e\n@for(var n in context.users) {\n\n\t@if(context.users[n].online) {\n\t\t\t\n\t\t\u003cli\u003e@(context.users[n].name)\u003c/li\u003e\n\t}\n}\n\u003c/ul\u003e\n\n//----------------------------------------------\n// program.ts\n//----------------------------------------------\n\nexport function index(context) {\n\t\n    var users = [{name:'dave' , online : true}, \n                 {name:'smith', online : true}, \n                 {name:'jones', online : false}, \n                 {name:'alice', online : true}];\n\n    var text = context.template.render('./view.txt', { users: users });\n\t\n\tcontext.response.headers['Content-Type'] = 'text/html';\n\n    context.response.send(text);\n}\n\n```\n\n\u003ca name=\"template_context\" /\u003e\n### context\n\neach template is passed a data context. this context allows the caller to \nsend data to the template for rendering. the context parameter is optional.\nthe example below is sending the users array to the template context for \nrendering.\n\n```\nexport function index(context) {\n\t\n    var users  = [{name:'dave' , online : true}, \n                 {name:'smith', online : true}, \n                 {name:'jones', online : false}, \n                 {name:'alice', online : true}];\n\n    context.response.send(context.template.render('./view.txt', { users: users }));\n}\n```\n\n\u003ca name=\"template_syntax\" /\u003e\n### syntax\n\nappex templates support the following statements and syntax\n\n#### if statement\n\nif statments are supported.\n\n```\n@if(expression) {\n\tsome content\n}\n\n@if(a \u003e 10) {\n\tsome content\n}\n\n@(user.loggedin) {\n\t\u003cspan\u003ewelcome\u003c/span\u003e\n}\n```\n\n#### for statement\n\nthe following for loops are supported.\n\n```\n@for(var i = i; i \u003c 100; i++) {\n\t@(i)\n}\n\n@for(var n in list) {\n\t@(list[n])\n}\n```\n\n#### expressions\n\nwill emit the value contained.\n\n```\n@('hello world')\n\n@(123)\n\n@(some_variable)\n```\n\n#### code blocks\n\ncode blocks can be useful for adding template side rendering logic.\n\n```\n@{\n\tvar message = 'hello'\n}\n\n@(message)\n```\n\n#### comments\n```\n@*\n\tthis comment will not be rendered!\n*@\n```\n\n\u003ca name=\"template_layouts_and_sections\" /\u003e\n### layouts and sections\n\nappex templates support template inheritance.\n\nconsider the following where layout.txt defines the sections 'header' and 'content' and the view.txt overrides\nthese sections with its own content.\n\n```\n//----------------------------------------------\n// layout.txt\n//----------------------------------------------\n\n\u003chtml\u003e\n\n\t\u003chead\u003e\n\t\t\n\t\t@section header\n\n\t\u003c/head\u003e\n\n\t\u003cbody\u003e\n\n\t\t@section content {\n\t\t\n\t\t\t\u003cspan\u003esome default content\u003c/span\u003e\n\t\t\t\n\t\t}\n\n\t\u003c/body\u003e\n\t\n\u003c/html\u003e\n\n//----------------------------------------------\n// view.txt\n//----------------------------------------------\n\n@layout 'layout.txt'\n\n@section header {\n\n\t\u003ctitle\u003emy page\u003c/title\u003e\n}\n\n@section content {\n\n\t\u003cp\u003eoverriding the layout.txt content section.\u003c/p\u003e\n\n\t\u003cul\u003e\n\t@for(var n in context.users) {\n\n\t\t@if(context.users[n].online) {\n\t\t\t\n\t\t\t\u003cli\u003e@(context.users[n].name)\u003c/li\u003e\n\t\t}\n\t}\n\t\u003c/ul\u003e\n}\n\n//----------------------------------------------\n// program.ts\n//----------------------------------------------\n\nexport function index(context) {\n\t\n    var users = [{name:'dave' , online : true}, \n                 {name:'smith', online : true}, \n                 {name:'jones', online : false}, \n                 {name:'alice', online : true}];\n\n    var text = context.template.render('./view.txt', { users: users });\n\n    context.response.send(text);\n}\n```\n\nnote : when specifying a layout, the view will only render content within\nthe layouts section placeholders. \n\n\u003ca name=\"render\" /\u003e\n### render\n\nappex templates also allow for partial views with the @render statment. consider the following \nwhich renders the nav.txt file into the layout.txt file.\n\n```\n//----------------------------------------------\n// nav.txt\n//----------------------------------------------\n\u003cul\u003e\n\t\u003cli\u003ehome\u003c/li\u003e\n\t\u003cli\u003eabout\u003c/li\u003e\n\t\u003cli\u003econtact\u003c/li\u003e\n\u003c/ul\u003e\n\n//----------------------------------------------\n// layout.txt\n//----------------------------------------------\n\n\u003chtml\u003e\n\t\u003chead\u003e\n\t\t\n\t\t@section header\n\n\t\u003c/head\u003e\n\n\t\u003cbody\u003e\n\t\t\n\t\t@render 'nav.txt'\n\n\t\t@section content {\n\t\t\n\t\t\t\u003cspan\u003esome default content\u003c/span\u003e\n\t\t\t\n\t\t}\n\n\t\u003c/body\u003e\n\t\n\u003c/html\u003e\n\n//----------------------------------------------\n// view.txt\n//----------------------------------------------\n\n@layout 'layout.txt'\n\n@section header {\n\n\t\u003ctitle\u003emy page\u003c/title\u003e\n}\n\n@section content {\n\n\t\u003cp\u003eoverriding the layout.txt content section.\u003c/p\u003e\n\n\t\u003cul\u003e\n\t@for(var n in context.users) {\n\n\t\t@if(context.users[n].online) {\n\t\t\t\n\t\t\t\u003cli\u003e@(context.users[n].name)\u003c/li\u003e\n\t\t}\n\t}\n\t\u003c/ul\u003e\n}\n\n//----------------------------------------------\n// program.ts\n//----------------------------------------------\n\nexport function index(context) {\n\t\n    var users = [{name:'dave' , online : true}, \n                 {name:'smith', online : true}, \n                 {name:'jones', online : false}, \n                 {name:'alice', online : true}];\n\n    var text = context.template.render('./view.txt', { users: users });\n\n    context.response.send(text);\n}\n```\n\n\u003ca name=\"caching_and_devmode\" /\u003e\n### caching and devmode\n\nappex template content is not cached (the implementor is expected to handle their own caching)\nhowever the generated template code is. \n\nappex templates do inheriate the behaviour of the appex 'devmode' option. setting\ndevmode to 'true' will cause template code to be reloaded from disk and code generated with each \nrequest. setting devmode to false will load content from disk on first request, and \ncache the generated template code in memory for the lifetime of the application.\n\nin addition to this, a implementation where the devmode is false can override the caching \nbehaviour with the following.\n\n```typescript\nexport function index(context) {\n\n\t// manually override the template devmode option.\n\tcontext.template.option.devmode = true; \n\n\tcontext.response.send(context.template.render('./view.txt'))\n}\n```\n\n\u003ca name=\"sitemaps\" /\u003e\n## sitemaps\n\nappex is able to derive sitemap metadata automatically from http endpoints created with\ntypescript modules and functions. This metadata is useful to generate sitemap.xml\nfiles, as well as helping to create site navigation links when combined a template\nengine.\n\n\u003ca name=\"sitemap_generate\" /\u003e\n### generate sitemap\n\nappex sitemaps can be obtained from the context.sitemap property. \n\n```typescript\nexport function index(context) {\n\n\t// return all nodes in this site.\n\tcontext.response.json(context.sitemap)\n\n}\n```\n\nAdditionally, it may be helpful to isolate branches of the sitemap with the \ncontext.sitemap.get([qualifier]) function. as demonstrated below.\n\n```typescript\nexport module admin {\n\n\texport function index     (context) { }\n\n\texport function dashboard (context) { }\n\n\texport function content   (context) { }\n\n\texport module users {\n\n\t\texport function login(context) { }\n\n\t\texport function logout(context) { }\n\t}\n}\n\nexport function test(context) {\n\t\n\t// view all admin sitemap nodes\n\tcontext.response.json(context.sitemap.get('admin'))\n\n\t// view all admin.users sitemap nodes\n\t//context.response.json(context.sitemap.get('admin.users'))\n}\n```\n\n\u003ca name=\"sitemap_metadata\" /\u003e\n### attribute metadata\n\neach sitemap node contains the attribute applied to the handler for which the node applies. With this\ndevelopers can apply custom metadata for a given node. as demonstrated below.\n\n```typescript\ndeclare var attribute;\n\nattribute({website:'http://mysite.com/'}) // global\n\nattribute('index', {title:'home page'})\nexport function index(context) {\n\n\tcontext.response.send('index')\n}\n\nattribute('about', {title: 'about page'})\nexport function about(context) {\n\n\tcontext.response.send('about')\n}\n\nattribute('sitemap', {title: 'sitemap page'})\nexport function sitemap(context) {\n\n\tcontext.response.json(context.sitemap)\n}\n```\n\nvisiting /sitemap will output the following.\n\n```typescript\n{\n    \"name\": \"sitemap\",\n    \"nodes\": [\n        {\n            \"name\": \"index\",\n            \"urls\": [\n                \"/\"\n            ],\n            \"website\": \"http://mysite.com/\",\n            \"title\": \"home page\"\n        },\n        {\n            \"name\": \"about\",\n            \"urls\": [\n                \"/about\"\n            ],\n            \"website\": \"http://mysite.com/\",\n            \"title\": \"about page\"\n        },\n        {\n            \"name\": \"sitemap\",\n            \"urls\": [\n                \"/sitemap\"\n            ],\n            \"website\": \"http://mysite.com/\",\n            \"title\": \"sitemap page\"\n        }\n    ]\n}\n```\n\n\u003ca name=\"json_schema\" /\u003e\n## json schema\n\nappex provides functionality for generating json schemas from TypeScript classes\nand interfaces as well as tools for validating json data.\n\n\u003ca name=\"generating_schema\" /\u003e\n### generating schema\n\nThe following demonstrates generating json schema from the following class\nhierarchy.\n\n```typescript\nexport module model {\n\n    /** a product */\n    export class Product {\n        \n        /** the product name */\n        public name        : string;\n\n        /** the product description */\n        public description : string;\n\n        /** the product cost */\n        public cost        : number;\n    } \n\n    /** a order */\n    export class Order {\n        \n        /** the product being ordered */\n        public products  : Product;\n    }\n\n    /** a customer */\n    export class Customer {\n\n        /** the customers firstname */\n        public firstname  : string;\n\n        /** the customers lastname */\n        public lastname   : string;\n\n        /** orders made by this customer */\n        public orders     : Order[];\n    }\n}\n\nexport function index (context:appex.web.IContext) {\n\t\n\t// pass the fully qualified name of the type.\n    var schema = context.schema.get('model.Customer');\n\n    context.response.json(schema);\n}\n```\n\nwhich generates the following json schema.\n\n```typescript\n{\n    \"id\": \"model.Customer\",\n    \"type\": \"object\",\n    \"description\": \"a customer\",\n    \"properties\": {\n        \"firstname\": {\n            \"type\": \"string\",\n            \"description\": \"the customers firstname\",\n            \"required\": true\n        },\n        \"lastname\": {\n            \"type\": \"string\",\n            \"description\": \"the customers lastname\",\n            \"required\": true\n        },\n        \"orders\": {\n            \"type\": \"array\",\n            \"items\": {\n                \"type\": {\n                    \"id\": \"model.Order\",\n                    \"type\": \"object\",\n                    \"description\": \"a order\",\n                    \"properties\": {\n                        \"products\": {\n                            \"id\": \"model.Product\",\n                            \"type\": \"object\",\n                            \"description\": \"a product\",\n                            \"properties\": {\n                                \"name\": {\n                                    \"type\": \"string\",\n                                    \"description\": \"the product name\",\n                                    \"required\": true\n                                },\n                                \"description\": {\n                                    \"type\": \"string\",\n                                    \"description\": \"the product description\",\n                                    \"required\": true\n                                },\n                                \"cost\": {\n                                    \"type\": \"number\",\n                                    \"description\": \"the product cost\",\n                                    \"required\": true\n                                }\n                            },\n                            \"required\": true\n                        }\n                    }\n                }\n            },\n            \"description\": \"orders made by this customer\",\n            \"required\": true\n        }\n    }\n}\n```\na quick note...\n\nwhen generating schema from classes:\n\n* only public class variables will be emitted.\n* all properties will be marked as \"required\".\n\nwhen generating schema from interfaces:\n\n* all properties will be emitted. \n* all properties will be marked as \"required\" unless modified with '?'.\n\n\u003ca name=\"validating_json\" /\u003e\n### validating json\n\nappex supports json schema validation from class and interface definitions. consider the following...\n\n```typescript\ninterface Customer {\n\n    firstname    : string;\n\n    lastname     : string;\n\n    age          : number;\n\n    emails       : string[];\n\n    option_a ?   : boolean; // optional\n\n    option_b ?   : boolean; // optional\n\n}\n\nexport function index(context) {\n\n    // a customer with invalid data.\n\n    var customer = {\n\n        firstname    : 'dave',\n\n        age          : '33',\n\n        emails       : [12345, 'dave@domain.com', true],\n\n        option_b     : 1,\n\n        option_c     : 1\n    }\n\n    // do validation.\n\n    var errors = context.schema.validate('Customer', customer);\n\n    if(errors) {\n\n        context.response.json(errors);\n    }\n}\n```\n\nwill output the following.\n\n```typescript\n[\n    {\n        \"message\": \"instance.lastname is required.\"\n    },\n    {\n        \"message\": \"instance.age is not a number\"\n    },\n    {\n        \"message\": \"instance.emails[0] is not a string\"\n    },\n    {\n        \"message\": \"instance.emails[2] is not a string\"\n    },\n    {\n        \"message\": \"instance.option_b is not a boolean\"\n    },\n    {\n        \"message\": \"instance.option_c unexpected property\"\n    }\n]\n```\n\n\u003ca name=\"web_service_descriptions\"\u003e\n### web service descriptions\n\nFor those using appex for web services, developers can leverage appex json schema generation\nto generate endpoint metadata (think wsdl). Consider the following which leverages both appex \nschema generation and attributes to produce a metadata endpoint consumers of your\napi can use to see what data the endpoint http://example.com/customer/create accepts \nand returns.\n\n```typescript\nclass Request {\n\n    /** the customers firstname */\n    firstname : string;\n\n    /** the customers lastname */\n    lastname  : string;\n\n\t/** the customers lastname */\n}\n\nclass Response {\n\n    /** true on success  */\n    success:boolean;\n    \n    /** an array of validation errors  */\n    errors : string[];\n}\n\nattribute('metadata', {input  : 'Request', output : 'Response'})\nexport function metadata(context:appex.web.IContext) {\n\n    var metadata = {\n        \n\t\tendpoint : 'http://example.com/customer/create',\n\n        input    : context.schema.get(context.attribute.input),\n\n        output   : context.schema.get(context.attribute.output)\n    }\n\n    context.response.json(metadata)\n}\n```\n\nwhich outputs the following.\n\n```typescript\n{\n    \"endpoint\": \"http://example.com/customer/create\",\n    \"input\": {\n        \"id\": \"Request\",\n        \"type\": \"object\",\n        \"properties\": {\n            \"firstname\": {\n                \"type\": \"string\",\n                \"description\": \"the customers firstname\",\n                \"required\": true\n            },\n            \"lastname\": {\n                \"type\": \"string\",\n                \"description\": \"the customers lastname\",\n                \"required\": true\n            }\n        }\n    },\n    \"output\": {\n        \"id\": \"Response\",\n        \"type\": \"object\",\n        \"properties\": {\n            \"success\": {\n                \"type\": \"boolean\",\n                \"description\": \"true on success\",\n                \"required\": true\n            },\n            \"errors\": {\n                \"type\": \"array\",\n                \"description\": \"an array of validation errors\",\n                \"items\": {\n                    \"type\": \"string\"\n                },\n                \"required\": true\n            }\n        }\n    }\n}\n```\ntip: use the appex sitemap metadata to produce a metadata endpoint for all service methods \nin your application.\n\n\n\u003ca name=\"reflection\" /\u003e\n## reflection\n\nappex provides a reflection api derived from TypeScript's type system that developers can \nleverage to reflect type information declared throughout their appex modules. \n\nthe following section outlines how to use the reflection api.\n\n\u003ca name=\"reflect_everything\" /\u003e\n### reflect everything\n\nthe appex reflection api is passed on the context.module.reflection property and is available to all\nappex handler methods. The following code will JSON serialize everything declared in your appex\nproject and write it to the http response. \n\n```typescript\nexport function index (context:appex.web.Context) {\n    \n    context.response.json( context.module.reflection );\n}\n```\n\n\u003ca name=\"reflect_specific_types\" /\u003e\n### reflect specific types\n\nIn typical scenarios, developers will want to leverage reflection meta data to generate\nservice contacts and client side models. the reflection api lets you access meta data \nfor the following types declared in your project. \n\n* modules\n* imports\n* classes\n* interfaces\n* functions\n* variables\n\nto access specific type metadata, use the reflection.get([qualifier]) method, as demonstrated below.\n\n```typescript\nexport module model {\n    \n    export class Customer {\n\n        public firstname   : string;\n\n        public lastname    : string;\n\n        public age         : number;\n    }\n}\n\nexport function index (context:appex.web.Context) {\n    \n    context.response.json( context.module.reflection.get('model.Customer') );\n}\n```\nand methods..\n\n```typescript\nfunction some_method(a:string, b:number, c?:boolean) : void { }\n\nexport function index (context:appex.web.Context) {\n    \n    context.response.json( context.module.reflection.get('some_method') );\n}\n```\n\n....and variables...\n\n```typescript\nvar some_variable:number = 10;\n\nexport function index (context:appex.web.Context) {\n    \n    context.response.json( context.module.reflection.get('some_variable') );\n}\n```\n\n\u003ca name=\"developing_with_appex\" /\u003e\n## developing with appex\n\nThis section outlines development with appex.\n\n\u003ca name=\"appex_declaration\" /\u003e\n### appex.d.ts declaration\n\nIf you develop on a TypeScript complicant editor (one that supports TS 0.9), appex comes bundled\nwith a declaration file you can reference in your project. If installing appex via npm, your\nreference should be as follows.\n\n```typescript\n/// \u003creference path=\"node_modules/appex/appex.d.ts\" /\u003e\n\nexport function index (context:appex.web.IContext) { \n    \n    context.response.send('hello');\n}\n\nexport function wildcard(context:appex.web.IContext, path:string) {\n\n    context.response.serve('./', path);\n}\n```\n\nBy referencing this in your project, you get the benefits of code completion and static type checking\nagainst both appex, and the nodejs core.\n\n![](https://raw.github.com/sinclairzx81/appex/master/artifacts/code-completion.jpg)\n\nAdditional declaration files may be obtained from [here](https://github.com/borisyankov/DefinitelyTyped)\n\n\u003ca name=\"structuring_projects\" /\u003e\n### structuring projects\n\nappex includes TypeScript's ability to reference source files with the 'reference' element. appex \nwill traverse each source files references and include it as part of the compilation.\n\nDevelopers can use this functionality to logically split source files into reusable components of\nfunctionality, as demonstrated below. \n\n```typescript\n//---------------------------------------------------\t\n// file: app.js\n//---------------------------------------------------\n\nvar appex = require('appex');\n\nvar app = appex ({ program : './index.ts' });\n\napp.listen(3000);\n\n//---------------------------------------------------\t\n// file: index.ts\n//---------------------------------------------------\n\n/// \u003creference path=\"users.ts\" /\u003e\n/// \u003creference path=\"pages.ts\" /\u003e\n\n//---------------------------------------------------\t\n// file: users.ts\n//---------------------------------------------------\n\nexport module users {\n\t\n\t// http://[host]:[port]/users/login\n\texport function login  (context) { context.response.send('users.login') }\n\t\n\t// http://[host]:[port]/users/logout\n\texport function logout (context) { context.response.send('users.logout') }\n}\n\n//---------------------------------------------------\t\n// file: pages.ts\n//---------------------------------------------------\n\n// http://[host]:[port]/\nexport function index   (context) { context.response.send('home') }\n\n// http://[host]:[port]/about\nexport function about   (context) { context.response.send('about') }\n\n// http://[host]:[port]/contact\nexport function contact (context) { context.response.send('contact') }\n\nexport function wildcard (context, path) { context.response.send(404, ' not found') }\n\n```\n\n\u003ca name=\"resources\" /\u003e\n## additional resources\n\n* [typescript homepage](http://www.typescriptlang.org/)\n* [typescript language specification](http://www.typescriptlang.org/Content/TypeScript%20Language%20Specification.pdf)\n* [typescript declarations repository](https://github.com/borisyankov/DefinitelyTyped)\n\n\u003ca name=\"license\" /\u003e\n## license\n\nThe MIT License (MIT)\n\nCopyright (c) 2013 Haydn Paterson (sinclair) \u003chaydn.developer@gmail.com\u003e\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsinclairzx81%2Fappex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsinclairzx81%2Fappex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsinclairzx81%2Fappex/lists"}