@litstack/core

Typescript REST Framework

Stats

StarsIssuesVersionUpdatedCreatedSize
@litstack/core
150.9.15 months ago3 years agoMinified + gzip package size for @litstack/core in KB

Readme

npm version Build Status Coverage Status

Litstack

Using Angular and Spring boot design patterns, Litstack is a Typescript REST framework that Angular/Spring Boot engineers already know how to use, keeping their code organized and concise, and pushing off the express wiring to the library.

Getting Started

Option 1: Clone the seed app

Follow the directions in the litstack seed to get started right away.

Option 2: Start with a blank slate

Create a new project and install the Litstack core library.

> npm install @litstack/core --save

Make sure experimental decorators are on in your tsconfig.json at the root level:

{
    "compilerOptions": {
        "experimentalDecorators": true
    }
}

For more on building, see the minimum configuration section below.

Bootstrapping

Bootstrap app.module at the index.ts level:

// in index.ts
import { LitCompiler } from '@litstack/core/dist/compiler';

import { AppModule } from './modules/app.module';

LitCompiler.bootstrap(AppModule);

Modules

Modules can export component routes and package other modules.

Basic Module

This module will export app.component's routes.

// in app.module.ts
import { LitModule } from '@litstack/core';

import { AppComponent } from './app.component';

@LitModule({
    exports: [
        AppComponent
    ]
})
export class AppModule {
}

Module with Imports

Modules can also import other modules with components:

import { LitModule } from '@litstack/core';

import { ItemsModule } from  './modules/items/items.module';
import { OrdersModule } from  './modules/orders/orders.module';
import { PeopleModule } from  './modules/people/people.module';
import { VersionComponent } from  './components/version/version.component';

@LitModule({
    path: 'api', // will add all imported routes at '/api/..'
    imports: [ 
        ItemsModule,
        PeopleModule,
        OrdersModule
    ],
    exports: [
        VersionComponent
    ]
})
export class AppModule {
}

Components

Components register route listeners:

Basic Component

// in app.component.ts
import { LitComponent } from '@litstack/core';
import { HttpResponse } from '@litstack/core/dist/http';
import { GetMapping } from '@litstack/core/dist/http/mappings';

@LitComponent()
export class AppComponent {

    private message = 'Hello World!';

    @GetMapping() // defaults to '/'
    onHello(res: HttpResponse): void {
        res.success({
            message: this.message
        });
    }
}

Using path params

Specify params in the path:

import { LitComponent } from '@litstack/core';
import { HttpRequest, HttpResponse } from '@litstack/core/dist/http';
import { GetMapping } from '@litstack/core/dist/http/mappings';

@LitComponent()
export class ItemsComponent {

    @GetMapping({
        path: ':id'
    })
    getItem(req: HttpRequest, res: HttpResponse): void {
        res.success({
            id: req.params.id
        });
    }
}

Chaining methods with next

We can keep our route functionality isolated by using the "next" param:

import { LitComponent } from '@litstack/core';
import { HttpRequest, HttpResponse, HttpNext } from '@litstack/core/dist/http';
import { PutMapping } from '@litstack/core/dist/http/mappings';

@LitComponent()
export class ItemsComponent {

    // NOTE: The order matters here:
    @PutMapping({
        path: ':id'
    })
    updateItem(req: HttpRequest, res: HttpResponse, next: HttpNext) {

        if(req.param.id === 'some-id') {

            // do some update
            res.success({ id: 'some-id' });
            return;
        }

        next();
    }

    // same route as above, will run only if "next" is called
    @PutMapping({
        path: ':id'
    })
    updateItemErr(res: HttpResponse) {
        
        res.error(404);
    }
}

Dependency Injection

Services are a great place for business logic:

// ./services/items.service
import { LitService } from '@litstack/core';

@LitService()
export class ItemsService {

    get description(): string {
        return 'This is an item description';
    }
}

And then in our component:

import { LitComponent } from '@litstack/core';
import { HttpResponse } from '@litstack/core/dist/http';
import { GetMapping } from '@litstack/core/dist/http/mappings';

import { ItemsService } from './services/items.service';

@LitComponent()
export class ItemsComponent {

    constructor(private itemsService: ItemsService) {}

    @GetMapping()
    getItems(res: HttpResponse) {
        res.success({
            description: this.itemsService.description
        });
    }
}

Testing

Test components using supertest methods and the Litstack TestBed:

import { TestBed, LitComponentTest } from '@litstack/core/dist/testing';

import { AppComponent } from './app.component';

describe('AppComponent', () => {

    let component: LitComponentTest;

    beforeEach(() => {
        
        component = TestBed.start(AppComponent);
    });

    afterEach(() => {

        TestBed.stop();
    });

    it('should return a welcome message', (done) => {

        component
            .get('/')
            .expect(200)
            .expect((res) => {
                expect(res.body.message).to.equal('Hello World!');
            })
            .end((err, res) => {
                if (err) return done(err);
                done();
            });
    });
});

Minimum Configuration

The build process can be customized to the needs of the project, but a minimum configuration could look like this:

> mkdir my-project
> cd my-project
> npm init -y
> npm install @litstack/core --save
> npm install typescript -D
> npm install ts-node -D

Change the following in package.json:

{
  "main": "dist/index.js",
  "scripts": {
    "start": "tsc && node dist/index.js"
  }
}

A tsconfig.json could look like this:

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es5",
        "outDir": "dist",
        "lib": [
            "es7"
        ],
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true
    },
    "include": [
        "**/*.ts"
    ]
}

Now, run the app:

> npm start

Have fun!

If you find any bugs or have a feature request, please open an issue on github!

The npm package download data comes from npm's download counts api and package details come from npms.io.