Full-stack unopinionated express powered javascript framework that :
Uses | For |
---|---|
typescript tsx |
Static typing and on-demand compilation for the entire codebase |
react |
Declarative and component-based user interface development |
vite |
HMR and browser auto-reload on file change during development |
nodemon |
Server auto-restart on file changes during development |
dotenv |
Loading environment variables from configuration files |
jose cookie-parser |
Managing stateless client-side sessions with JSON web tokens |
formidable |
Handling file uploads and multipart form data |
esbuild |
Bundling server-side code into builds that run on any modern node.js version |
vite postcss |
Bundling client-side code into builds that run in any TLSv1.2+ compatible browser |
jest |
Running unit tests for server-side business logic |
testing-library |
Running unit tests for individual react components |
puppeteer jest-puppeteer |
Running end to end tests through browser automation |
I designed this project to acquire a basic understanding of how different things work together in a self-contained development solution.
β The idea is to create a full-stack development framework following these principles :
-
π Remain as generic as possible (no default integration of any framework or library except react).
-
π Remain as manageable as possible by relying on a single package.json file.
-
π Silo the back-end and frontend during development - vite server and express run in separate processes.
-
π Provide extensive capabilities for writing tests (unit, components and e2e tests).
-
π Provide typescript support across the entire codebase.
-
π Create builds that can be seamlessly packed into a Docker image.
β DX is guaranteed to be smooth and enjoyable.
β All the dependencies included by default are the go-to modules of the ecosystem for their respective usages.
β Scaffold your project and integrate any additional dependency you need in an incremental, controlled way.
Required software | Recommended version |
---|---|
Linux | Debian 12 bookworm |
GNU Bash shell | 5.2.15 |
Openssl | 3.0.11 |
Node.js | 20.0.9 |
NPM | 10.2.3 |
Docker | 25.0.2 |
Puppeteer compatible browser | google chrome / puppeteer defaults |
-
β οΈ Running puppeteer on linux requires dependencies usually shipped with any desktop environment. -
β οΈ Installing a desktop environment is therefore recommended (I use xfce).
- Use the following commands to scaffold a new project :
# clone the repository using degit
npx degit https://github.com/mulekick/vittel.git myproject
# cd into your project's folder
cd myproject
# clean install dependencies (important)
npm ci
- Optional : create a new key pair for the server (do not change the command arguments) :
# create a private key
openssl ecparam -param_enc named_curve -name prime256v1 -genkey -noout -outform PEM -out .server.key
# create a self-signed certificate
openssl req -x509 -key .server.key -new -outform PEM -out .server.crt -verbose
# use the newly created key pair in development or production :
# - open a dotenv config file from the .env.files directory
# - change the value of the APP_PRIVATE_KEY variable to the contents of .server.key.
# - change the value of the APP_X509_CERT variable to the contents of .server.crt.
# - save the dotenv config file
-
β οΈ The server uses the key pair to sign / verify the JWTs and for HTTPS (if enabled). -
β οΈ If you want to do step 2, I assume you are comfortable with TLS and key pairs management. -
β οΈ If you're not, dummy key pairs are provided in the .env files so HTTPS and JWTs work out of the box. -
β οΈ In those circumstances, seek assistance on the matter prior to pushing anything in production.
.
βββ .env.files
β βββ .env.development # dotenv config file (development)
β βββ .env.production # dotenv config file (production)
βββ .vscode
β βββ launch.json # vscode debug configurations (dev, build and tests)
βββ dist
β βββ *.* # react app + express server build
βββ dist.test
β βββ *.test.ts # end to end tests files
βββ node_modules
β βββ *.* # ...
βββ src
β β
β βββ client # ====== react app source files =======
β β βββ app
β β β βββ *.tsx # react components
β β β βββ *.ts # non react code (helpers etc)
β β β βββ *.test.ts # unit / react components tests
β β βββ public
β β β βββ *.* # statically served files (served from / in production mode)
β β βββ scss
β β β βββ *.scss # scss files for the react app
β β βββ static
β β β βββ *.* # assets to include in the build (use named imports)
β β βββ index.html # main html page (rollup entrypoint)
β β
β βββ server # ==== express server source files ====
β β βββ helpers
β β β βββ *.ts # business agnostic code
β β βββ middlewares
β β β βββ *.ts # express middlewares (implement business logic)
β β β βββ *.test.ts # unit tests for the business logic
β β βββ routes
β β β βββ routes.ts # main express router (mounted on VITE_SRV_ENTRYPOINT)
β β β βββ *.ts # express routers to import in routes.ts
β β βββ uploads
β β β βββ *.* # uploads folder
β β βββ config.ts # express server config file
β β βββ server.ts # main express server file
β β
β βββ interfaces.ts # === typescript types / interfaces ===
β
βββ .browserslistrc # browserslist queries for babel, autoprefixer and vite
βββ .eslintrc.json # eslint config for typescript and react / jsx support
βββ .postcssrc.json # postcss plugins used for development and build
βββ babel.config.json # react / typescript support for babel-jest
βββ Dockerfile # bundle the app and server into a docker image
βββ jest-puppeteer.config.json # puppeteer config for e2e tests
βββ jest.config.json # transforms to apply before running tests
βββ nodemon.json # nodemon config
βββ package.json # dependencies for the react app and the express server
βββ tsconfig.json # typescript config
βββ vite.config.js # vite config + entrypoints for the rollup build process
-
β οΈ The build process packs all the react app code as well as its dependencies into a self-sufficient bundle. -
β οΈ As a result, it is important to install react app dependencies as dev dependencies. -
β οΈ The docker image will be optimized in that it will only embark dependencies needed by the express server.
Command | Usage |
---|---|
npm run dev |
Start the project in development mode HMR and browser auto-reload are enabled Vite proxies requests to express |
npm run list |
List all files for the current typescript codebase |
npm run typecheck |
Typecheck the entire project against the current typescript configuration |
npm run lint |
Validate the entire project against the current eslint configuration |
npm run test:unit |
Run unit tests and components tests |
npm run test:cover |
Run unit tests and components tests and print coverage report |
npm run build |
Build the react app and the express server |
npm run test:e2e |
Start a puppeteer automated browser and run the end to end test suite |
npm run prod |
Start the project in production mode Express serves the app bundle |
npm run docker:build |
Create a docker image and packs the build files and the production environment in it |
npm run docker:up |
Start an interactive container from the image Container's port APP_PORT is mapped to the corresponding host port |
npm run docker:down |
Stop the container |
-
β οΈ HTTPS is disabled by default, it can be enabled by setting theAPP_ENABLE_HTTPS
variable to"true"
. -
β οΈ JWTs won't work in Firefox when starting the project with HTTPS disabled. -
β οΈ When starting in development mode, the vite dev server listens at${ VITE_HOST }:${ VITE_PORT }
. -
β οΈ When starting in production mode, the express server listens at${ APP_HOST }:${ APP_PORT }
.
- All environment variables can be configured in the dotenv config files.
Variable | Usage |
---|---|
VITE_HOST |
Vite dev server host |
VITE_PORT |
Vite dev server port |
VITE_SRV_ENTRYPOINT |
Server API root route |
APP_HOST |
Express server host |
APP_PORT |
Express server port |
APP_ENABLE_HTTPS |
HTTPS enabled / disabled |
APP_UPLOAD_DIR |
Server file upload directory |
APP_MAX_UPLOAD_SIZE |
Max size allowed for uploads |
APP_PRIVATE_KEY |
Server private key |
APP_X509_CERT |
Server certificate |
APP_COOKIE_NAME |
Cookie holding the JWT |
APP_TOKEN_VALIDITY |
JWT validity duration in seconds |
β οΈ Do not touch any variable that's not in this list unless you really know what youre doing.
Module | Usage |
---|---|
cookie-parser |
parse cookies from HTTP requests header |
cors |
serve or reject cross origin requests |
dotenv |
load server environment variables |
express |
node.js web server framework |
formidable |
handle multipart data and file uploads |
helmet |
add security-related headers to HTTP responses |
jose |
JSON web tokens javascript implementation |
morgan |
HTTP logger for express.js |
Module | Usage |
---|---|
@babel/preset-react |
React and jsx syntax support for babel |
@babel/preset-typescript |
Typescript support for babel |
@fortawesome/fontawesome-free |
Fontawesome free icons package |
@fortawesome/fontawesome-svg-core |
Core svg library for fontawesome |
@fortawesome/free-brands-svg-icons |
Free fontawesome icons (brands family) |
@fortawesome/free-regular-svg-icons |
Free fontawesome icons (regular family) |
@fortawesome/free-solid-svg-icons |
Free fontawesome icons (solid family) |
@fortawesome/react-fontawesome |
Fontawesome 5 react component using svg with js |
@jest/globals |
Allow explicit imports for jest primitives |
@mulekick/eslint-config-muleslint |
Mulekicks's base JS / Node ESLint configuration |
@mulekick/pepe-ascii |
Used as an example of client-side module bundling |
@testing-library/jest-dom |
Custom matchers for react components testing |
@testing-library/react |
React DOM testing utilities |
@testing-library/user-event |
Fire events the same way the user does |
@types/(whatever) |
Typescript type definitions |
@typescript-eslint/eslint-plugin |
Typescript specific linting rules for eslint |
@typescript-eslint/parser |
Typescript support for eslint |
@vitejs/plugin-legacy |
Enable legacy browsers support in vite.js builds |
@vitejs/plugin-react |
The all-in-one Vite plugin for react projects |
autoprefixer |
Postcss plugin that adds vendor-specific prefixes to CSS rules |
babel-plugin-transform-import-meta |
Babel transforms import.meta into legacy code in node.js |
eslint |
Identify and report patterns found in ECMAScript code |
eslint-plugin-react |
React specific linting rules for eslint |
eslint-plugin-react-hooks |
Enforces the rules of hooks in react functions |
globals |
Global identifiers from different JavaScript environments |
jest |
Delightful javascript testing |
jest-environment-jsdom |
DOM test environment for jest |
jest-puppeteer |
Jest preset that enables end-to-end testing with puppeteer |
nodemon |
Watch server files and auto restart on file change |
postcss |
A tool for transforming CSS withΒ JavaScript |
puppeteer |
High-level API to control Chrome/Chromium over the DevTools Protocol |
react |
Javascript library for creating user interfaces |
react-dom |
React entry point to the DOM and server renderers |
sass |
Auto-compile SCSS files to CSS in vite.js builds |
terser |
Required for minification during the vite.js build process |
tsx |
TypeScript Execute: Node.js enhanced to run TypeScript & ESM |
typescript |
Bring static typing to javascript code |
vite |
Next generation froontend tooling |
vite-plugin-webfont-dl |
Extracts, downloads and injects fonts during the build |
- This project is at its third major iteration (it started with plain javascript, then react, now typescript).
- It is a case of having done this before the new react docs came out :