Git Product home page Git Product logo

litstack's Introduction

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!

litstack's People

Stargazers

 avatar

Watchers

 avatar  avatar  avatar

Forkers

jsfix-ci

litstack's Issues

Allow request method stacking

The framework should allow for multiple requests types.

@RequestMapping({
    type: [ RequestMethods.GET, RequestMethods.DELETE ]
})

Unable to use litstack v0.4.0

Describe the bug
Installing litstack v0.4.0 does not install the dist as it is ignored in npmignore

To Reproduce
Steps to reproduce the behavior:

  1. Type npm install
  2. Attempt to use the library

Add middleware at module level

An argument could be made that this could be done with next methods... could possibly close this if that meets the need.

Limit requests at the endpoint / component / module level

Is your feature request related to a problem? Please describe.
In order to prevent DDOS/other attack vectors, it would be good to set a rate limit on the endpoint or module level

Describe the solution you'd like
What would be nice is if I could do something like the following:
For modules:

@LitModule({
    rateLimit: {
        windowMs: 15 * 60 * 1000, // 15 minutes
        max: 100 // limit each IP to 100 requests per windowMs
    },
    exports: [
       ...
    ],
    imports: [
        ...
    ]
})
export class AppModule {

}

For components, maybe something like:

@LitComponent({
    rateLimit: {
        windowMs: 15 * 60 * 1000, // 15 minutes
        max: 100 // limit each IP to 100 requests per windowMs
   }
})
export class AppComponent {
    @GetMapping({
        produces: AppConstants.MESSAGE_V1,
        rateLimit: {
              windowMs: 15 * 60 * 1000, // 15 minutes
              max: 100 // limit each IP to 100 requests per windowMs
        }
    })
    home(res: HttpResponse) {
        res.success({
            message: AppConstants.WELCOME_MESSAGE
        });
    }
}

Describe alternatives you've considered
Alternative would be to add a middleware to the application or endpoint handling the rate limiting

Support "forRoot" global providers, auto-inject

Main use case for this is so the consumer can do:

@LitService()
class StorageService {

    constructor() {
        // .. do this once
        this.connect();
    }

    update() {
        // .. do something many times here
    }

    private connect() {
        // .. do something once here
    }
}

Currently, injecting the above service would give you a new instance of StorageService each time the component is new'd up (once per component route).

Could consider doing something like:

@LitModule({
    providers: [
        StorageService
    ]
})
export class CoreServicesModule {

}

Notice we don't have to make a custom forRoot() method like in Angular

And then in app.module:

@LitModule({
    imports: [
        CoreServicesModule
    ]
})
export class AppModule {

}

Now when you inject it in a component, it will have only constructed once.

This will require some changes to the Injector.resolve method, as well as the compiler.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.