Creating a web application using Yarn workspace, TypeScript, esbuild, React, and Express — part 2
This article will guide you through setting up a basic web application using Yarn's workspace, TypeScript, esbuild, Express, and React. At the end of this tutorial you will have a fully buildable and deployable web application.
- Setting up the project — part 1
- Adding code — part 2
- Building the app — part 3
- Going further — advanced
Want to see the full code? Check out the repository on GitHub at halftheopposite/tutorial-app.
The second part will focus on adding the code to our common
, app
, and server
packages.
Common
We will start with common
as this package will be used by both app
and server
. It's goal is to provide shared logic and variables.
Files
For the purpose of this tutorial the common
package will be quite simple. First, start by adding a new folder:
- A
src/
folder, containing our package's code.
Once this folder has been created, add the following file into it:
src/index.ts
1export const APP_TITLE = 'my-app';
Now that we have some code to export, we want to tell TypeScript where to look for it when importing it from other packages. To do so, we will need to update the package.json
file:
package.json
1{ 2 "name": "@my-app/common", 3 "version": "0.1.0", 4 "license": "UNLICENSED", 5 "private": true, 6 "main": "./src/index.ts" // Add this line to provide TS with an entrypoint to this package. 7}
We have now completed the common
package!
Structure reminder:
1common/ 2├─ src/ 3│ ├─ index.ts 4├─ package.json
App
Dependencies
The app
package will need the following dependencies:
From the project's root, run:
yarn app add react react-dom
yarn app add -D @types/react @types/react-dom
(to add the typings for TypeScript)
package.json
1{ 2 "name": "@my-app/app", 3 "version": "0.1.0", 4 "license": "UNLICENSED", 5 "private": true, 6 "dependencies": { 7 "@my-app/common": "^0.1.0", // Notice that we've added this import manually 8 "react": "^17.0.1", 9 "react-dom": "^17.0.1" 10 }, 11 "devDependencies": { 12 "@types/react": "^17.0.3", 13 "@types/react-dom": "^17.0.2" 14 } 15}
Files
To create our React application we will need to add two new folders:
- A
public/
folder, that will hold the base HTML page and our assets. - A
src/
folder, containing our application's code.
Once these two folders have been created, we can start by adding the HTML file that will be our application's host.
public/index.html
1<!DOCTYPE html> 2<html> 3 <head> 4 <title>my-app</title> 5 <meta name="description" content="Welcome on my application!" /> 6 </head> 7 <body> 8 <noscript>You need to enable JavaScript to run this app.</noscript> 9 <!-- This div is where we will inject our React application --> 10 <div id="root"></div> 11 <!-- This is the path to the script containing our application --> 12 <script src="script.js"></script> 13 </body> 14</html>
Now that we have a page to render, we can implement our very basic, but functional, React application, by adding the two files below.
src/index.tsx
1import * as React from 'react'; 2import * as ReactDOM from 'react-dom'; 3 4import { App } from './App'; 5 6ReactDOM.render(<App />, document.getElementById('root'));
This code hooks into the root
div from our HTML file and injects the React component tree into it.
src/App.tsx
1import { APP_TITLE } from '@my-app/common'; 2import * as React from 'react'; 3 4export function App(): React.ReactElement { 5 const [count, setCount] = React.useState(0); 6 7 return ( 8 <div> 9 <h1>Welcome on {APP_TITLE}!</h1> 10 <p> 11 This is the main page of our application where you can confirm that it 12 is dynamic by clicking the button below. 13 </p> 14 15 <p>Current count: {count}</p> 16 <button onClick={() => setCount((prev) => prev + 1)}>Increment</button> 17 </div> 18 ); 19}
This simple App
component will render our app's title and a dynamic counter. It will be the entrypoint to our React tree. Feel free to add any code that you would like.
And that's it! We've completed our very basic React application. It doesn't do much for now, but we can always come back to it later and add more features.
Structure reminder:
1app/ 2├─ public/ 3│ ├─ index.html 4├─ src/ 5│ ├─ App.tsx 6│ ├─ index.tsx 7├─ package.json
Server
Dependencies
The server
package will need the following dependencies:
From the project's root, run:
yarn server add cors express
yarn server add -D @types/cors @types/express
(to add the typings for TypeScript)
package.json
1{ 2 "name": "@my-app/server", 3 "version": "0.1.0", 4 "license": "UNLICENSED", 5 "private": true, 6 "dependencies": { 7 "@my-app/common": "^0.1.0", // Notice that we've added this import manually 8 "cors": "^2.8.5", 9 "express": "^4.17.1" 10 }, 11 "devDependencies": { 12 "@types/cors": "^2.8.10", 13 "@types/express": "^4.17.11" 14 } 15}
Files
Our React app is now ready, and the last part that we need is a server to serve it. Start by creating the following folder to it:
- A
src/
folder, containing our server's code.
Next, add the main file for our server:
src/index.ts
1import { APP_TITLE } from '@my-app/common'; 2import cors from 'cors'; 3import express from 'express'; 4import { join } from 'path'; 5 6const PORT = 3000; 7 8const app = express(); 9app.use(cors()); 10 11// Serve static resources from the "public" folder (ex: when there are images to display) 12app.use(express.static(join(__dirname, '../../app/public'))); 13 14// Serve the HTML page 15app.get('*', (req: any, res: any) => { 16 res.sendFile(join(__dirname, '../../app/public', 'index.html')); 17}); 18 19app.listen(PORT, () => { 20 console.log(`${APP_TITLE}'s server listening at http://localhost:${PORT}`); 21});
This is a very basic Express application, but if we don't have anything else beside a Single-Page Application to serve, then that's pretty much all we need.
Structure reminder:
1server/ 2├─ src/ 3│ ├─ index.ts 4├─ package.json
Summary
All modules are now ready and our small app has all the code it needs to run. However, since we are using TypeScript, which is not pure JavaScript, we need to a way to convert all our files into something that is parsable and executable by Node.js (for our server), or the browser (for our React application). This step is called transpiling. The next post will explain how to build and serve our application using a bundler.
Posted on March 12, 2021