Skip to content

atmajs/atma-server

Repository files navigation

Atma Node.js Server Module

NPM version CircleCI

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 resources

  • config.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 processing
    atma.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

©️ 2014-2015; MIT; The Atma.js Project

About

Node.js HttpApplication

Resources

Stars

Watchers

Forks

Packages

No packages published