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 @connectrpc/connect-node:
import * as http from "http";
import routes from "./connect";
import { connectNodeAdapter } from "@connectrpc/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.contextValues?: (req: NodeServerRequest) => ContextValues
A function that returns a set of context values for each request. The context values are passed to the service implementation. See Context values for more information.
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 @connectrpc/connect-fastify with Fastify:
$ npm install fastify @connectrpc/connect-node @connectrpc/connect-fastify
import { fastify } from "fastify";
import routes from "./connect";
import { fastifyConnectPlugin } from "@connectrpc/connect-fastify";
const server = fastify();
await server.register(fastifyConnectPlugin, {
routes
});
await server.listen({
host: "localhost",
port: 8080,
});
The plugin accepts all common options, and the following additional ones:
shutdownTimeoutMs?: number
If set, the server will wait for the specified duration before aborting any in-flight requests oncefastify.close
is called.shutdownError?: unknown
The reason to use when shutdown occurs. Note that if this is aConnectError
it will be sent to the client.contextValues?: (req: FastifyRequest) => ContextValues
A function that returns a set of context values for each request. The context values are passed to the service implementation. See Context values for more information.
Note that @connectrpc/connect
and @connectrpc/connect-node
are peer
dependencies of @connectrpc/connect-fastify
.
Next.js
Next.js is a framework supported by Vercel that enables creating full-stack web applications using the latest React features. With @connectrpc/connect-next, you can serve your Connect RPCs via Next.js API Routes.
$ npm install next @connectrpc/connect-node @connectrpc/connect-next
To enable the server plugin, create the file pages/api/[[...connect]].ts
in your project:
import { nextJsApiRouter } from "@connectrpc/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.
The middleware accepts all common options, and the following additional one:
prefix?: string
Serve all handlers under this prefix. For example, the prefix "/something" will serve the RPC foo.FooService/Bar under "/something/foo.FooService/Bar". By default, this is/api
for Next.js.
Note that many gRPC client implementations do not allow for prefixes.contextValues?: (req: NextApiRequest) => ContextValues
A function that returns a set of context values for each request. The context values are passed to the service implementation. See Context values for more information.
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 @connectrpc/connect-express to add your Connect RPCs to Express:
$ npm install express @connectrpc/connect-node @connectrpc/connect-express
import http from "http";
import express from "express";
import routes from "./connect";
import { expressConnectMiddleware } from "@connectrpc/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.contextValues?: (req: express.Request) => ContextValues
A function that returns a set of context values for each request. The context values are passed to the service implementation. See Context values for more information.
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.maxTimeoutMs?: number
The maximum value for timeouts that clients may specify. If a client requests a timeout that is greater thanmaxTimeoutMs
, the server responds with the error codeinvalid_argument
.shutdownSignal?: AbortSignal
To shut down servers gracefully, this option takes anAbortSignal
. If this signal is aborted, all signals in handler contexts will be aborted as well. This gives implementations a chance to wrap up work before the server process is killed. Abort this signal with aConnectError
to send a message and code to clients.connect?: boolean
Whether to enable the Connect protocol for your routes. Enabled by default.grpcWeb?: boolean
Whether to enable the gRPC-web protocol for your routes. Enabled by default.grpc?: boolean
Whether to enable the gRPC protocol for your routes. Enabled by default.interceptors?: Interceptor[]
An array of interceptors to apply to all requests. See Interceptors for more information.
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. This mechanism is called Cross-Origin Resource Sharing, or CORS.
If your server is not set up to handle CORS preflight requests, you will see an
error like Failed to fetch
in the browser, or response headers sent by your
server will be invisible to your client.
The cors
object from @connectrpc/connect
helps to configure common middleware packages. The following example shows how
to use it with Fastify:
import fastifyCors from "@fastify/cors";
import { cors } from "@connectrpc/connect";
await server.register(fastifyCors, {
origin: "https://demo.connectrpc.com",
methods: cors.allowedMethods,
allowedHeaders: [
...cors.allowedHeaders,
"Custom-Request-Header"
],
exposedHeaders: [
...cors.exposedHeaders,
"Custom-Response-Header",
"Trailer-Response-Id",
],
// Let browsers cache CORS information to reduce the number of
// preflight requests. Modern Chrome caps the value at 2h.
maxAge: 2 * 60 * 60
});
Make sure to include application-specific request headers in the allowed headers,
and response headers in the exposed headers. If your application uses trailers,
they will be sent as header fields with a Trailer-
prefix for Connect unary RPCs.
For additional examples using CORS with the various flavors of Node.js servers, see the Express and Vanilla Node examples in the examples-es repository.