Astad
Quick Start with Koajs
import { HttpApp, HttpKoa, HttpCors } from 'astad';
import Koa from 'koa';
import koaLogger from 'koa-logger';
import { koaBody } from 'koa-body';
import ejs from 'ejs';
import { Config } from './config.js';
import web from './http/web/index.js';
import api from './http/api/index.js';
const app = new Koa();
app.use(koaLogger());
app.use(koaBody({ multipart: true, formidable: { keepExtensions: true } }));
const httpApp = new HttpApp({
use: new HttpKoa(app),
conf: Config,
});
httpApp.use(new HttpCors());
httpApp.viewEngine(new class {
async render(template: string, data: Record<any, any> = {}) {
return await ejs.renderFile(`./resources/views/${template}.ejs.html`, data);
}
});
httpApp.router(web);
httpApp.router('/api', api);
httpApp.listen();
Config file example
import { config } from 'dotenv';
import { Conf } from 'astad/conf';
config({
path: '.env',
});
export const Config = new Conf();
Router example
Web router file
import { HttpRouter } from 'astad/http';
const web = new HttpRouter();
web.get('/', async ctx => {
await ctx.view('welcome');
});
export default web;
Below is api router file
import { HTTP_KEY_REQ_ID, HttpRouter } from 'astad/http';
const api = new HttpRouter();
api.get('/', async ctx => {
ctx.json({
name: 'Astad Example',
ver: '0.1.0',
reqid: ctx.value(HTTP_KEY_REQ_ID),
});
});
export default api;
Console application
import { Astad, AstadContext, AstadCompose } from 'astad';
class TestCommand {
signature: string = "test";
description: string = "This is test command!";
compose() {
const command = new AstadCompose(this.signature);
return command;
}
async handle(ctx: AstadContext) {
ctx.info('This is test command!');
}
}
const cmd = new Astad();
cmd.register(new TestCommand);
await cmd.handle();
Container example
import { Container, ContainerScope } from 'astad/container';
const container = new Container('default');
container.register({
id: 'static value',
scope: ContainerScope.Value,
value: 'Any static value',
});
container.register({
id: 'static date',
scope: ContainerScope.Singleton,
value: () => new Date(),
});
container.register({
id: 'current date',
scope: ContainerScope.Transient,
value: () => new Date(),
});
container.register({
id: 'context date',
scope: ContainerScope.Contextual,
value: () => new Date(),
});
console.log(container.resolve('static value'));
console.log(container.resolve('static date'));
console.log(container.resolve('static date'));
console.log(container.resolve('current date'));
console.log(container.resolve('current date'));
Testing
Testing routes, can use with any testing tool
import t from 'tap';
import { TestHttpRouter } from 'astad/testing';
import routes from './api.js';
const testroute = TestHttpRouter(routes);
function createRequest(
method: string,
path: string,
body?: Record<string, any>,
) {
const req: any = {
method: method,
path: `/api${path}`,
body,
headers: { accept: 'application/json' },
};
if (body) {
req.headers['content-type'] = 'application/json';
}
return testroute.req(req);
}
t.test('route', async t => {
const res = await createRequest('GET', '/').exec();
t.equal(res.status, 200);
const data = res.json();
t.hasOwnProp(data, 'name');
});
Testing services
import t from 'tap';
import { TestMinimalContext } from 'astad/testing';
import Service from './service.js';
const ctx = new TestMinimalContext();
t.test('service', async t => {
await ctx.run(async () => {
const service = new Service();
const data = await service.test();
t.ok(data);
})
});
TODO
- http: should consider accept header
- http: should compose or translate error to response
- http: improve throw in context, should accept error and custom inputs
- http/cors: should allow vary header configuration, for asset caching
- docs: automate documentation
In-progress
- events handler
- background queue
- data mapper
- templating
- testing
Bugs
- handle duplicate middlewares in router, should only have unique middlewares