{"id":11262,"library":"mali","title":"Mali: Minimalistic gRPC Framework","description":"Mali is a minimalistic, middleware-based gRPC microservice framework for Node.js, designed to simplify the creation of gRPC servers. It operates similarly to popular HTTP frameworks like Koa or Express, leveraging a context object (`ctx`) for request and response handling within a cascading middleware pattern. The current stable version is `0.47.1`. The project maintains a regular, albeit not rapid, release cadence, primarily focusing on dependency updates, performance improvements, and bug fixes, as seen in recent versions like `v0.47.0` and `v0.46.0`. Its key differentiator lies in its commitment to minimalism and a familiar middleware API for gRPC, abstracting away some of the complexities of the underlying `@grpc/grpc-js` and `@grpc/proto-loader` libraries, which are required as peer dependencies. It also ships with TypeScript types.","status":"active","version":"0.47.1","language":"javascript","source_language":"en","source_url":"https://github.com/malijs/mali","tags":["javascript","mali","grpc","service","server","microservice","typescript"],"install":[{"cmd":"npm install mali","lang":"bash","label":"npm"},{"cmd":"yarn add mali","lang":"bash","label":"yarn"},{"cmd":"pnpm add mali","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Required gRPC implementation; peer dependency.","package":"@grpc/grpc-js","optional":false},{"reason":"Required for loading Protocol Buffer definitions; peer dependency.","package":"@grpc/proto-loader","optional":false}],"imports":[{"note":"CommonJS `require` is typical for older Node.js projects, but ESM `import` is recommended for modern TypeScript/ESM projects.","wrong":"const Mali = require('mali');","symbol":"Mali","correct":"import { Mali } from 'mali';"},{"note":"Importing the `Context` type for type-safe middleware definitions in TypeScript.","symbol":"Context","correct":"import type { Context } from 'mali';"},{"note":"The main application class can also be imported as `App` for clarity in some contexts, though `Mali` is the primary export.","symbol":"App","correct":"import { App } from 'mali';"}],"quickstart":{"code":"const Mali = require('mali');\nconst path = require('path');\nconst fs = require('fs');\n\n// A minimalistic gRPC service definition\nconst PROTO_CONTENT = `\n  syntax = \"proto3\";\n\n  package helloworld;\n\n  service Greeter {\n    rpc SayHello (HelloRequest) returns (HelloReply) {}\n  }\n\n  message HelloRequest {\n    string name = 1;\n  }\n\n  message HelloReply {\n    string message = 1;\n  }\n`;\n\n// Write the proto content to a temporary file for Mali to load\nconst PROTO_PATH = path.resolve(__dirname, 'helloworld.proto');\nfs.writeFileSync(PROTO_PATH, PROTO_CONTENT);\n\nfunction sayHello (ctx) {\n  ctx.res = { message: `Hello ${ctx.req.name || 'World'}` };\n  console.log(`Handled SayHello for ${ctx.req.name || 'World'}`);\n}\n\nasync function main () {\n  const app = new Mali(PROTO_PATH);\n  app.use({ sayHello });\n  const port = '0.0.0.0:50051';\n  app.start(port);\n  console.log(`gRPC server running on ${port}`);\n\n  // Basic client to test the server (optional, for demonstration)\n  const grpc = require('@grpc/grpc-js');\n  const protoLoader = require('@grpc/proto-loader');\n  const packageDefinition = protoLoader.loadSync(PROTO_PATH, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true });\n  const hello_proto = grpc.loadPackageDefinition(packageDefinition).helloworld;\n  const client = new hello_proto.Greeter(port, grpc.credentials.createInsecure());\n\n  client.sayHello({ name: 'Mali User' }, (err, response) => {\n    if (err) {\n      console.error('Client error:', err);\n    } else {\n      console.log('Client received:', response.message);\n    }\n    // Clean up temporary proto file and stop the server for a real test\n    fs.unlinkSync(PROTO_PATH);\n    app.shutdown();\n  });\n}\n\nmain().catch(console.error);\n","lang":"javascript","description":"This quickstart demonstrates setting up a basic Mali gRPC server, defining a simple 'Greeter' service with a 'SayHello' method, and then creating a client to interact with it. It includes the necessary proto definition and server startup/shutdown."},"warnings":[{"fix":"Upgrade Node.js to version 16 or newer. The project recommends Node.js 18+ for best compatibility and features.","message":"Mali `v0.47.0` officially dropped support for Node.js 14. Applications running on Node.js 14 might encounter issues or become unsupported.","severity":"breaking","affected_versions":">=0.47.0"},{"fix":"Ensure direct dependencies are explicitly installed if your project relied on them. Review your package's dependency tree for unexpected removals.","message":"In `v0.44.0`, several internal dependencies (`grpc-create-metadata`, `lodash.concat`, `lodash.forown`, `lodash.isplainobject`, `ascallback`) were removed. While primarily an internal cleanup for performance, if your project indirectly relied on these being available via Mali's dependency tree, it could cause runtime errors.","severity":"breaking","affected_versions":">=0.44.0"},{"fix":"Upgrade to `v0.44.3` or newer to get the corrected TypeScript declarations.","message":"Older versions of Mali (`<0.44.3`) had an invalid TypeScript declaration for `Mali.addService`. This could lead to compilation errors or incorrect type inference when using `addService` in TypeScript projects.","severity":"gotcha","affected_versions":"<0.44.3"},{"fix":"Upgrade to `v0.44.2` or newer to resolve the namespace omission bug. Review your `.proto` files if they use `type` as a namespace key and test thoroughly.","message":"Versions prior to `v0.44.2` contained a bug that caused namespaces with the key `type` to be omitted from the gRPC service definition. This could lead to missing services or methods if your proto schema used `type` as a namespace key.","severity":"gotcha","affected_versions":"<0.44.2"},{"fix":"Upgrade to `v0.47.1` or newer to ensure response metadata is always sent, even when an error occurs during processing.","message":"When sending response metadata, it was not always sent if an error occurred in versions prior to `v0.47.1`. This could cause clients to not receive expected metadata alongside error responses.","severity":"gotcha","affected_versions":"<0.47.1"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Upgrade `mali` to `v0.44.3` or newer. Ensure correct method signature according to documentation if not using TypeScript.","cause":"Using `Mali.addService` in TypeScript with an older version that had incorrect type declarations, leading to compiler issues or runtime misinterpretations.","error":"TypeError: Mali.addService is not a function"},{"fix":"Run `npm install mali @grpc/grpc-js @grpc/proto-loader` to ensure all necessary peer dependencies are installed.","cause":"Mali lists `@grpc/grpc-js` and `@grpc/proto-loader` as peer dependencies, meaning they must be installed explicitly alongside `mali`.","error":"Error: Cannot find module '@grpc/grpc-js' or '@grpc/proto-loader'"},{"fix":"Verify the server is running on the specified port and address. Check firewall rules. Ensure the client connection string matches the server's listening address.","cause":"The gRPC client failed to connect to the server. This could be due to the server not running, incorrect address/port, firewall issues, or network problems.","error":"Error: 14 UNAVAILABLE: No connection established"}],"ecosystem":"npm"}