Tutorial – Setting up the project

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.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(() => {
.then((res) =>
? 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>}

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:

The frontend showing Hello world