1.0.1 • Published 5 months ago

elysia-protobuf v1.0.1

Weekly downloads
-
License
MIT
Repository
-
Last release
5 months ago

elysia-protobuf

Easy support protobuf integration for Elysia. To decode/encode we use @bufbuild/protobuf lib and schemas generated by ts-proto

Install

bun install elysia-protobuf

Before starting

Lib is incompatible with default elysia body/response validation! Don't mix it with parse: "protobuf"!

Usage

✅ Do: Use requestSchema field and import decode from context

import Elysia from "elysia";
import {
  protobuf,
  ProtoRequestError,
  ProtoResponseError,
} from "elysia-protobuf";
import {
  RequestMessage,
  ResponseMessage,
  ResponseStatus,
} from "./proto/message";

const app = new Elysia()
  .use(
    protobuf({
      schemas: {
        "post.request": RequestMessage,
        "post.response": ResponseMessage,
      },
      // (optional) verify body with signature
      signature: {
        enabled: true,
        secret: "test123",
        headerName: "x-signature",
      },
    }),
  )
  .post(
    "/post",
    async ({ body, decode, headers }) => {
      // decode uint8array with your schema
      const data = await decode("post.request", body, headers);
      console.log(data);
      return {
        status: ResponseStatus.SOME,
        inlineTags: data.tags.join(", "),
      };
    },
    {
      // parse body as arrayBuffer -> Uint8Array
      parse: "protobuf",
      // encode response with protobuf schema
      responseSchema: "post.response",
    },
  )
  .listen(3000);

❌ Don't: Use default body/response elysia validation with protobuf parser

// ...
const app = new Elysia()
  .use(
    protobuf({
      schemas: {
        "post.request": RequestMessage,
        "post.response": ResponseMessage,
      },
    }),
  )
  .post(
    "/post",
    async ({ body, decode }) => {
      // decode uint8array with your schema
      const data = await decode("post.request", body);
      console.log(data);
      return {
        status: ResponseStatus.SOME,
        inlineTags: data.tags.join(", "),
      };
    },
    {
      parse: "protobuf",
      responseSchema: "post.response",
      // ! ❌ INCOMPATIBLE with this plugin
      //   body: t.Object({
      //     title: t.String(),
      //     updatedAt: t.Optional(t.Number()),
      //     tags: t.Array(t.String()),
      //   }),
      // Doubtful But Okay
      // body: t.Uint8Array(),
    },
  )
  .post(
    "/json",
    ({ body }) => {
      return body;
    },
    {
      // OK if parse mode isn't protobuf
      body: t.Object({
        title: t.String(),
        updatedAt: t.Optional(t.Number()),
        tags: t.Array(t.String()),
      }),
    },
  )
  .listen(3000);

You can handle plugin errors with onError event

import { protobuf, ProtoRequestError, ProtoResponseError } from "../../src";
// ...

const app = new Elysia()
  .use(
    protobuf({
      schemas: {
        "post.request": RequestMessage,
        "post.response": ResponseMessage,
      },
    }),
  )
  .error({
    PROTO_RESPONSE_ERROR: ProtoResponseError,
    PROTO_REQUEST_ERROR: ProtoRequestError,
  })
  .onError(({ code, error, set }) => {
    // something like that
    switch (code) {
      case "PROTO_REQUEST_ERROR": {
        set.status = 400;
        break;
      }
      case "PROTO_RESPONSE_ERROR": {
        set.status = 500;
        break;
      }
    }

    return {
      message: (error as Error).message,
    };
  });
// ...

Create protobuf schema:

  1. Install protoc
  2. Install ts-proto package
  3. Convert .proto to .ts with ts-proto (see example for details):
protoc --plugin=.\\node_modules\\.bin\\protoc-gen-ts_proto --ts_proto_opt=esModuleInterop=true --ts_proto_opt=importSuffix=.js --ts_proto_out=./src ./proto/*.proto
  1. Import schemas from ./src/proto/YOUR_FILE.ts

Options

KeyTypeDefaultDescription
schemasSchemas{}key - proto schema
signatureSignatureundefinedsignature settings
new Elysia().use(
  protobuf({
    schemas: {
      // any string key: proto schema
      "post.request": RequestMessage,
      "post.response": ResponseMessage,
    },
    signature: {
      // disabled by default
      enabled: true,
      secret: "changeme",
      headerName: "x-signature",
    },
  }),
);
1.0.1

5 months ago

1.0.0

5 months ago