Creating NPM package from protobuf TypeScript compilation

I want to build an NPM package that can be reused in Angular 18+ projects. The package should be based on .proto files. My goal is to install it using npm install @company/proto-lib and then import the generated classes in my Angular components.

Here’s my proto definition:

message Employee {
  string full_name = 1;
  int years_old = 2;
}

What I want to achieve in my Angular component:

import { Employee } from "@company/proto-lib";

workerData: Employee = new Employee();

workerData.SetFullName("Luke");
workerData.SetYearsOld(25);

I’m using this command to generate the files:

.\node_modules\.bin\protoc.cmd "--proto_path=.\src\protos" "--plugin=protoc-gen-ts=.\node_modules\.bin\protoc-gen-ts.cmd" "--ts_out=service=grpc-web:.\output" "--js_out=import_style=commonjs,binary:.\output" ".\src\protos\Employee.proto"

This creates Employee_pb.d.ts and Employee_pb.js files in my output directory.

My package.json looks like this:

{
  "name": "@company/proto-lib",
  "version": "1.0.0",
  "main": "output/Employee_pb.js",
  "files": [
    "output/",
    "output/Employee_pb.js",
    "output/Employee_pb.d.ts"
  ],
  "type": "module",
  "dependencies": {
    "@improbable-eng/grpc-web": "^0.15.0",
    "@protobuf-ts/protoc": "2.9.4",
    "ts-protoc-gen": "0.15.0"
  }
}

The problem is that after publishing with npm pack and npm publish, when I install the package and try to use it, the imported class shows as undefined in the console. However, TypeScript autocomplete works fine and the types are recognized correctly.

When I test the same files locally (not through NPM), everything works as expected. I think there might be an issue with how webpack handles the module resolution for the packaged files.

Any ideas on how to fix this module resolution issue?

You’re mixing ES modules with CommonJS output. Your package.json has “type”: “module” but protoc generates CommonJS files with import_style=commonjs. Webpack can’t handle this mismatch properly. I hit the same issue last year with our internal proto package. Remove “type”: “module” from package.json since your generated files are CommonJS. Also, don’t point the main field directly at Employee_pb.js. Create an index.js file that re-exports your generated classes, then update package.json to point at that index file instead. This fixed our undefined import issues at runtime and kept TypeScript support working.

Check if your Employee_pb.js file is actually exporting the Employee class. That undefined error usually means the module export got messed up during compilation. Try adding an explicit export statement at the end of the generated file, or tweak your ts_out settings. I ran into the same thing with protobuf - switching my protoc command to --ts_out=service=grpc-web,mode=grpc-js:./output fixed the export problems. Also double-check your tsconfig.json module resolution settings. Angular’s strict resolution sometimes clashes with protobuf modules.

had the same issue with our grpc setup. switch your --js_out flag to import_style=es6 instead of commonjs - that fixed my undefined imports when publishing proto packages. also double-check that your angular project’s tsconfig has "moduleResolution": "node" configured correctly.

your protoc command is generating wrong method names. the generated classes don’t have SetFullName() or SetYearsOld() methods by default - they usually have setFullName() (camelCase) or direct property access. check what methods are actually available in your Employee_pb.js file first. probably just a naming issue causing the undefined behavior.

This happens because webpack can’t properly resolve packaged protobuf files. I’ve hit this exact issue distributing proto libraries across Angular projects. Your generated files need proper bundling config to work as NPM packages. Make sure your package.json has “main” and “types” fields pointing to the right files. Add a “module” field for ES module support and set “sideEffects”: false for tree shaking. The real fix is your build process. After protoc generates files, bundle them properly for distribution. Use rollup or webpack to create a single bundle that exports all proto classes cleanly. This kills the module resolution conflicts causing runtime undefined issues but keeps TypeScript definitions intact. Works reliably across different Angular versions and webpack configs.

The Problem:

You’re experiencing difficulties packaging your Protobuf-based NPM package for use in Angular projects. After publishing, the imported classes are undefined at runtime, despite TypeScript correctly recognizing the types. This issue only surfaces after publishing the package via npm; local testing works as expected. The root cause appears to be related to how webpack handles module resolution for the packaged files.

:thinking: Understanding the “Why” (The Root Cause):

The problem stems from inconsistencies in how your Protobuf-generated code is handled during the build and packaging process, specifically in the context of Webpack used by Angular. Directly including the generated Employee_pb.js file in your package.json’s main field, without proper bundling or consideration of CommonJS vs. ES modules, leads to module resolution conflicts at runtime. Webpack, and other bundlers, struggle to correctly resolve the dependencies within your Protobuf-generated code when it is not packaged in a way that is compatible with its module system.

:gear: Step-by-Step Guide:

  1. Embrace a Simplified Build Process: The most effective solution is to eliminate direct reliance on the Protobuf compiler (protoc) output within your package’s structure. Instead, use a bundler (like Webpack or Rollup) as part of your build process to generate a single, optimized output file. This will properly resolve and bundle all the necessary Protobuf-generated classes for your Angular project. This approach addresses the underlying conflict with webpack’s module resolution capabilities.

  2. Configure your Build Process (e.g., using Webpack): Assume you are using Webpack (adapt as needed for Rollup or other bundlers). Create a Webpack configuration that includes the Protobuf-generated files as entry points. The configuration should bundle these files, resolving their internal dependencies, and output a single .js file (and a .d.ts file for TypeScript definitions) that is suitable for your npm package. This bundle would contain your Employee class and any related classes, pre-resolved and ready for import into your Angular applications.

  3. Update package.json: Once the build process is in place, update your package.json file accordingly:

    {
      "name": "@company/proto-lib",
      "version": "1.0.0",
      "main": "dist/proto-lib.js", // Path to your bundled output
      "types": "dist/proto-lib.d.ts", // Path to your bundled type definitions
      "files": ["dist/"],  // Only include the bundled output files
      "module": "dist/proto-lib.es.js", // Optional: ES module version for modern bundlers
      "sideEffects": false, // Enable tree shaking
      "dependencies": {
        "@improbable-eng/grpc-web": "^0.15.0",
        "@protobuf-ts/protoc": "2.9.4",
        "ts-protoc-gen": "0.15.0"
      }
    }
    
  4. Rebuild and Publish: Run your new build process and generate the output files. Afterwards, publish your updated package to NPM. The new bundle should resolve the runtime undefined issues.

:mag: Common Pitfalls & What to Check Next:

  • Incorrect Bundler Configuration: Carefully review your Webpack (or Rollup) configuration to ensure all Protobuf-generated files are correctly included and bundled. Double check the output paths in your package.json.
  • TypeScript Configuration: Ensure that your tsconfig.json file is correctly configured for module resolution (moduleResolution, module).
  • ES Module vs. CommonJS: Consider whether your project requires both ES modules and CommonJS output and configure your bundler accordingly. The above example illustrates this with both "main" and "module" fields.
  • Caching: Clear npm and browser caches to prevent old files from affecting the results.

:speech_balloon: Still running into issues? Share your (sanitized) config files, the exact command you ran, and any other relevant details. The community is here to help!

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.