atma-server
v0.5.25
Published
Server Application
Downloads
5,845
Readme
Atma Node.js Server Module
Overview
Can be used as a Connect Middleware
This module uses:
To setup a bootstrap project use Atma.Toolkit - $ atma gen server
HttpApplication
var atma = require('atma-server');
atma
.server
.Application({
base:__dirname,
configs: '/server/config/**.yml'
})
.done(function(app){
// configuration and resources are loaded
app
.processor({
// pipeline is executed on every request
before: [
function(req, res, next){ next() },
]
// this pipeline is executed only if the application finds any endpoint
// (server, handler, page, subapp)
middleware: [
// refer to connectjs middleware documentation
function(req, res, next){ next() },
require('body-parser').json(),
],
// otherwise, if response was not completed by any middleware or any endpoint before
// continue with this middleware pipeline.
after: [
function(req, res, next){ next() },
atma.server.middleware.static
]
})
// start server, portnumber is taken from the configuration
.listen();
// or start the server manually:
var server = require('http')
.createServer(app.process)
.listen(app.config.$get('port'));
if (app.config.debug)
app.autoreload(server);
});
Configuration
appcfg module is used to load the configurations and the routings. Default path is the /server/config/**.yml
.
The default configuration can be viewed here - link
Resources
scripts / styles for the NodeJS application itself and for the web pages. They are defined in:
config.env.both.scripts<Object|Array>
config/env/both.yml
- shared resourcesconfig.env.server.scripts<Object|Array>
config/env/server.yml
- resources for the nodejs application, e.g. server side components paths.config.env.client.scripts<Object|Array>
,config.env.client.styles<Object|Array>
config/env/client.yml
- resources, that should be loaded on the client.In the DEV Mode all client-side scripts/styles/components are served to browsers without concatenation. For the production compile resources with
atma custom node_modules/atma-server/tools/compile
Define scripts and styles for a particular page in page routing.
Routing
subapps
config/app.yml
subapps: // all `rest/*` requests are piped to the Api Application // `Api.js` should export the `atma.server.Application` instance 'rest': '/../Api.js'
handlers
config/handlers.yml
handler: location: /server/http/handler/{0}.js #< default handlers: # route - resource that exports a HttpHandler '/foo': 'baz' # path is '/server/http/handler/baz.js' # method is '*' '$post /qux': 'qux/postHandler' # path is '/server/http/handler/quz/postHander.js' # method is 'POST'
services
config/services.yml
service: location: /server/http/service/{0}.js #< default services: # route - resource that exports a HttpService @see HttpService '/user': 'User' # path is '/server/http/service/User.js' # method is '*' # futher routing is handled by the service, like '/user/:id'
pages
config/pages.yml
page: # see default config to see the default page paths pages: # route - Page Definition /: id: index #optional, or is generated from the route template: quz #optional, or is equal to `id` # path is `/server/http/page/quz/quz.mask master: simple #optional, or is `default` # path is `/server/http/master/simple.mask` # optional secure: # optional, default - any logged in user role: 'admin' scripts: # scripts for the page styles: # styles for the page # any other data, which then is accessable via javascript or mask # `ctx.page.data.title` title: String # rewrite the page request to some other route rewrite: String # redirect the page request to some other route redirect: String
Endpoints
There are 4 types of endpoints in route lookup order
Sub Application
We support application nesting, that means you can bind another server application instance for the route e.g. /api/
and all /api/**
requests are piped to the app.
Each application instance has its own settings and configurations. This allows to create highly modular and composit web-applications.
Handler
To declare a Handler is as simple as to define a Class/Constructor with Deferred(Promise) Interface and process
function in prototypes, like this
// server/http/handler/hello.js
module.exports = Class({
Base: Class.Deferred,
process: function(req, res){
this.resolve(
data String | Object | Buffer,
?statusCode Number,
?mimeType String,
?headers Object
);
this.reject(error)
}
});
To bind for a route(server/config/handlers.yml
):
handler:
location: '/server/http/handler/{0}.js'
# <- default
handlers:
'/say/hello': Hello
'(\.less(\.map)?$)': LessHandler
'(\.es6(\.map)?$)': TraceurHandler
Usually, this are the low level handlers, like 'less' preprocessor.
But the interface (Deferred + process(req, res))
is same also for HttpService and HttpPage
HttpEndpoint
Class and decorators oriented HttpService
import { HttpEndpoint, deco } from 'atma-server'
@deco.route('/foo')
@deco.isAuthorized()
export default class MyEndpoint extends HttpEndpoint {
@deco.isInRole('admin')
async '$get /:id' (
@deco.fromUri('id', Number) id: number
) {
return service.fetch(id)
}
}
Decorators can be applied to the class or methods
HttpEndpoint.isAuthorized()
HttpEndpoint.isInRole(...roles: string[])
HttpEndpoint.hasClaim(...roles: string[])
HttpEndpoint.origin(origin: string = "*")
HttpEndpoint.middleware(fn: (req, res?, params?) => Promise<any> | any | void)
HttpEndpoint.createDecorator(methods: ICreateDecorator)
HttpEndpoint.fromUri(name, Type?)
HttpEndpoint.fromBody(Type)
Decorators are also accessable via
deco
export, e.g.:deco.isAuthorized()
interface ICreateDecorator {
forCtor (Ctor: Function, meta: IHttpEndpointMeta): Function | void;
forMethod (Proto: any, method: IHttpEndpointMethod): IHttpEndpointMethod | void
}
HttpService
Service routes
For the route docs refer to RutaJS
Sample:
module.exports = atma.server.HttpService({
'$get /': Function | Endpoint
'$post /': ...
'$get /:name(foo|bar|qux)': ...
'$put /user': ...
})
Service endpoints
Function
atma.server.HttpService(/*endpoints*/ {
// route:handler
'route': function(req, res, params){
this.resolve(/*@see Handler*/);
this.reject(...);
},
'route': {
process: function(){ ... }
}
})
Meta - help & validation
- help - list all endpoints of a service with there meta information.
http://127.0.0.1/rest/user?help
- validation - when sending data with
post
/put
, httpservice will validate it before processingatma.server.HttpService({ '/route': { meta: { description: 'Lorem...', /* For request validating and the documentation */ arguments: { // required, not empty string foo: 'string', // required, validate with regexp age: /^\d+$/, // optional, of type 'number' '?baz': 'number', // unexpect '-quz': null, // validate subobject jokers: { left: 'number', right: 'number' }, // validate arrays collection: [ {_id: 'string', username: 'string'} ] }, // allow only properties which are listed in `arguments` object strict: false, /* Documentation purpose only*/ response: { baz: 'string', ... } }, process: function(req, res, params) { ... } } })
- Headers Set default headers for the service
atma.server.HttpService({ '/route': { meta: { headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept' } }, process: function(req, res, params) { ... } } });
Barricades (Middlewares)
atma.server.HttpService({
// route - Barricade (Middleware pattern)
'/route': [
function(req, res, params, next){
// error example
if (req.body.name == null){
next('Name argument expected');
return;
}
// continue
req.name = req.body.name;
next();
// stop processing
this.resolve(...);
this.reject(...);
},
function(req, res, params, next){
...
},
...
],
// same with `help`
'/other/route': {
meta: { ... }
process: [
fooFunction,
bazFunction,
...
]
}
})
Service and the application routing example
// server/http/service/time.js
module.exports = atma.server.HttpService({
'/': function(req, res){
this.resolve('This is a time service');
},
'/:transport(console|file|client)': function(req, res, params){
var time = new Date().toString(),
that = this;
switch(params.transport){
case 'console':
console.log(' > time', time);
this.resolve('Pushed to console');
return;
case 'file':
io
.File
.writeAsync('someFile.txt')
.pipe(this, 'fail')
.done(() => {
this.resolve('Saved to file');
});
return;
case 'client':
this.resolve(time);
return;
}
}
})
# server/config/services.yml
service:
location: /server/http/service/{0}.js'
# <- default
services:
'/time': time
HttpPage
HttpPage consists of 3 parts
- Controller
- Master View Template
- View Template
You would rare redefine the default controller, as each Page should consist of a component composition, so that the logic could be moved to each component. We wont explain what a component is, as you should refer to MaskJS and MaskJS.Node Some things we remind:
Context
{ req: <Request>, res: <Response>, page: <HttpPage (current instance)> }
Render-mode
mode: 'server' | 'client' | 'both' // @default is 'both' modeModel: 'server' // if `server` is defined, the model wont be serialized
Cache Each components output could be cached and the conditions could be defined.
byProperty
: For each unique value from model or ct
Example
mask.registerHandler(':requestedUrl', Compo({
mode: 'server:all'
modelMode: 'server:all'
cache: {
byProperty: 'ctx.req.url'
},
onRenderStart: function(model, ctx){
this.nodes = jmask('h4').text(ctx.req.url);
}
}))
Going back to the HttpPage, lets start from a master view template
Master View
Refer to the layout component
Example:
// server/http/master/default.mask
layout:master #default {
:document {
head {
meta http-equiv="Content-Type" content="text/html;charset=utf-8";
meta name="viewport" content="maximum-scale=1.5, minimum-scale=.8, initial-scale=1, user-scalable=1";
title > "Atma.js"
atma:styles;
}
body {
@placeholder #body;
atma:scripts;
}
}
}
Page View
// server/http/page/hello.mask
layout:view master=default {
@content #body {
'Hello World'
}
}
The routing is also made via the configuration files
# server/config/pages.yml
pages:
'/hello':
id: hello
Preprocessors
E.g., to use ES6
or Less
files, please install server plugins
# atma.toolkit, is only a helper util to intall plugins (further on is not required)
$ npm i atma -g
$ atma plugin install atma-loader-traceur
$ atma plugin install atma-loader-less
:copyright: 2014-2015; MIT; The Atma.js Project