A template that can be used to quickly scaffold a project based on express, knex and typescript.
Buzzwords ...or... What technologies does this project use:
- Docker: Linux container runtime
- docker-compose: command line tool that manages multi-container projects
- PostgreSQL: SQL Database
- Knex.js: Query Builder library
- Mocha & Chai: Testing framework and assertion library
- Nodemon: Node process manager, used for fast development
- TypeScript: JavaScript + Types = <3
- Pug: HTML/XML/Plain Text Template engine
- ESLint: linting library that enforces code style
- Express.js: HTTP server library that provides a middleware-based architecture
- JWT: Json Web Tokens, used for authorization/authentication purposes
- dotenv: library that imports environment variables from a
.env
file - Morgan & Winston: Logging middleware and logging library
- bcrypt: cryptographic library used for hashing
Running the following command will clone this template into my-awesome-project
, install all its dependencies and setup the application
npx degit cdellacqua/express-knex-typescript-template my-awesome-project
cd my-awesome-project
npm i
npm run setup
This app reads its environment variables from a .env file, a .env.example is provided.
The npm run setup
command will copy the content of the .env.example and replace the SECRET variable with a cryptographically-safe random string.
Moreover, the setup script will also rename the package name in both package.json and package-lock.json
Note: never add the .env file to git or any other version control system. It's meant to be a local file with custom configurations relative to the machine where the app runs
Inside the project root directory open a shell and run:
docker-compose up -d
to start the PostgreSQL containernpm run build
to build the typescript source filesnpm run migrate
to run all the pending migrationsnpm run seed
to seed the database with test datanpm run dev
to start the project in development (watch) mode
When you add a new migration you'll need to rebuild the project before running the migrate
script.
When you change the seeds (by adding, removing or modifying files) you'll need to reset the database to its initial state before running npm run seed
again. You can achieve this by
recreating the docker containers (e.g. docker-compose down && docker-compose up -d
) or by rolling back all the existing migrations and redoing them
again (e.g. npm run migrate:down:all && npm run migrate
)
This template provides a set of useful scripts that can be called using the npm run <script>
syntax.
These scripts are:
knex
: knex cli wrapper that runs dotenv/config before instantiating knexcoverage
: runs tests computing code coveragetest
: tests the application using mocha and chaitest:prepare
: prepares the application for the test script (it's invoked automatically by it), for example, by starting and reinitializing the local development databasebuild
: runs the typescript compiler to build the applicationstart
: starts a node process that will execute this packagedev
: starts nodemon in watch mode, this way you can edit your source .ts files without having to rebuild and restart the application manuallylint
: runs eslintlint:fix
: runs eslint with the --fix flagmigrate
: runs all the (compiled) migrations under build/db/migrationsmigrate:up
: runs the first (compiled) pending migrationmigrate:down
: reverts the last (compiled) migrationmigrate:down:all
: rolls back all (compiled) migrationsseed
: runs all the (compiled) seeds under build/db/seedssetup
: runs the setup.js script described belowgen:secret
: regenerates the SECRET inside the .env file
This template includes a docker-compose.yml that is used to startup a PostgreSQL container. The following commands can be used to manage the containers:
docker-compose up -d
: starts the containers in detached mode, so they won't block your shelldocker-compose down
: stops and removes the containers, by default removing also all the data. To enable data persistence you just can uncomment some lines in docker-compose.yml as described in the filedocker-compose exec postgres psql -U default -d default
: logs your shell into the PostgreSQL CLI client of the containerdocker-compose build
: rebuilds the containers, useful if you edit the Dockerfile or to the init.sql script
At the top level directory you can find the following files and folders:
docker
: contains containers definition and configuration filespostgres
: contains Dockerfile that describes a postgres docker image, used to setup a development database
public
: contains static files that will be served to HTTP clients. By default these files are served under/
, without any prefix. In development mode, these files are served by express, but once in production they should be served by another application (e.g. nginx) to maximize performancemain.css
: a simple example css file referenced by layout.pug (see below)
src
: contains all the typescript source codealgebra
: contains some functional-programming related utilitiesfunctions
: contains some functions and function generators that help write less and more expressive code
collection
:index.ts
: contains a set of utility functions that operate on arrays
crypto
:index
: contains some basic cryptographically related functionalitiesurl
: contains functions that enable signed urls and the related express middleware
db
:migrations
: contains knex migration scripts00-create_user_table.ts
: creates a basicuser
table containing common columns, you can extend it or, if your application doesn't need users, you can delete this file
seeds
: contains knex seed files for development and testing purposes00-setup.ts
: includes the application startup code (see below) that enables you to safely call any function/instantiate any class01-user.ts
: contains a simple seed that adds one user to the database99-tear-down.ts
: gracefully stops the application after all seeds have run
index.ts
: exports the knex instance configured using the process.env.NODE_ENV variableutils.ts
: contains a set of useful functions that can be used to easily create common I/O mechanism with the database
email
:index.ts
: exports functions that render and send emails
http
:routes
: contains all the exposed end points of the application, you can choose the structure that better fits your needs. This template provides a simple structure based on multiple express routers that are registered hierarchically, mainly following the directory structureauthenticated
: contains all the routes that are accessible by authenticated users only_middleware.ts
: contains a basic authentication/authorization middleware based on JWTs, you can customize it if you need togoodbye.ts
: example authorized routeindex.ts
: registers the routes of this directory and exports the express router objectuser.ts
: contains a route that can be used by the client to renew its JWT if the one it has is near its expiration
index.ts
: registers the routes of this directory and exports the express router objectuser.ts
: contains an authentication route
error.ts
: a custom Error class that is used to immediately stop a request and return a status code and a message to the clientexpress-app.ts
: contains the express app initialization code, including the registrations of routers and middlewaresserver.ts
: exports a shutdownable (see below) HTTP server already initialized with the express applicationshutdown-server.ts
: adds socket tracking and manages the HTTP shutdown procedurestatus.ts
: exports enums for Http status codesvalidation.ts
: exports a middleware that can be used with express-validator to automatically reject requests that do not pass all the validation steps
i18n
: contains basic internationalization codetranslations
: contains string translations based on localeen.ts
: contains english string translations
index.ts
: exports a translation function generator (i.e. given a locale, it returns a function that translates a string to that locale) and other minor locale-related functionalities
lifecycle
: contains scripts that needs to be executed during the application initialization and tear downindex.ts
: exports the start and stop functions
log
:logger.ts
: configure and exports a logger based on the winston library
promise
: contains promise related functionalitiesparallel.ts
: exports functionalities which help deal with parallelism
runtime
: contains runtime related functionalitiesdelay.ts
: exports functions that helps manage delays between tasksindex.ts
: exports common runtime functionalities
services
: contains all the services of your application. What "service" means to you depends on how much abstraction you want to put in your code, in this template a service is intended as a module containing functions that are needed to communicate with the database or, more in general, that manages the application logicuser.ts
: service that manages basic user logic
types
: contains custom data typescommon.ts
: contains simple custom data types
config.ts
: wraps environment variables in a typed objectindex.ts
: startup the express application and attaches listener to process termination signals to handle graceful shutdown
tests
: contains the tests scripts, based on mocha and chaicrypto
: tests cryptographically related functionalitiessigned-urls.test.ts
: tests signed urls. These are URLs that contains an hash, the hash can then be verified against the URL itself to identify tampering attempts
user
: contains tests about the userauth.test.ts
: tests the default authentication mechanismuser-spy.test.ts
: example test that uses chai-spy to alter modules at runtimeuser.test.ts
: tests some functions that interacts with the user entity in the db
_hooks.ts
: setup file that prepares the application for testinghello.test.ts
: tests the example routesjsconfig.json
: helps the IDE find the typings needed for the testing library
views
: contains all thepug
files used to server-side render HTML pagesemails
: contains the email viewscommon
: contains sharedpug
markuplayout.pug
: a basicpug
template that is extended by the other viewsstyle.pug
: contains the style definition that will be inlined by pug directly in the html tags
email-verification.plain.pug
: a plain text version of an example email address verification requestemail-verification.pug
: an HTML version of an example email address verification request
pages
: contains the page viewscommon
: contains sharedpug
markuplayout.pug
: a basicpug
page with placeholders for head and main content
403.pug
: basic 403 error page you may customize404.pug
: basic 404 error page you may customizehello-world.pug
: example page that extends layout.pug and contains a paragraph
.editorconfig
: configures basic editor settings like indentation, end of line format and end of file newline.env
: contains your (local) environment variables.env.example
: template for .env.eslintignore
: contains a list of patterns/files that eslint should ignore.eslintrc.js
: contains the eslint configuration.gitignore
: describes what should and shouldn't be committed to git.mocharc.js
: initializes the testing library mochadocker-compose.yml
: describes the docker project (which by default consists of a postgres container)knexfile.js
: contains the database connection settings for different environment (production, development, staging and test)nodemon.json
: specifies some rules for nodemonpackage-lock.json
: keeps track of the exact version of all the installed dependenciespackage.json
: describes this project, its dependencies and provides a set of useful scriptsREADME.md
: this filescripts
setup.js
: script that creates a .env file copying the content of .env.example and replaces the "SECRET" variable with a random string. It also renames the package (in package.json and package-lock.json) to reflect the name of the root directory of the projectgensecret.js
: script that generates a random "SECRET" in the .env file
tsconfig.json
: contains the configuration for the typescript compiler
When you deploy the application you have to take care of a few things:
- environment variables: on your production (or staging) machine you'll need to setup environment variables, these can be set using a local .env file as the one you have on your development machine or by setting environment variables in your system (globally) or in the process manager that spins up the application (e.g. systemd, docker-compose, ...)
- database migrations: when you deploy a new version of your application, you might also need to run some database migrations. All you'll have to do before restarting the application is running
npm run migrate
on your target machine
If you look for the word TODO
in this project you'll find some places where your intervention could be needed to better fit the needs of your new project. Feel free to
modify anything you want!