These integration guides are not official documentation and the Strapi Support Team will not provide assistance with them.
What Is Bun?
Bun is a JavaScript runtime built on Apple's JavaScriptCore engine. It ships as an all-in-one toolkit: runtime, package manager, bundler, and test runner in a single binary. Where Node.js requires you to assemble a toolchain from npm, webpack or Vite, ts-node, and Jest, Bun handles all of those roles natively.
The package manager is where most developers feel the difference immediately. bun install resolves and installs dependencies significantly faster than npm or yarn, thanks to a global module cache and optimized resolution algorithm. Bun also executes TypeScript directly without a separate compilation step, and its built-in bundler can handle production builds without additional configuration.
As of Bun v1.3.10, the runtime provides broad Node.js API compatibility, though gaps remain for applications that depend heavily on native C/C++ addons and specific middleware frameworks.
Sign up for the Logbook, Strapi's Monthly newsletter
Why Integrate Bun with Strapi
Even with the runtime limitation, Bun adds tangible value to a Strapi development workflow. Here's what you gain from the hybrid approach:
- Faster dependency installs.
bun installworks with Strapi'spackage.jsonand resolves the full dependency tree in a fraction of the time npm takes. This adds up fast across CI pipelines and fresh environment setups. - Native TypeScript for scripts and tooling. Migration scripts, seed files, and build orchestration scripts run directly with
bun run script.ts, nots-nodeortscstep required. - Built-in bundling for frontends. Bun's bundler can handle your React frontend without webpack or Vite configuration overhead, while Astro and SvelteKit currently rely on their existing Vite-based build tooling when run with Bun.
- Monorepo workspace support. Bun workspaces manage dependencies across your Strapi backend and frontend apps from a single lockfile.
- Faster test execution. The Bun test runner provides built-in watch mode and coverage reporting for frontend and utility code.
- Simplified development orchestration. Bun scripts can spawn and manage both your Node.js Strapi process and Bun-powered frontend server from a single command.
How to Integrate Bun with Strapi
This section walks through setting up a hybrid workspace where Bun manages tooling and frontend development while Node.js powers the Strapi backend.
Prerequisites
Before starting, verify you have the following installed:
| Tool | Version | Purpose |
|---|---|---|
| Node.js | v20, v22, or v24 (LTS) | Strapi v5 runtime |
| Bun | v1.3+ | Package management, frontend runtime |
| PostgreSQL | 14.0+ | Database (recommended for production) |
| Git | Latest | Version control |
Check your installations:
node --version # Should output v20.x, v22.x, or v24.x
bun --version # Should output 1.3.x
psql --version # Should output 14.0+You should also be comfortable with Strapi's Content-Type Builder and REST API concepts. Familiarity with TypeScript is helpful but not required.
Step 1: Create the Workspace Structure
Start by scaffolding a monorepo that separates the Strapi backend from your frontend application. This structure lets each project use the runtime it needs.
mkdir strapi-bun-project && cd strapi-bun-projectCreate the root package.json with Bun workspace configuration:
{
"name": "fullstack-strapi-project",
"workspaces": ["backend", "frontend"],
"scripts": {
"install": "bun install",
"dev": "bun run scripts/dev.ts",
"build": "bun run scripts/build.ts"
}
}The workspaces field tells Bun to manage dependencies across both directories from a single bun.lockb lockfile.
Step 2: Install Strapi v5
Scaffold a new Strapi v5 project inside the backend directory:
npx create-strapi@latest backendDuring the interactive setup, select your preferred database. For production projects, PostgreSQL is the recommended choice according to a Strapi blog article on cloud database providers, not the core database documentation.
Once the scaffolding completes, install dependencies using Bun's package manager from the project root:
bun installThis installs workspace dependencies via Bun, but some Strapi dependencies (such as aliased packages like codemirror5) may still require manual workarounds when using bun install. The Bun CLI handles package.json resolution the same way npm does, just faster.
Important: While bun install works for dependency installation, always use Node.js to run Strapi commands. Attempting bun run strapi develop triggers errors in Koa.js middleware and database drivers documented in GitHub Issue #24892.
Step 3: Configure the Strapi Backend
Set up your Strapi environment variables in backend/.env:
HOST=0.0.0.0
PORT=1337
APP_KEYS=yourAppKey1,yourAppKey2
API_TOKEN_SALT=yourApiTokenSalt
ADMIN_JWT_SECRET=yourAdminJwtSecret
TRANSFER_TOKEN_SALT=yourTransferTokenSalt
JWT_SECRET=yourJwtSecret
DATABASE_CLIENT=postgres
DATABASE_HOST=127.0.0.1
DATABASE_PORT=5432
DATABASE_NAME=strapi
DATABASE_USERNAME=strapi
DATABASE_PASSWORD=strapiVerify the database configuration in backend/config/database.js matches your environment:
// backend/config/database.js
module.exports = ({ env }) => ({
connection: {
client: env("DATABASE_CLIENT", "postgres"),
connection: {
host: env("DATABASE_HOST", "127.0.0.1"),
port: env.int("DATABASE_PORT", 5432),
database: env("DATABASE_NAME", "strapi"),
user: env("DATABASE_USERNAME", "strapi"),
password: env("DATABASE_PASSWORD", "strapi"),
},
},
});Start Strapi to confirm everything works:
cd backend && node node_modules/.bin/strapi developThe Admin Panel should be accessible at http://localhost:1337/admin. Create your admin account, then stop the server.
Step 4: Set Up the Frontend with Bun
For this guide, we'll use Astro as the frontend framework. Astro officially documents Strapi integration, and while it can run on Bun's runtime, the Astro team notes there are still rough edges and Bun is not yet officially supported.
From the project root:
cd frontend
bun create astro@latest .Follow the Astro setup prompts. Once complete, add environment variables for the Strapi connection in frontend/.env:
STRAPI_URL=http://localhost:1337
PUBLIC_STRAPI_URL=http://localhost:1337Step 5: Create a Development Orchestration Script
This is where Bun's native TypeScript execution shines. Create a script that starts both services simultaneously:
mkdir scripts// scripts/dev.ts
import { spawn } from "bun";
console.log("Starting development environment...");
const strapiProcess = Bun.spawn(
["node", "node_modules/.bin/strapi", "develop"],
{
cwd: "./backend",
stdout: "inherit",
stderr: "inherit",
env: { ...process.env, NODE_ENV: "development" },
}
);
const clientProcess = Bun.spawn(["bun", "run", "dev"], {
cwd: "./frontend",
stdout: "inherit",
stderr: "inherit",
});
process.on("SIGINT", () => {
strapiProcess.kill();
clientProcess.kill();
process.exit(0);
});
await Promise.all([strapiProcess.exited, clientProcess.exited]);Now bun run dev from the root starts both Strapi (on Node.js) and Astro (on Bun) in a single terminal session. No separate tabs, no third-party process managers for development.
Step 6: Create a Production Build Script
Add a build script that handles both projects:
// scripts/build.ts
import { spawn } from "bun";
console.log("Building for production...");
// Build Strapi admin panel with Node.js
const strapiBuild = Bun.spawn(
["node", "node_modules/.bin/strapi", "build"],
{
cwd: "./backend",
stdout: "inherit",
stderr: "inherit",
env: { ...process.env, NODE_ENV: "production" },
}
);
await strapiBuild.exited;
console.log("Strapi admin panel built.");
// Build frontend with Bun
const frontendBuild = Bun.spawn(["bun", "run", "build"], {
cwd: "./frontend",
stdout: "inherit",
stderr: "inherit",
});
await frontendBuild.exited;
console.log("Frontend built. Ready for deployment.");Run the full build with:
bun run buildProject Example: Blog with Astro Frontend and Strapi v5
Let's build a blog that demonstrates the hybrid architecture in practice. Strapi handles content management, and an Astro frontend powered by Bun fetches and renders articles.
Define the Content Model in Strapi
Start the backend and open the Content-Type Builder at http://localhost:1337/admin:
cd backend && node node_modules/.bin/strapi developCreate a new Collection Type called Article with the following fields:
| Field Name | Type | Details |
|---|---|---|
| title | Text | Required |
| slug | UID | Attached to title |
| content | Rich Text (Blocks) | Main article body |
| excerpt | Text (Long) | Short summary |
| publishedDate | Date | Publication date |
| cover | Media (Single) | Featured image |
After creating the Collection Type, add a few sample articles through the Content Manager. Make sure to publish them.
Next, configure API permissions. Navigate to Settings → Users & Permissions → Roles → Public, and enable find and findOne for the Article content type. This exposes the REST API endpoints without authentication for public content.
Build the Astro Frontend
Create a utility function for fetching data from Strapi's REST API:
// frontend/src/lib/strapi.ts
const STRAPI_URL = import.meta.env.STRAPI_URL || "http://localhost:1337";
interface StrapiResponse<T> {
data: T;
meta: {
pagination?: {
page: number;
pageSize: number;
pageCount: number;
total: number;
};
};
}
export async function fetchFromStrapi<T>(
endpoint: string,
params: Record<string, string> = {}
): Promise<StrapiResponse<T>> {
const url = new URL(`/api/${endpoint}`, STRAPI_URL);
Object.entries(params).forEach(([key, value]) => {
url.searchParams.set(key, value);
});
const response = await fetch(url.toString());
if (!response.ok) {
throw new Error(`Strapi API error: ${response.status} ${response.statusText}`);
}
return response.json();
}Create the articles listing page. According to Astro's data fetching documentation, you can call APIs directly in the frontmatter:
---
// frontend/src/pages/articles.astro
import { fetchFromStrapi } from '../lib/strapi';
import Layout from '../layouts/Layout.astro';
interface Article {
id: number;
documentId: string;
title: string;
slug: string;
excerpt: string;
publishedDate: string;
}
const { data: articles } = await fetchFromStrapi<Article[]>('articles', {
'fields[0]': 'title',
'fields[1]': 'slug',
'fields[2]': 'excerpt',
'fields[3]': 'publishedDate',
'sort[0]': 'publishedDate:desc',
'pagination[pageSize]': '10',
});
---
<Layout title="Blog Articles">
<main>
<h1>Articles</h1>
<div class="article-grid">
{articles.map((article) => (
<article>
<h2>
<a href={`/articles/${article.slug}`}>
{article.title}
</a>
</h2>
<time datetime={article.publishedDate}>
{new Date(article.publishedDate).toLocaleDateString()}
</time>
<p>{article.excerpt}</p>
</article>
))}
</div>
</main>
</Layout>Containerize with Docker
For production deployment, Docker provides environment consistency. Here's a docker-compose.yml following Strapi's Docker documentation:
version: '3'
services:
strapi:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "1337:1337"
environment:
- DATABASE_CLIENT=postgres
- DATABASE_HOST=postgres
- DATABASE_PORT=5432
- DATABASE_NAME=strapi
- DATABASE_USERNAME=strapi
- DATABASE_PASSWORD=strapi
- APP_KEYS=yourAppKey1,yourAppKey2
- API_TOKEN_SALT=yourApiTokenSalt
- ADMIN_JWT_SECRET=yourAdminJwtSecret
- TRANSFER_TOKEN_SALT=yourTransferTokenSalt
- JWT_SECRET=yourJwtSecret
- NODE_ENV=production
depends_on:
- postgres
volumes:
- strapi-uploads:/srv/app/public/uploads
networks:
- app-network
postgres:
image: postgres:17-alpine
environment:
- POSTGRES_DB=strapi
- POSTGRES_USER=strapi
- POSTGRES_PASSWORD=strapi
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- app-network
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- STRAPI_URL=http://strapi:1337
- PUBLIC_STRAPI_URL=http://localhost:1337
depends_on:
- strapi
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
postgres-data:
strapi-uploads:Notice the two different Strapi URLs. Server-side API calls within the Docker network use http://strapi:1337 (service name resolution), while client-side browser requests use http://localhost:1337 (exposed port mapping).
The Strapi backend container uses a Node.js base image as required, while the frontend container can use Bun's official image for faster builds and script execution.
Strapi Open Office Hours
If you have any questions about Strapi 5 or just would like to stop by and say hi, you can join us at Strapi's Discord Open Office Hours, Monday through Friday, from 12:30 pm to 1:30 pm CST: Strapi Discord Open Office Hours.
For more details, visit the Strapi documentation and Bun documentation.
Get Started in Minutes
npx create-strapi-app@latest in your terminal and follow our Quick Start Guide to build your first Strapi project.