Supported browsers & frameworks
Connect supports all modern web browsers that implement the widely available fetch API and the Encoding API. The library and the generated code are compatible with ES2017 and TypeScript 4.1.
Connect is entirely framework-agnostic and will even work with vanilla Javascript. It is compatible with commonly-used package managers such as npm, pnpm, and Yarn. It works out of the box with most popular application and testing frameworks including React, Remix, Svelte, Vue, Playwright and Vitest. It also integrates seamlessly with various module loaders and bundlers such as esbuild, Vite and Rollup.
However, there are some gotchas to be aware of as well as common errors that you may encounter when attempting to set up your stack. The issues vary depending on the tooling in your project. This page explains some helpful workarounds with links to examples.
Webpack
If your project is using Webpack and you are generating pure TypeScript artifacts via your buf.gen.yaml
file, you may
see an error similar to the following:
Failed to compile.
Module not found: Error: Can't resolve '<import name>' in 'path/to/your/connect/genfiles'
This is because if you are using opt: target=ts
as part of your plugin configuration, protoc-gen-es
will generate only TypeScript
files. However, these files will use imports internally that specify a .js
extension. This is in accordance with the
ECMAScript specification, but may cause some difficulty loading these files.
Fortunately, some workarounds exist depending on your version of Webpack.
v5.74.0 and above
If you are using Webpack v5.74.0 and above, there is an option
called extensionAlias that you can add to the
resolve
section of your Webpack config file. This will tell Webpack how to alias TypeScript file extensions and load
them properly.
For a working example, see the Webpack project in the examples-es repo.
v5.73.0 and below
If you are using a version < v5.74.0 and are not able to upgrade, then you have three options:
Use the import_extension
option
The code generator plugins used by Connect and its dependency Protobuf-ES will
add a .js
extension to import paths by default. However, you can configure these plugins to use a different value
using the plugin option import_extension
. Visit the docs here for details.
Note that if you do use this approach, you will need to set this option on both the Protobuf-ES plugin and the Connect-ES plugin.
For example, in your buf.gen.yaml
:
version: v2
plugins:
- local: protoc-gen-es
out: gen
opt: import_extension=none
- local: protoc-gen-connect-es
out: gen
opt: import_extension=none
Output JavaScript files
Changing the target setting in your buf.gen.yaml
to opt: target=js+dts
will eliminate the file resolving issue as it
will generate JavaScript files with a .js
extension.
Note that the default generation option is target=js+dts
so omitting this opt
option from your config will also do the trick. For example:
- opt: target=ts
+ opt: target=js+dts
or remove altogether.
- opt: target=ts
Use the Resolve TypeScript Plugin
If you would still prefer to use opt: target=ts
, it is recommended to use the
Resolve TypeScript Plugin. For more details on why this is
necessary, see the README of this plugin.
Note that some scaffolding tools such as Create React App
and the Angular CLI both use Webpack as their underlying bundler and at press time are using
a version of Webpack < 5.74.0. In addition, they discourage modification of the underlying Webpack config. Therefore,
until these tools are updated to the latest version of Webpack, the recommended workaround is to use opt: target=js+dts
as this is the path of least resistance.
If you've come this far and you still want to output TypeScript files, there are libraries you can use to override the Webpack config in the above tools such as react-app-rewired and Angular Builders Custom Webpack
For a working example of the above with Create React App, see the Create React App project in the examples-es repo.
Cypress
Cypress is an end-to-end testing framework that uses Webpack as its module bundler and as a result will also experience the file resolving issue. The options specified under v5.73.0 and below also apply. However, modifying the Webpack config in Cypress is a bit easier, so if you'd prefer to output TypeScript files, you'll also need a Cypress plugin called Cypress Webpack Preprocessor, which should be packaged with Cypress by default.
The Webpack overrides will need to be in your cypress.config
file. An example config would look something like this:
import { defineConfig } from 'cypress'
const webpackPreprocessor = require('@cypress/webpack-preprocessor')
const ResolveTypeScriptPlugin = require('resolve-typescript-plugin')
const options = {
webpackOptions: {
resolve: {
extensions: ['.ts', '.tsx', '.js'],
plugins: [new ResolveTypeScriptPlugin()],
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
options: { transpileOnly: true },
},
],
},
},
}
export default defineConfig({
e2e: {
specPattern: 'cypress/e2e/**/*.{cy,spec}.{js,jsx,ts,tsx}',
baseUrl: 'http://localhost:4173',
setupNodeEvents(on, config) {
on('file:preprocessor', webpackPreprocessor(options))
},
},
})
For a working example, see the Vue project in the examples-es repo.
Parcel
Parcel is another build tool that is susceptible to the aforementioned resolver issue. With Parcel, you will see an error similar to:
@parcel/resolver-default: Cannot load file 'path/to/your/connect/genfiles'
As a result, the current recommendation when using Parcel is to eschew the usage of target=ts
in your buf.gen.yaml
file and instead to use opt: target=js+dts
.
Unfortunately, if you still prefer to use target=ts
, there is no workaround at this time as no plugin exists similar to the Webpack
Resolve TypeScript Plugin. For more details, see this
Github issue.
React Native
React Native does not offer great support for the Fetch API and the Text Encoding API. As such, React Native requires a polyfill for these two implementations. We recommend the following to accomplish this:
- Install the polyfills.
npm install react-native-fast-encoder react-native-fetch-api web-streams-polyfill
- Apply the polyfills in the root of your app.
import { polyfillGlobal } from "react-native/Libraries/Utilities/PolyfillFunctions"
import TextEncoder from "react-native-fast-encoder"
import { fetch, Headers, Request, Response } from "react-native-fetch-api"
import { ReadableStream } from "web-streams-polyfill"
polyfillGlobal("ReadableStream", () => ReadableStream)
polyfillGlobal("TextDecoder", () => TextEncoder)
polyfillGlobal("TextEncoder", () => TextEncoder)
polyfillGlobal(
"fetch", () => (...args) => fetch(args[0], {
...args[1],
// Inject textStreaming: https://github.com/react-native-community/fetch/issues/15
reactNative: { textStreaming: true }
}),
)
polyfillGlobal("Headers", () => Headers)
polyfillGlobal("Request", () => Request)
polyfillGlobal("Response", () => Response)
Unfortunately, only unary requests via the Connect protocol are supported in React Native due to some limitations in React Native's implementation of the Fetch API. For more information, please see this issue
For a working example, see the React Native project in the examples-es repo.
Jest
There is one configuration to be aware of when using Jest in concert with the opt: target=ts
option in buf.gen.yaml
.
For Jest to find generated artifacts, you will need to specify a moduleNameMapper
option in your package.json
file
as follows:
{
"name": "buf-cra",
"version": "0.1.0",
"private": true,
"dependencies": {
// ...
},
"jest": {
"moduleNameMapper": {
"(.+)\\.js": "$1"
}
}
}
For a working example, see the Create React App project in the examples-es repo.