Server plugins
If you just need an API server, using a built-in Node.js server might be sufficient, but Connect also supports several server frameworks on Node.js.
The following code snippets expect that you have already added a file connect.ts
with your Connect RPCs to your
project. See Implementing services for more information.
Vanilla Node.js
Run your Connect RPCs on the Node.js built in HTTP modules with the function
connectNodeAdapter()
from @bufbuild/connect-node:
import * as http from "http";
import routes from "./connect";
import { connectNodeAdapter } from "@bufbuild/connect-node";
http.createServer(
connectNodeAdapter({ routes }) // responds with 404 for other requests
).listen(8080);
The function accepts all common options, and the following additional ones:
fallback?: NodeHandlerFn
If none of the handler request paths match, a 404 is served. This option can provide a custom fallback for this case.requestPathPrefix?: string
Serve all handlers under this prefix. For example, the prefix "/something" will serve the RPC foo.FooService/Bar under "/something/foo.FooService/Bar". Note that many gRPC client implementations do not allow for prefixes.
Fastify
Fastify is a fast and low overhead web framework, for Node.js. We highly recommend it if you want to serve anything else along with your Connect RPCs. Use the plugin from @bufbuild/connect-fastify with Fastify:
import { fastify } from "fastify";
import routes from "./connect";
import { fastifyConnectPlugin } from "@bufbuild/connect-fastify";
const server = fastify();
await server.register(fastifyConnectPlugin, {
routes
});
await server.listen({
host: "localhost",
port: 8080,
});
The plugin accepts all common options.
Next.js
Next.js is a framework supported by Vercel that enables creating full-stack web applications using the latest React features. With @bufbuild/connect-next, you can serve your Connect RPCs via Next.js API Routes.
To enable the server plugin, create the file pages/api/[[...connect]].ts
in your project:
import { nextJsApiRouter } from "@bufbuild/connect-next";
import routes from "./connect";
const {handler, config} = nextJsApiRouter({ routes });
export {handler as default, config};
This file is a Next.js catch-all API route. It will
serve your Connect RPCs with the /api
prefix. Make sure to include the /api
prefix in the baseUrl
option for
your client transport.
Note that Next.js does not support the http2 module.
Express
Express has been around for a long time, and it's still popular because of its simplicity. Use the middleware provided by @bufbuild/connect-express to add your Connect RPCs to Express:
import http from "http";
import express from "express";
import routes from "./connect";
import { expressConnectMiddleware } from "@bufbuild/connect-express";
const app = express();
app.use(expressConnectMiddleware({
routes
}));
http.createServer(app).listen(8080);
The middleware accepts all common options, and the following additional one:
requestPathPrefix?: string
Serve all handlers under this prefix. For example, the prefix "/something" will serve the RPC foo.FooService/Bar under "/something/foo.FooService/Bar". Note that many gRPC client implementations do not allow for prefixes.
Note that Express does not support the http2
module.
Common options
All adapters take a set of common options:
routes: (router: ConnectRouter) => void
The adapter will call this function, and lets you register your services.
See Implementing services for an example.connect?: boolean
Whether to enable the Connect protocol for your routes. Enabled by default.grpcWeb?: boolean
Whether to enable the gRPC protocol for your routes. Enabled by default.grpc?: boolean
Whether to enable the gRPC protocol for your routes. Enabled by default.
HTTP/2, TLS, and gRPC
All examples above use HTTP 1.1 without TLS (Transport Layer Security).
This works just fine for browsers and Connect clients, but most gRPC clients require HTTP/2. If you want gRPC clients and browsers, you need HTTP/2 and TLS:
Web browsers | Connect | gRPC-web | gRPC | |
---|---|---|---|---|
HTTP/2 + TLS | ✓ | ✓ | ✓ | ✓ |
HTTP/2 cleartext | ✓ | ✓ | ✓ | |
HTTP 1.1 | ✓ | ✓ | ✓ |
TLS is usually used to negotiate the HTTP protocol version for a connection.
Without TLS, you have to tell the client which HTTP version it should use.
For example, you would use the flag --http2-prior-knowledge
with cURL or
buf curl
.
Unfortunately, web browsers do not have such a flag, and flat out refuse HTTP/2 over cleartext. If you want to use gRPC clients and browser clients during local development, we recommend to set up locally-trusted development certificates and run HTTP/2 with TLS. This actually only takes a minute to set up if you follow the steps in Getting Started.
CORS
If your browser client makes a request to a different host or port, the browser will send a preflight request first, and will let the server decide whether the actual request should be allowed, and which response headers should be visible to the browser client. This mechanism is called CORS, short for Cross-Origin Resource Sharing.
If your server does not handle CORS preflight requests properly, you will see an
error like Failed to fetch
in the browser, and response headers sent by your
server will be invisible to your client.
Fortunately, middleware like cors and
@fastify/cors make it easy to
support CORS, and the cors
object from @bufbuild/connect
provides helpful constants to configure middleware for the protocols supported
by Connect:
allowedMethods
- Values for theAccess-Control-Allow-Methods
CORS header.allowedHeaders
- Values for theAccess-Control-Allow-Headers
CORS header.Make sure to also include any application-specific headers your browser client may send.
exposedHeaders
- Values for theAccess-Control-Expose-Headers
CORS header of the actual response.Make sure to also include any application-specific headers your browser client should see. If your application uses trailers, they will be sent as header fields with a
Trailer-
prefix for Connect unary RPCs - make sure to expose them as well if you want them to be visible in all supported protocols.
The following example illustrates how to make use of the above constants with
the Fastify plugin and its associated CORS library @fastify/cors
.
import fastifyCors from "@fastify/cors";
import { cors } from "@bufbuild/connect";
await server.register(fastifyCors, {
// Reflects the request origin. This should only be used for development.
// Production should explicitly specify an origin
origin: true,
methods: [...cors.allowedMethods],
allowedHeaders: [...connectCors.allowedHeaders, "Custom-Request-Header"],
exposedHeaders: [
...connectCors.exposedHeaders,
"Custom-Response-Header",
"Trailer-Response-Id",
],
// Let browsers cache CORS information for longer, which reduces the number
// of preflight requests. Any changes to exposedHeaders won't take effect
// until the cached data expires. Firefox caps this value at 24h, and modern
// Chrome caps it at 2h. Value shown is in seconds.
maxAge: 2 * 60 * 60
});
For additional examples using CORS with the various flavors of Node.js servers, see the Express and Vanilla Node examples in the Connect ES Integration repository.
Finally, for detailed information on CORS in general, please view the MDN documentation.