npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

next-rpc

v3.7.1

Published

Call serverside code from the browser in Next.js applications

Downloads

1,146

Readme

next-rpc

next-rpc makes exported functions from API routes accessible in the browser. Just import your API function and call it anywhere you want.

Example

Define your rpc route as follows:

// /pages/api/countries.js
export const config = { rpc: true }; // enable rpc on this API route

// export a function that needs to be called from the server and the browser
export async function getName(code) {
  return db.query(`SELECT name FROM country WHERE code = ?`, code);
}

Now in your components you can just import getName and call it anywhere you want:

// /pages/index.js
import { getName } from './api/countries';

export default function MyPage({ initialData }) {
  const [countryName, setCountryName] = React.useState(initialData);

  return (
    <button onClick={() => getName('BE').then(setCountryName)}>
      {countryName || 'click me'}
    </button>
  );
}

Installation

Install the next-rpc module

npm install -S next-rpc

configure Next.js to use the module

// ./next.config.js
const withRpc = require('next-rpc')();
module.exports = withRpc({
  // your next.js config goes here
});

Why this library is needed

Next.js 9.3 introduced getServerSideProps and getStaticProps. New ways of calling serverside code and transfer the data to the browser. a pattern emerged for sharing API routes serverside and browserside. In short the idea is to abstract the logic into an exported function for serverside code and expose the function to the browser through an API handler.

// /pages/api/myApi.js
export async function getName(code) {
  return db.query(`SELECT name FROM country WHERE code = ?`, code);
}

export default async (req, res) => {
  res.send(await getName(req.query.code));
};

This pattern is great as it avoids hitting the network when used serverside. Unfortunately, to use it client side it still involves a lot of ceremony. i.e. a http request handler needs to be set up, fetch needs to be used in the browser, the input and output needs to be correctly encoded and decoded. Error handling needs to be set up to deal with network related errors. If you use typescript you need to find a way to propagate the types from API to fetch result. etc...

Wouldn't it be nice if all of that was automatically handled and all you'd need to do is import getName on the browserside, just like you do serverside? That's where next-rpc comes in. With a next-rpc enabled API route, all its exported functions automatically become available to the browser as well.

Note: next-rpc is not meant as a full replacement for Next.js API routes. Some use cases are still better solved with classic API routes. For instance when you want to rely on the existing browser caching mechanisms.

Rules and limitations

  1. Rpc routes are only allowed to export async functions. They also need to be statically analyzable as such. Therefore only the following is allowed, either:

    export async function fn1() {}
    
    export const fn2 = async () => {};
  2. All inputs and outputs must be simple JSON serializable values.

  3. a default export is not allowed. next-rpc will generate one.

  4. You must enable rpc routes through the config export. It must be an exported object that has the rpc: true property.

typescript

Try the example on codesandbox

next-rpc works really nicely with typescript. There is no serialization layer so functions just retain their type sigantures both on server and client.

swr

Try the example on codesandbox

next-rpc can work seamlessly with swr.

// ./pages/api/projects.js
export const config = { rpc: true };

export async function getMovies(genre) {
  return db.query(`...`);
}

// ./pages/index.jsx
import useSwr from 'swr';
import { getMovies } from './api/movies';
import MoviesList from '../components/MoviesList';

const callFn = (method, ...params) => method(...params);

export default function Comedies() {
  const { data, error } = useSwr([getMovies, 'comedy'], callFn);
  if (error) return <div>failed to load</div>;
  if (!data) return <div>loading...</div>;
  return <MoviesList items={data} />;
}

react-query

Try the example on codesandbox

next-rpc can also work with react-query.

// ./pages/api/projects.js
export const config = { rpc: true };

export async function getMovies(genre) {
  return db.query(`...`);
}

// ./pages/index.jsx
import { QueryClient, QueryClientProvider, useQuery } from 'react-query';
import { getMovies } from './api/movies';
import MoviesList from '../components/MoviesList';

function App() {
  const queryClient = React.useMemo(() => new QueryClient(), []);
  return (
    <QueryClientProvider client={queryClient}>
      <Movies genre="comedy" />
    </QueryClientProvider>
  );
}

export default function Movies({ genre = 'comedy' }) {
  const { isLoading, error, data } = useQuery(['getMovies', genre], () =>
    getMovies(genre)
  );
  if (error) return <div>failed to load</div>;
  if (isLoading) return <div>loading...</div>;
  return <MoviesList items={data} />;
}

Middleware

next-rpc allows for defining middleware functions that automatically wrap all your API methods. this could be useful for logging purposes. To define such middleware you can supply a wrapMethod option to the config export. This function receives the method it's wrapping along with some metadata and is expected to return a function with the exact same signature. Example:

import { NextRpcConfig, WrapMethod } from 'next-rpc';

const wrapMethod: WrapMethod = (method, meta) => {
  return async (...args) => {
    console.log(`calling "${meta.name}" on "${meta.pathname}" with ${args}`);
    const result = await method(...args);
    console.log(`result: ${result}`);
    return result;
  };
};

export const config: NextRpcConfig = {
  rpc: true,
  wrapMethod,
};

Debugging next-rpc

Since version 3.7.0, next-rpc uses the JSON-RPC format for its messages. This makes it possible to use tools like JSON RPC Chrome Viewer to introspect the rpc frames that are being transferred by next-rpc.

JSON RPC Chrome Viewer screenshot

Disclaimer: I have no affiliation with this extension, use at your own discretion.

Next.js request context

⚠️ warning:

request context is not supported in the Next.js edge runtime and won't be for the foreseeable future.

This library completely hides the network layer. This makes it elegant to use, but also imposes limitations. To efficiently be able to implement things like cookie authentication, access to the underlying requests is required. To enable that, this library introduces next-rpc/context. An example:

// ./pages/api/myRpc.js
import { getContext } from 'next-rpc/context';

const config = { rpc: true };

export async function currentUser() {
  const { req } = getContext();
  return getUserFromRequest(req);
}

The req variable in the previous example will contain the IncomingMessage that lead to the call of currentUser(). That means it will receive req from either:

  • NextPageContext: if it traces back to getServerSideProps or getInitialProps.
  • NextApiHandler: if it traces back to a call in another API handler, or if it was called from the browser.

next-rpc intercepts all instances of getInitialProps, getServerSideProps and api handlers and injects its context provider in there. From there on, every function invocation that descends from that point will be able to access the context through getContext. Since this feature relies on experimental APIs, it needs to be explicitly enabled by configuring the experimentalContext flag in next.config.js:

// ./next.config.js
const withRpc = require('next-rpc')({
  experimentalContext: true,
});
module.exports = withRpc();

How it works

next-rpc compiles api routes. If it finds rpc enabled it will rewrite the module. In serverside bundles, it will generate an API handler that encapsulates all exported functions. For browserside bundles, it will replace each exported function with a function that uses fetch to call this API handler.

It's important to note that next-rpc intends to be fully backwards compatible. If you don't specify the rpc option, the API route will behave as it does by default in Next.js.

Roadmap

  • Improve dev experience: warn when using unserializable input/output
  • Custom contexts: it should be possible to build on the context feature to provide a custom context. e.g. UserContext.

Contributing

Bug fixing PRs are welcome, provided they are of high quality and accompagnied by tests. Don't open PRs for feature requests prior to my approval.