NextJS API routes with Tauri

I have used both NextJS and Tauri in many projects and they are a fantastic combo.

However, there is one problem. NextJS API routes are not compatible with Tauri.

Tauri requires that a bundled NextJS application is exported as a statically generated site. Whilst this is great for speed, the downfall is that the app will not support API routes.

This is a major drawback, as one of biggest strengths of NextJS is the ability to create API routes.

Thankfully, there is a way to work around this problem.

TLDR: just show me the code.

The full source code is available on Github.

The Solution

Whilst it is not possible to directly use NextJS API routes with Tauri, it is possible to create a separate server with routes that share logic your NextJS API.

The server can then be bundled into a binary using pkg to be used by Tauri.

This server can be made using any framework you like but for my example I used Fasitfy.

import Fastify from 'fastify';
import cors from '@fastify/cors';
import { add } from '@/shared/add'; // Shared code between NextJS and server

const fastify = Fastify({
  logger: true,
});

fastify.register(cors);

const PORT = 5661;

fastify.get('/add', async (req, res) => {
  const added = add(1, 2);
  res.type('application/json').status(200).send(added);
});

fastify.listen(PORT, () => {
  console.log(`Server listening on port ${PORT}`);
});

In this code I am using the add function from the shared folder, which is also used in my NextJS API route.

Compiling the server

First we need to compile our Typescript code into Javascript. We can do this by running the command:

  npx tsx ./build-server.ts

We do this using the excellent esbuild bundler.

The code to bundle the server is as follows:

import * as esbuild from 'esbuild';

(async () => {
  await esbuild.build({
    entryPoints: ['./server/server.ts'],
    bundle: true,
    platform: 'node',
    target: ['node18.0'],
    outfile: 'dist/server/server.js',
    plugins: [],
  });
})();

Next, the server can then be packaged up into a binary using pkg to be used by Tauri. You can do this by using the command:

  pkg dist/server/server.js -o ./src-tauri/bin/server-aarch64-apple-darwin

This will bundle up the server into a binary that can be used by Tauri, and place it inside the /src-tauri/bin to be used by Tauri.

Finally, we need to tell Tauri to use this binary. We can do this by adding the following to our tauri.conf.json file:

//  ...Other tauri config
    "bundle": {
      "externalBin": ["bin/server"],
    }
  ...

Tauri uses the binary and executes it as a sidecar process to the Tauri application. Your application can then do make requests to the server inside your Tauri application, just like it would call the NextJS API routes.

The application code looks something like this:


// ...In our react application
const apiURL = useRef('');

useEffect(() => {
  const isTauri = (window as any).__TAURI__;
  // Set the url to the server if running in Tauri. This is a Ref.
  apiURL.current = isTauri ? 'http://localhost:5661/add' : '/api/add';
  if (isTauri) {
    // Import tauri command and execute the sidecar process
    import('@tauri-apps/api/shell').then((mod) => {
      const command = mod.Command.sidecar('bin/server');
      command.execute();
    });
  }
}, []);
// ...more react code

This code checks if the application is running in Tauri, and if it is, it executes the server binary as a sidecar process.

Ww also set the apiURL to the server if running in Tauri, otherwise it will use the NextJS API route.

Conclusion

Although NextJS API routes aren’t directly compatible with Tauri, this is a pretty straightforward workaround. By creating a separate server to handle the API routes, you can continue to use the full power of NextJS in your tauri application.

The full source code is available on Github.

If you liked this content, please follow me on Twitter where I post nuggets of wisdom and other interesting things all related to web development.