Package Exports
- @vladbasin/strong-api-mapping
- @vladbasin/strong-api-mapping/dist/index.js
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 (@vladbasin/strong-api-mapping) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Strong API mapping
Strongly typed API models. Mapping & validation. Use meaningful decorators. Don't repeat yourself.
Installation
npm
npm install reflect-metadata @vladbasin/strong-api-mapping
yarn
yarn add reflect-metadata @vladbasin/strong-api-mapping
Usage
This is a generic mapping library. You can integrate it with any HTTP stack. Currently the following packages use this library:
- @vladbasin/strong-api-middleware - generic middleware library which can integrate with any HTTP stack, by introducing additional layers of request/response pipeline and with help of this library can support any HTTP stack.
- @vladbasin/strong-api-middleware-aws-lambda - complete e2e HTTP pipeline integration for AWS Lambda
- @vladbasin/strong-api-middleware-express - IN PROGRESS - complete e2e HTTP pipeline integration for express server
- @vladbasin/strong-api-client - HTTP client which can use shared models to execute request. Therefore, no need to duplicate mapping & validation logic for API consumers & producers.
Step-by-step guide
- Import
reflect-metadataONCE in your index file:
import 'reflect-metadata';- Define your model
import { body, header, path, query } from '@vladbasin/strong-api-mapping';
export class RequestPayload {
@path()
public userId!: number;
@path({ key: 'userId' })
public id!: number;
@query()
public name!: string;
@query()
public isAdmin!: boolean;
@query({ key: 'lastname' })
public surname!: string;
@query({ parser: String })
public cars!: string[];
@query({ parser: Number })
public cash!: number[];
@body()
public details!: DetailsType;
@header({ key: 'Content-Type' })
public contentType!: string;
@header({ key: 'X-Info', parser: String })
public info!: string[];
}- Define validation rules with
Joi
export const RequestPayloadSchema = Joi.object<RequestPayload>({
surname: Joi.string().min(10),
cars: Joi.array().max(3),
// other rules for field content...
});- Prepare
RawApiRequestfor mapping. For example, @vladbasin/strong-api-middleware-aws-lambda already does it for you for AWS Lambda. But you can create your own middleware for your stack and use this library to do mapping & validation for you.
// represents Query, Header, Route and Body values for HTTP request
export type RawApiRequestType = {
queryParams?: MaybeNullable<Record<string, Maybe<string>>>;
multiValueQueryParams?: MaybeNullable<Record<string, Maybe<string[]>>>;
pathParams?: MaybeNullable<Record<string, Maybe<string>>>;
headers?: MaybeNullable<Record<string, Maybe<string>>>;
multiValueHeaders?: MaybeNullable<Record<string, Maybe<string[]>>>;
body?: MaybeNullable<string>;
};- Call the following methods to map HTTP request to your model and vice versa with DRY principle
// maps RawApiRequest to Model (RequestPayload) and validates it (throws `CodedError` with information if model is not valid)
mapRawApiRequestToPayload<RequestPayload>({
rawApiRequest,
PayloadConstructor: RequestPayload,
schema: RequestPayloadSchema,
});
// maps Model (RequestPayload) to RawApiRequest
mapPayloadToRawApiRequest(requestPayload);Also applicable to response models (mapRawApiResponseToPayload(), mapPayloadToRawApiResponse()).
In case validation fails, the library throws CodedError instance with information which properties are not valid:
{
message: 'ValidationError: "surname" is required'
code: 'ValidationFailed',
errors: [ { code: 'surname', message: 'any.required' } ],
}Thus, you can share request/response models with your API consumers, so they don't need to repeat the same mapping & validation logic. See: @vladbasin/strong-api-client
Custom decorator/mapping
You can also specify custom mappings for custom decorator:
- Define custom decorator
import { defineDecorator, ParserType } from '@vladbasin/strong-api-mapping';
export const context = (options: { key?: string; parser?: ParserType }): PropertyDecorator =>
defineDecorator({
source: 'context',
useKey: true,
isKeyCaseSensitive: false,
key: options.key,
parser: options.parser,
isCustom: true,
});- Specify data for custom context
mapRawApiRequestToPayload<RequestPayload>({
rawApiRequest,
PayloadConstructor: RequestPayload,
schema: RequestPayloadSchema,
customApiRequestData: {
context: {
customKey: 'customValue',
//...
},
},
});