el enfoque es similar a nextjs, ya tendra sun fronten de react y un backend con nestjs, todo con typescript.
Un monorepo tiene las ventajas:
- un solo pipeline
- configuracion y codigo compartido
- nestjs puede servir react
mkdir nestjs-react-monorepo
cd nestjs-react-monorepo
instalacion de turbo
npm init -y
npm i -D turbo
mkdir apps
cd apps
nest new api
npm
Puedes cancelar la instalacion de modulos de npm de nestjs, porque mas adelante instalaremos tanto los modulos de backend y frontend juntos
Luego dentro de la misma carpeta apps, ejecuta:
npm create vite
react y typescript npm
cd ..
luego ubicado en el root del proyecto instala todas las dependencias
configurando turbo repo
crea un archivo en la raiz del proyecto llamado turbo.json
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"dev": {
"cache": false
}
},
}
https://turbo.build/repo/docs/reference/configuration
Luego en nestjs cambia el script start:dev, por solo dev, para que tanto el frontnd como el backend tengan el mismo nombre.
deja de esta forma el comando dev:
"dev": "nest start --watch --preserveWatchOutput",
de esta forma nest no limpiara la consola borrando tambien los mensajes de vitesjs
Luego actualiza la configuracion de turbo asi
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"dev": {
"cache": false
}
},
"scripts": {
"dev": "npm run dev"
}
}
luego actualiza el package.json de root:
{
"scripts": {
"dev": "turbo run dev"
},
"devDependencies": {
"turbo": "^1.9.9"
},
"workspaces": [
"apps/*"
]
}
el desliegue
cuando hagamos build de nestjs, luego esete se ejecutara y servira el build de react. esto quiere decir que en produccion las peticiones del frontend llegar al servidor bajo el mimso dominio
proxy
pero en desarrollo seran dos dominios
asi que para evitar colocar configuraciones de mas, podemos usar un proxy desde el frontend ya que estamos usando vitejs. en apps/client/vite.config.js:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
server: {
proxy: {
"/api": {
target: "http://localhost:3000",
changeOrigin: true,
}
}
}
})
Luego en nesjts tambien necesitamos añadir un prefijo llamado /api en main.ts:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.setGlobalPrefix('/api');
await app.listen(3000);
}
bootstrap();
ahora todas las rutas empiezan con /api en el backend
Puedes probar esta configuracion usando esto:
function App() {
return (
<div>
<button
onClick={() => {
fetch("/api")
.then((res) => res.text())
.then((data) => console.log(data));
}}
>
get data
</button>
</div>
);
}
export default App;
build
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"dev": {
"cache": false
},
"build": {
"dependsOn": [
"^build"
],
"outputs": [
"dist/**"
]
}
}
}
en package.json del root:
"build": "turbo build"
npm run build
esto creara ambos build, de hecho si ejecutas de nuevo este usara la memoria cache para hacr el build de forma mas rapida
server archivos estaticos
en el root:
npm i --workspace api @nestjs/serve-static
luego en app.module.ts
import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path';
@Module({
imports: [
ServeStaticModule.forRoot({
rootPath: join(__dirname, '../..', 'client', 'dist'),
}),
],
...
})
export class AppModule {}
luego en api/packag.ejson
finalmente en el package.json de root:
"start": "node apps/api/dist/main.js","