JSPM

  • Created
  • Published
  • Downloads 48
  • Score
    100M100P100Q54075F
  • License MIT

A Typescript HTTP server wrapper with express.

Package Exports

  • @alkocats/http-ts

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@alkocats/http-ts) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

http-ts

Build
Status Maintainability Test Coverage npm version MIT License Known Vulnerabilities dependencies Status devDependencies Status

Installation

npm install @alkocats/http-ts

Usage

Example

All relevant imports for a minimal setup:

import { HttpGet, HttpServer, HttpForbiddenError, SimpleRepository, HttpController, HttpResponse, HTTP_STATUS } from '@alkocats/http-ts';

The user interface for the user repository:

interface User {
    name: string;
    password: string;
}

The user repository for the stored data the controller uses:

class UserRepository extends SimpleRepository<User[]> {
    public async getAsyncData(): Promise<User[]> {
        return this.data;
    }

    public async addUser(name: string, password: string): Promise<void> {
        this.data.push({
            name: name,
            password: password
        });
    }
}

The user controller, which handles the user requests, equipped with different GET-methods:

class UserController extends HttpController<UserRepository> {
    /**
     * Define a GET-method for the url /users.
     */
    @HttpGet('/users')
    public getUsers(): User[] {
        return this.repository.getData();
    }

    /**
     * Define a asynchronous GET-method for the url /async-users.
     */
    @HttpGet('/async-users')
    public async getAsyncUsers(): Promise<User[]> {
        return await this.repository.getAsyncData();
    }

    /**
     * Define a asynchronous GET-method for the url /async-users-with-http-response and a custom http code
     */
    @HttpGet('/async-users-with-http-response')
    public async getAsyncUsersWithHttpResponse(): Promise<HttpResponse> {
        const data = await this.repository.getAsyncData();

        return new HttpResponse(data, HTTP_STATUS.CODE_202_ACCEPTED);
    }

    /**
     * Define a asynchronous POST-method for the url /async-post-data which handles JSON bodies
     */
    @HttpPost('/post-data/:username')
    public async postAsyncData(options: HttpRequestOptions): Promise<User[]> {
        const name = options.request.params.username;
        const password = options.request.body.password;
    
        this.repository.addUser(name, password);

        return this.repository.getAsyncData();
    }

    /**
     * Define a GET-method for the url /faulty-users which throws a HTTP error.
     * None HTTP errors are automatically transformed to HTTP 500 error.
     */
    @HttpGet('/faulty-method')
    public faultyMethod(): HttpResponse {
        throw new HttpForbiddenError();
    }
}

Bringing it all together:

const userRepository = new UserRepository([{
    name: 'admin',
    password: 'the-cake-is-a-lie'
}]);
const userController = new UserController(userRepository);

const httpServer = new HttpServer();
httpServer.registerController(userController);
httpServer.start();

Basic JWT / bcrypt Authentication

All relevant imports for a minimal setup:

import * as bcrypt from 'bcrypt';
import { Authenticated, HttpController, HttpGet, HttpServer, JwtAuthenticator, SimpleRepository } from '@alkocats/http-ts';

The user interface for the user repository:

interface User {
    email: string;
    password: string;
}

The data interface for the data repository:

interface Data {
    foo: number;
    bar: string;
}

The data repository for the stored data the controller uses:

class DataRepository extends SimpleRepository<Data[]> {
    public getSecretData(): Data[] {
        return [{
            foo: 0,
            bar: 'top secret'
        }];
    }
}

const dataRepository = new DataRepository([{
    foo: 1,
    bar: 'no secret data'
}]);

The data controller, which handles all data requests.

class DataController extends HttpController<DataRepository> {
    /**
     * A unauthaenticated get method which can be called by everyone
     */
    @HttpGet('/data')
    public getDataUnauthenticated() {
        return this.repository.getData();
    }

    /**
     * An authenticated get method, which can only be called by authenticated
     * users who deliver a valid bearer token.
     */
    @Authenticated()
    @HttpGet('/data-authenticated')
    public getDataAuthenticated(): Data[] {
        return this.repository.getSecretData();
    }
}

const dataController = new DataController(dataRepository);

Bringing it all together:

async function main() {
    // To login with a valid password, the password needs to be hashed with bcrypt
    const hashedPassword = await bcrypt.hash('the-cake-is-a-lie', 10);

    // This repository is used by the JwtAuthenticator and contains all valid logins.
    const userRepository = new SimpleRepository<User[]>([{
        email: 'alkocats.info@gmail.com',
        password: hashedPassword
    }]);

    // The secret for JwtAuthenticator to use for encryption / decryption.
    const secret = 'some-secret';

    /**
     * The path '/auth' defines, where to make a post with email / password
     * as a json object to obtain a valid bearer token.
     * The token then can be used to access @Authenticated methods of the
     * registered controllers.
     */
    const authenticator = new JwtAuthenticator<User>('/auth', {
        // Defines the repository with all the valid logins
        repository: userRepository,
        // Defines, which field of the repository data is used for identification
        identificationKey: 'email',
        // Defines, in which field of the repository data the hashed password is stored
        passwordKey: 'password',
        // Defines the expiration time of generated tokens (still valid after restart of server)
        expiresIn: 86400,
        // Defines the secret JWT uses to encrypt / decrypt the generated tokens.
        secret: secret
    });

    const httpServer = new HttpServer(80, authenticator);

    httpServer.registerController(dataController);
    httpServer.start();
}

main();

After everything is implemented, a POST to http://localhost/auth with the JSON data

{
    "email": "alkocats.info@gmail.com",
    "password": "the-cake-is-a-lie"
}

generates a bearer token, wich can be used in future requests to access @Authenticated methods of the registered controllers.

Alternatively a custom Authenticater can be created by creating a class which extends Authenticator.

API

Supported HTTP Methods

@HttpGet(path: string)

@HttpPut(path: string)

@HttpPost(path: string)

@HttpDelete(path: string)

@HttpPatch(path: string)

@HttpHead(path: string)

@HttpTrace(path: string)

@HttpConnect(path: string)

Contributing

Feel free to create branches or pull requests.