Includes:
- Server Ready-to-run
- Dependency Injection via typedi
- Authentication
- bcrypt
- jwt
- Register and Sign In Endpoints
- Config Service with process.env
- Logging Service
- Exception & Error Handling Framework
- Single-location Route Registration
/hello/hello.api.ts
/server/route-registry.ts
1. create your class and/or function that will accept the http request. The method definition must accept express request and response objects.
import { BaseApi } from '../path/to/base.api';
import * as Express from 'express';
import { Service } from 'typedi';
@Service()
export class SomeApi extends BaseApi {
constructor(private _someClassThatDoesSomething: SomeClassIWantToInject) {
super();
}
public doSomething(req: Express.Request, res: Express.Response): void {
let successMessage: string = this._someClassThatDoesSomething.doTheThing();
res.status(200).send({message: successMessage});
}
public doSomethingForAccount(req: Express.Request, res: Express.Response): void {
try {
// The accountId is placed on the request body by the auth handler, registered in the express server. Don't pass accountId directly
let accountId: string = req.body.accountId;
let successMessage: string = this._someClassThatDoesSomething.doTheThingForTheAccount(accountId);
res.status(200).send({message: successMessage});
}
catch (err) {
// In BaseApi
this.sendErrorResponse(err, res);
}
}
}
Note: Using @Service()
on the class allows for your class to have injected classes using typedi
. Full Documentation on typedi.
If you use constructor injection in the api/controller classes and you register the apis in the route-registery as documented below, the dependency injection is taken care of for you.
2. Register your new controller and/or function with the /server/route-registery.ts
file. If it should be an authenticated endpoint, add it to the auth object, otherwise, add it to the nonAuth object
import {SomeApi} from '../path/to/api.ts'
...
constructor(private _someApi: SomeApi) {}
this.routes.nonAuth.post['/do/something'] = (req, res, next) => this._someApi.doSomething(req, res);
this.routes.auth.post['/do/something/for/me'] = (req, res, next) => this._someApi.doSomethingForAccount(req, res);
- Defining the api/controller as an argument in the constructor, automatically creates an instance using dependency injection
- The registration pattern: so the api/controller classes do not need to be static.
When supplying information in the request body, the Content-Type header is required
Content-Type: application/json
npm i
npm run build
npm run start
npm run build
npm start
You will need to rebuild and restart the api after making changes. To make this quicker:
Ctrl + c, Ctrl + c
npm run retry
This will stop the API process, run the quick-build command (only what is necessary to rebuild the files) and start the server again
The config service and supporting classes are in src/framework/config
.
This folder contains:
config.service.ts
: the instantiable class that should be used to get configsconfig-store.ts
: the variable that holds all config valuesconfig.interface.ts
: the file that holds all the interfaces defining the configs
Make sure to set your own values.
Authentication is configured in the src/framework/auth/jwt.service.ts
file. There is a basic JWT implemented here; it currently contains the following public claims:
- iss
- iat
- exp
- sub
It encrypts based on sha512 and uses the ConfigService to retrieve settings for:
- the secret key
- the number of days the token is valid
- the issuer (iss) of your token
If you would like to implement your own scheme you may adjust this file.
The logging service is in src/framework/logging
There is a basic, non-blocking implementation that logs to the console.
If you would like to log to a 3rd party, add the functionality in the logging.service.ts
file. The service makes use of the ConfigService and imports the error tracking configs. Modify the IErrorTrackingConfig interface and ConfigStore in the config folder as needed.
There is a set of custom API exceptions in the src/framework/exceptions/exceptions.ts
file. These include:
- Unauthorized Exception
- Forbidden Exception
- NotFound Exception
- Conflict Exception
- Validation Exception
These custom exceptions make use of BaseApiException. Everything extending from BaseApiException is explicitly handled by the ResponseUtils which is implemented by the BaseApi abstract class. This allows you to throw a handled exception all the way out to the api/controller, and it will take care of creating the error response. If an error that does not extend from BaseApiException is recieved, it will assume it was unhandled, assign a 500 to the response and log the error using the LoggingService.