Skip to main content

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 browsersConnectgRPC-webgRPC
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 the Access-Control-Allow-Methods CORS header.

  • allowedHeaders - Values for the Access-Control-Allow-Headers CORS header.

    Make sure to also include any application-specific headers your browser client may send.

  • exposedHeaders - Values for the Access-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.