@selfage/service_descriptor

Descriptor for web/http services.

Usage no npm install needed!

<script type="module">
  import selfageServiceDescriptor from 'https://cdn.skypack.dev/@selfage/service_descriptor';
</script>

README

@selfage/service_descriptor

Install

npm install @selfage/service_descriptor

Overview

Written in TypeScript and compiled to ES6 with inline source map & source. See @selfage/tsconfig for full compiler options. Provides a TypeScript interface ServiceDescriptor to be referenced when implementing @selfage/service_handler or when calling services via @selfage/service_client. A ServiceDescriptor is usually generated by @selfage/cli.

Generate ServiceDescriptor

With @selfage/cli, it requires an input file, e.g., service.json which looks like the following.

[{
  "message": {
    "name": "GetCommentsRequest"
    ...
  }
}, {
  "message": {
    "name": "GetCommentsResponse"
    ...
  }
}, {
  "service": {
    "name": "GetComments",
    "path": "/get_comments",
    "request": "GetCommentsRequest",
    "response": "GetCommentsResponse"
  }
}, {
  "service": {
    "name": "GetHistory",
    "path": "/get_history",
    "request": "GetHistoryRequest",
    "importRequest": "./request_def",
    "response": "GetHistoryResponse",
    "importResponse": "./response_def"
  }
}]

By running selfage gen service, it will generate service.ts file, which looks like the following.

import { MessageDescriptor, PrimitiveType } from '@selfage/message/descriptor';
import { UnauthedServiceDescriptor, AuthedServiceDescriptor } from '@selfage/service_descriptor';
import { GetHistoryRequest, GET_HISTORY_REQUEST } from './request_def';
import { GetHistoryResponse, GET_HISTORY_RESPONSE } from './response_def';

// ... Generated messages for GetCommentsRequest and GetCommentsResponse.

export let GET_COMMENTS: UnauthedServiceDescriptor<GetCommentsRequest, GetCommentsResponse> = {
  name: "GetComments",
  path: "/get_comments",
  requestDescriptor: GET_COMMENTS_REQUEST,
  responseDescriptor: GET_COMMENTS_RESPONSE,
};

export let GET_HISTORY: AuthedServiceDescriptor<GetHistoryRequest, GetHistoryResponse> = {
  name: "GetHistory",
  path: "/get_history",
  requestDescriptor: GET_HISTORY_REQUEST,
  responseDescriptor: GET_HISTORY_RESPONSE,
};

It's recommended to commit service.ts as part of your source code.

The schema of the json file is an array of definition. See @selfage/message for explanation of generating messages. request and response each refers to a message, and each can be imported following Nodejs's module path resolution.

When request contains a field named as signedSession, that service is then treated as an authed service. If not, an unauthed service. E.g., request_def.json might look like the following.

[{
  "message": {
    "name": "GetHistoryRequest",
    "fields": [{
      "name": "signedSession",
      "type": "string"
    }, {
      "name": "userId",
      "type": "string"
    }]
  }
}]

You usually don't need to explicitly set signedSession field or read it. See @selfage/service_client for how it is set. See @selfage/service_handler for it was generated, read and parsed, where a definition of session needs to be provided.

Design considerations

path is not generated by name because you would need them when debugging. Therefore explicitly specifying it can save anyone from understanding the rule to generate it.

signedSession field for authed request is probably an unusual decision when most frameworks use HTTP headers to implicitly pass them. The downside seems to be that it needs to be repeated for each authed request, but in fact, one has to specify a service is authed in one way or another.