Goal for this step 🏁: Have a working frontend and backend that communicates with each other, without any Dossier specific functionality.
Before getting started with Dossier we need to have a project with a working frontend and backend. Ensure you have Node installed on your computer and run these commands in a terminal:
npm create vite@latest tutorial -- --template react-ts
cd tutorial
npm install
You should now have a working React / Vite project. Let's also add an Express server to the mix:
npm install express body-parser
npm install -D concurrently nodemon tsx @types/express @types/body-parser
We can now create a backend/main.ts
file:
import bodyParser from 'body-parser';
import express from 'express';
const app = express();
const port = 3000;
app.use(bodyParser.json());
app.get('/api/message', (req, res) => {
res.send({ message: 'Hello World!' });
});
app.listen(port, () => {
console.log(`Listening on http://localhost:${port}`);
});
After adding the following to package.json
, we can start both the frontend and backend with: npm start
.
{
"scripts": {
"start": "concurrently -n be,fe --handle-input 'nodemon --watch \"backend/*\" --exec tsx backend/main.ts' 'sleep 2 && vite'",
}
}
Let's add support for React Router, so we can add more pages in the future.
npm install react-router-dom
By renaming src/App.tsx
to src/IndexRoute.tsx
we can add a new src/App.tsx
that uses the route:
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import { IndexRoute } from './IndexRoute.js';
const router = createBrowserRouter([
{ path: '/', element: <IndexRoute /> },
]);
export default function App() {
return (<RouterProvider router={router} />);
}
To use the backend from the frontend, we change src/IndexRoute.tsx
to:
import { useEffect, useState } from 'react';
export function IndexRoute() {
const [message, setMessage] = useState<string | null>(null);
useEffect(() => {
fetch('/api/message')
.then((res) =>
res.ok
? res.json()
: { message: `Failed to fetch message: ${res.status} ${res.statusText}` }
)
.then((data) => setMessage(data.message));
}, []);
return (
<div style={{ maxWidth: '30rem', marginRight: 'auto', marginLeft: 'auto' }}>
<h1 style={{ fontSize: '2em' }}>Dossier</h1>
{message && <div>Got: {message}</div>}
</div>
);
}
And finally setting up Vite to proxy the calls to the backend in vite.config.ts
:
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [react()],
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
}
}
}
});
When opening http://localhost:5173/
in a browser we get this page: