I've been working on a full-stack application where the backend is separate from the frontend. The project structure looks like this:
š Project Root
āāā š .env
āāā š .git
āāā š .gitignore
āāā š .vscode
āāā š README.md
āāā š bun.lockb
āāā š compose.yml
āāā š eslint.config.mjs
āāā š frontend
āāā š node_modules
āāā š package.json
āāā š prisma
āāā š scripts
āāā š server
āāā š shared
āāā š tsconfig.json
The client code is in the "frontend" code, while the server code is in the root directory. I opted for Bun and Hono to build the backend and React with Vite for the frontend.
I like Hono for multiple reasons. One is that its RPC feature allows you to share the API specifications between the server and the client. That means you have a type-safe API and autocomplete functionality.
Once the Hono server is ready, you can create the client on the frontend and pass the type representing your API spec:
import { hc } from 'hono/client';
import { AppType } from '@/server/app'
const client = hc<AppType>('http://localhost:1234/')
You can then use the client to interact with your server. If you hover over the client constant, you can see the available endpoints. Also, if you type client., it'll start suggesting endpoint paths and methods.
The Problem - AppType Has Type "any"
That's all great when it works. However, in project structures like the one from this article, you may run into issues when trying to import the AppType type from the server.

As the image shows, the IDE says that AppType has the type "any." This means there is no type-safety or auto-completion functionality.
Even though the import is correct, it doesn't work. So, how can we fix that?
The Solution - TypeScript Project References
The solution to make this work is to update the two tsconfig.json files. In the root tsconfig.json file, add:
{
"compilerOptions": {
"composite": true,
...
}
}
Then, add the following property to the tsconfig.json file from the "frontend" directory:
{
"compilerOptions": {
....
},
"references": [
...
{
"path": "../tsconfig.json"
}
],
"files": []
}
If we hover over the AppType after making these two changes, you can see that it works properly.

But how and why does it work now? It works because the TypeScript Project References allow multiple TypeScript projects to "cooperate".
"Cooperate" means that one TypeScript project can access and use code from another TypeScript project. In this case, I import code from the backend on the frontend (AppType).
The referenced project needs to have the composite flag set to true in tsconfig.json. And the project that accesses code from the referenced project needs to specify the path to that project in its tsconfig.json:
....
"references": [
...
{
"path": "../tsconfig.json"
}
],
....In simpler terms:
- backend's
tsconfigfile - enable thecompositeflag here - frontend's
tsconfig- use thereferencesproperty here
After making the changes, restart the TypeScript server, and it should work properly.
Optional: Same Hono Version on the Client and Server
If you get further errors after setting up the project references, make sure that you use the same Hono version on both the server and the client.
In my case, I had version ^4.6.12 on the backend and ^4.6.14 on the frontend, which caused issues with the Hono client.
Optional: Better Project Structure
The current project structure, with the server in the root directory, is not the best choice. As a result, I split the app into 2 separate folders:
- client
- server