New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@pawsteam/ts-jsonrpc-server

Package Overview
Dependencies
Maintainers
3
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@pawsteam/ts-jsonrpc-server - npm Package Compare versions

Comparing version 0.2.0 to 0.2.1

2

package.json
{
"name": "@pawsteam/ts-jsonrpc-server",
"version": "0.2.0",
"version": "0.2.1",
"description": "A framework for easily building JSONRPC simple servers in TS",

@@ -5,0 +5,0 @@ "main": "dist/server.js",

@@ -22,3 +22,3 @@ # Typescript JSONRPC server

port: 2000,
services: [SomeService],
services: [HotelsService],
genericValidators: [

@@ -34,3 +34,3 @@ new HeaderExistsValidator('content-type'),

class HotelsService {
listHotels(): Promise<any> {
listHotels(request: IncomingMessage, response: ServerResponse): Promise<any> {
return new Promise((resolve, reject) => {

@@ -44,3 +44,8 @@ // fetch data.then((data) => {

```
Observe how we have an array, `services: [HotelsService]` which uses the class with the same name and listens on the path `/hotels`.
By default, the procedure that gets called receives 2 parameters: a request object of type `IncomingMessage`, default for nodejs HTTP servers, and a response object of type `ServerResponse`, also default for nodejs HTTP servers.
The method you want to export must return a promise for any type of data.
Later, when running this app, you can make a JSONRPC request to the methods exposed by this endpoint:

@@ -79,32 +84,38 @@

They check the request for required data and types. It's a place to define what parameters are needed for a method to be called.
Any number of Input Validators can be assigned for one method. Generic ones as well;
#### Generic validators
Generic validators are set on the app level, and they will be triggered on all requests for this app.
```typescript
@AppConfigDecorator({
port: 2000,
services: [SomeService],
genericValidators: [
new HeaderExistsValidator('content-type'),
new HeaderValueContainsValidator('user-agent', 'Mozilla/5.0')
]
})
export class AppMain {
They check the request for required data, without which you cannot perform your business logic. It's a place to define what parameters are needed for a method to be called.
Any number of Input Validators can be assigned for one method. Generic ones as well;
An input validator must implement the `ValidatorInterface`, which means it must implement the `validate` method, which will receive 2 parameters: `params` which is a JSON object with the params from the JSONRPC object. The second parameter is the whole request object, of type `IncomingMessage`
This `validate` method returns true of false. A truthy value will make the validation pass, a falsy value will make the validation fail.
Passing a validation means the exported method will be called.
Failing a validation means a `400 Bad Request` will be returned.
#### Generic validators
Generic validators are set on the app level, and they will be triggered on all requests for this app.
```typescript
@AppConfigDecorator({
port: 2000,
services: [SomeService],
genericValidators: [
new HeaderExistsValidator('content-type'),
new HeaderValueContainsValidator('user-agent', 'Mozilla/5.0')
]
})
export class AppMain {
}
export class HeaderExistsValidator implements ValidatorInterface {
headerName: string;
constructor(headerName: string) {
this.headerName = headerName;
}
export class HeaderExistsValidator implements ValidatorInterface {
headerName: string;
constructor(headerName: string) {
this.headerName = headerName;
}
validate(params: any, request): boolean {
return !!request.headers[this.headerName];
}
validate(params: any, request: IncomingMessage): boolean {
return !!request.headers[this.headerName];
}
```
The input validators implement ValidatorInterface and return a boolean value.
}
```

@@ -124,42 +135,78 @@ #### Method specific validators

They transform the request data into objects that are passed to the methods. Any number of input transformer can exist per method.
They transform the request data into application-defined objects that are passed to the methods. Any number of input transformers can exist per method.
They must implement the `TransformerInterface`, which means implementing the method `transform` which receives 2 parameters.
First one is the default request, of type `IncomingMessage` and the second one is the parsed JSON from the JSONRPC call, of type `ParsedRpcObjectType`
The transformers return a Promise of object type required.
Examples below:
1. The transformer returns a promise of one parameter type.
2. The order of validators will give the order of parameters in the method call.
3. The transformers implement the TransformerInterface and return a Promise to the type of object they are for.
```
export class BucketTransformer implements TransformerInterface {
/**
* The ParsedRpcObjectType is an object created from the body of the request, parsed,
* and put into the form of a JSONRPC object:
* {
* id: <id>,
* method: <methodname>,
* jsonrpc: '2.0',
* params: {...}
* }
*/
transform(request: IncomingMessage, jsonrpcObj: ParsedRpcObjectType): Promise<BucketType> {
const newBucket = new BucketType(jsonrpcObj.params.path);
return Promise.resolve(newBucket);
}
```typescript
export class BucketTransformer implements TransformerInterface {
/**
* The ParsedRpcObjectType is an object created from the body of the request, parsed,
* and put into the form of a JSONRPC object:
* {
* id: <id>,
* method: <methodname>,
* jsonrpc: '2.0',
* params: {...}
* }
*/
transform(request: IncomingMessage, jsonrpcObj: ParsedRpcObjectType): Promise<BucketType> {
const newBucket = new BucketType(jsonrpcObj.params.path);
return Promise.resolve(newBucket);
}
```
}
```
The reason this method needs to return a promise is because we might need to fetch additional data in order to create the object.
For instance, we might need to query a DB.
The reason this method needs to return a promise is because we might need to fetch additional data in order to create the object.
For instance, we might need to query a DB.
Validation can also be done in the transformer. If the transformer rejects the promise or throws, the transformation is interpreted as failed, so validation failed, returning `400 Bad Request`.
To use a transformer, you simply instantiate a new instance of your transformer:
Validation can also be done in the transformer. If the transformer rejects the promise or throws, the transformation is interpreted as failed, so validation failed.
To use a transformer, you simply instantiate a new instance of your transformer:
```
@TransformerDecorator(new CarTransformer())
@TransformerDecorator(new EngineTransformer())
method3(car: CarType, engine: EngineType, request: IncomingMessage, response: ServerResponse) {
return Promise.resolve(car.wheels * engine.power);
```typescript
class CarType {
wheels: number;
engine: string;
constructor(_wheels: number, _engine: string) {
this.wheels = _wheels;
this.engine = _engine;
}
```
}
class EngineType {
power: number
constructor(_power: number) {
this.power = _power;
}
}
class CarTransformer implements TransformerInterface {
transform(request: IncomingMessage, jsonrpcObj: ParsedRpcObjectType): Promise<any> {
return Promise.resolve(new CarType(4, 'v6'));
}
}
class EngineTransformer implements TransformerInterface {
transform(request: IncomingMessage, jsonrpcObj: ParsedRpcObjectType): Promise<any> {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(new EngineType(260));
}, 500);
})
}
}
@TransformerDecorator(new CarTransformer())
@TransformerDecorator(new EngineTransformer())
method3(car: CarType, engine: EngineType, request: IncomingMessage, response: ServerResponse) {
return Promise.resolve(car.wheels * engine.power);
}
```
Note how we instantiate the 2 classes for transforming, CarTransformer and EngineTransformer, and how the order of the two decorators is kept in the parameters passed to the method.
The first transformer will pass the first parameter, and the second transformer passes the second parameter.
Last 2 parameters remain the request and response.
### The method itself

@@ -169,18 +216,31 @@

It must also return a promise of any type of object, this object will be serialized/processed if needed, then replied to the user
It must also return a promise of any type of object, this object will be serialized/processed if needed, then replied to the user
```
@ValidatorDecorator(new BucketValidator())
@TransformerDecorator(new BucketTransformer())
createBucket(bucket: BucketType, context: RequestContextType): Promise<any> {
return Promise.resolve({created: bucket.path});
}
```
```typescript
@ValidatorDecorator(new BucketValidator())
@TransformerDecorator(new BucketTransformer())
createBucket(bucket: BucketType, context: RequestContextType): Promise<any> {
return Promise.resolve({created: bucket.path});
}
```
As a best practice, the validators should be as light as possible, and the validation should be avoided in the transformers, unless absolutely necessary.
### Response serializer
This is where you can decide what exactly ends up in the response. Everything in the response can be changed at this step.
The serializers must implement **SerializerInterface**.
They must return an object of type JsonRpcResponseType.
The serializers must implement `SerializerInterface`, which means implement the method `serialize`. This method receives 1 parameter of type `SerializerParameterType` which is a JSON containing a lot of information.
The properties of this object are:
```typescript
public objectToBeReturned: JsonRpcResponseType;
public methodOutput: HttpResponse;
public requestJsonRpcBody: ParsedRpcObjectType;
public originalRequest: IncomingMessage;
public service: ServiceType<any>;
```
Basically all of the information that are available in this request are passed through this object.
They must return an object of type JsonRpcResponseType (Which is also passed as a parameter).
```typescript

@@ -203,4 +263,15 @@ // example of serializer - removes all keys that contain the string 'test' from the response.

}
```
```
Expected output:
```json
{
"id": ...,
"jsonrpc": "2.0",
"result": {
"someKey": 16
}
}
```
## Events

@@ -217,5 +288,5 @@

```typescript
// step 1 - define the connection & transport
// for now only Rabbit transporters are allowed
@AppConfigDecorator({
// step 1 - define the connection & transport
// for now only Rabbit transporters are allowed
messaging: {

@@ -233,3 +304,2 @@ serviceName: 'NewService',

```typescript
// step 2 - in the service method, mention the decorator
@ServiceDecorator({

@@ -240,3 +310,4 @@ path: '/newpath',

export class NewService implements ServiceInterface {
EventEmitterDecorator()
// step 2 - in the service method, mention the decorator
@EventEmitterDecorator()
method2() {

@@ -247,3 +318,3 @@ return Promise.resolve({sum: 8, product: 15});

```
By default, this will render a message similar to this
By default, this will produce an event with the full response object as `eventParams`
```typescript

@@ -259,7 +330,7 @@ {

Alternatively, you can also select and change the keys that will be published in the event, by passing a json parameter to the EventEmitterDecorator()
Alternatively, you can also filter the keys that will be published in the event, and even rename them, by passing a json parameter to the EventEmitterDecorator()
```typescript
@EventEmitterDecorator({
sum: '', // an empty string means the key's name will be used in the event keys
sum: '', // an empty string means the key's original name will be used in the event keys
prod: 'product'

@@ -281,3 +352,3 @@ })

Notice how the returned keys are _sum, prod and excluded_, and the event only has _sum and product_.
Notice how the returned keys are `sum`, `prod` and `excluded`, and the event only has `sum` and `product`, where the `product` is actually the returned `prod`.
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc