appex is a nodejs web api framework built on top of the TypeScript programming language. It enables
developers to create RESTful service endpoints by writing TypeScript functions, as well as providing
reflection / type meta data derived from the languages type system.
### wildcard handlers
Wildcard handlers resolve their urls to their current module scope + url.
appex wildcard handlers allow for wildcard routing at a given module scope. Wildcard handlers
support 'typed' url argument mapping, as denoted by the arguments annotation.
In addition, wildcard handlers also support optional arguments which can be specified with TypeScript's '?'
on argument names.
appex wildcard handlers require the following signature:
- name - 'wildcard'
- argument[0] - app context
- argument[n] - 1 or more arguments to be mapped from the url
- returns - void (optional)
declare var console;
export module blogs {
export function wildcard(context, year:number, month:number, day?:number) {
context.response.json({ year: year, month: month, day: day})
}
}
export function index(context) {
context.response.send('home');
}
export function wildcard(context, path) {
context.response.send(404, 'not found');
}
note: appex supports boolean, number, string and any annotations on wildcard arguments. if no annotation
is specified, appex interprets the argument as a string. the type 'any' is also interpreted as string.
note: wildcard functions should be declared last in any module scope. this ensures other routes
will be matched first.
### cascades
appex supports a cascading attribute scheme on modules and functions. With this, developers can apply
arbituary meta data for modules and functions that will propagate through scope. appex has two special
cascade properties for middleware and http verb matching, which are described below, however consider
the following code which illustrates the concept.
declare function cascade (qualifier:string, obj:any);
cascade({a: 10});
cascade('foo', {b : 20})
export module foo {
cascade('foo.bar', {c : 30})
export module bar {
cascade('foo.bar.index', {d : 40})
export function index(context) {
context.response.json( context.cascade );
}
}
}
### http verbs
appex handles http verb matching with cascades. appex will recognise the
'verbs' property applied to the cascade to match against http verbs.
cascade('index', { verbs: ['get'] })
export function index (context) {
context.response.send('index')
}
cascade('index', { verbs: ['post', 'put'] })
export function submit (context) {
context.response.send('submit')
}
### middleware
appex supports middleware with cascades. appex middleware defined with cascades allows
developers to scope middleware on single functions, or entire module scopes. appex will
recognise the 'use' property applied to the cascade to invoke middleware.
the following demonstrates how one might use middleware to secure a site admin.
note: middleware 'must' call next or handle the request.
declare function cascade (qualifier:string, obj:any);
declare var console;
function authenticate(context) {
console.log('authenticate')
context.next();
}
function authorize(context) {
console.log('authorize')
context.next();
}
cascade('admin', {use: [authenticate, authorize]})
export module admin {
export function index(context) {
console.log(context.cascade);
context.response.send('access granted!')
}
}
export function index (context) {
console.log(context.cascade);
context.response.send('home')
}
### exporting functions
appex will only route functions prefix with the TypeScript 'export' declarer. This rule
also applied to modules. Developers can use this to infer notions of public and private
at the http level.
consider the following example:
module private_module {
export function public_method () { }
function private_method() { }
}
function private_function() { }
export function public_function (context) {
private_function();
private_module.public_method();
context.response.send('public_function');
}
## serving static files
Use wildcard functions with context.response.serve() to serve static content.
export module static {
export function wildcard(context, path) {
context.response.serve('./static/', path);
}
}
export function index (context) {
context.response.send('home page');
}
export function wildcard(context, path) {
context.response.send(404, path + ' not found');
}
### reflect specific types
In typical scenarios, developers will want to leverage reflection meta data to generate
service contacts and client side models. the reflection api lets you access meta data
for the following types declared in your project.
- modules
- imports
- classes
- interfaces
- functions
- variables
to access specific type metadata, use the reflection.get([qualifier]) method, as demonstrated below.
export module models {
export class Customer {
public firstname : string;
public lastname : string;
public age : number;
}
}
export function index (context:appex.web.Context) {
context.response.json( context.module.reflection.get('models.Customer') );
}
and methods..
function some_method(a:string, b:number, c?:boolean) : void { }
export function index (context:appex.web.Context) {
context.response.json( context.module.reflection.get('some_method') );
}
....and variables...
var some_variable:number = 10;
export function index (context:appex.web.Context) {
context.response.json( context.module.reflection.get('some_variable') );
}
### appex.d.ts declaration
If you develop on a TypeScript complicant editor (one that supports TS 0.9), appex comes bundled
with a declaration file you can reference in your project. If installing appex via npm, your
reference should be as follows.
export function index (context:appex.web.IContext) {
context.response.send('hello');
}
export function wildcard(context:appex.web.IContext, path:string) {
context.response.serve('./', path);
}
By referencing this in your project, you get the benefits of code completion and static type checking
against both appex, and the nodejs core.
### structuring projects
appex includes TypeScript's ability to reference source files with the 'reference' element. appex
will traverse each source files references and include it as part of the compilation.
Developers can use this functionality to logically split source files into reusable components of
functionality, as demonstrated below.
var appex = require('appex');
var app = appex ({ program : './index.ts' });
app.listen(3000);
export module users {
export function login (context) { context.response.send('users.login') }
export function logout (context) { context.response.send('users.logout') }
}
export function index (context) { context.response.send('home') }
export function about (context) { context.response.send('about') }
export function contact (context) { context.response.send('contact') }
export function wildcard (context, path) { context.response.send(404, ' not found') }