- tsx
- pkgroll
- eslint
- prettier
- typescript
- vitest
- prisma
- mongodb
- zod
- zod-prisma-types
- firebase auth
- gcp
- digitalocean
- helmet
- cookie-parser
A simple node/express backend api template.
This project requires node.js to be installed. This project uses volta to manage node versions.
To install volta run the following command in the terminal.
curl https://get.volta.sh | bash
This will require a mongodb database to be setup. You can set one up at https://cloud.mongodb.com/
This project also uses firebase auth for authentication. You can set one up at https://firebase.google.com/ and deployment is handled with DigitalOcean. See the Firebase Auth section for more info on setting up your credentials and project.
https://www.typescriptlang.org/docs/handbook/esm-node.html
This project has been setup to use ESM Node. This allows us to use ES6 imports in Node.
This uses tsx as a dev server and pkgroll to bundle and build the project.
Note: Prisma does not support ESM by default and have an open issue -- looking to migrate this to another ORM (drizzle) for ESM support.
"the Prisma client relies on globals like __dirname
that used to be part of Node but in ESM have been moved to import.meta.dirname. See: https://stackoverflow.com/questions/46745014/alternative-for-dirname-in-node-js-when-using-es6-modules"
As a result of this it's best to stick to the .cjs
format when running in production.
# install dependencies
npm i
# start the dev server
npm run dev
# view it running on localhost
curl localhost:4000
Be sure to configure the .env file with the correct values and make sure you have a mongodb database setup and firebase application credentials.
This project uses vitest for testing.
- run the unit tests with
npm run test
It's also recommended to install the vitest extension for vscode.
The GOOGLE_APPLICATION_CREDENTIALS
env variable is mapped to the docker container from your home directory. This is required for the firebase admin sdk to verify the token and decode the claims. see the Firebase Auth section for more info on the service account key file and Google Application Credentials.
docker compose up -d
# build the app
npm run build
# build with docker
docker build . --tag node-express
# or to build with a specific platform
docker build . --tag node-express --platform linux/amd64
# start the docker container
docker run -d -p 4000:4000 node-express
# view it running on localhost
curl localhost:4000
When building with Docker locally it will require the service account key to be set; this is because the Dockerfile is copying the service account key file into the image. See the Firebase Auth section for more info on the service account key file and Google Application Credentials.
This is setup to use MongoDB with Prisma. If you want to use a different database you can change the db provider in Prisma schema and use a different Database.
Note: when using Prisma the MongoDB database connector uses transactions to support nested writes. Transactions require a replica set deployment. The easiest way to deploy a replica set is with Atlas. It's free to get started.
https://www.prisma.io/docs/concepts/database-connectors/mongodb
create a .env file in the root of the project and copy the contents of .env.example into it.
You can replace DATABASE_URL
with your mongodb connection string whether that be cloud or locally hosted.
run the seed script to seed the db the first time.
npx prisma db seed
# after updating the model you want to generate the schema
npx prisma generate
Aliases can be configured in the import map, defined in package.json#imports.
see: https://github.com/privatenumber/pkgroll#aliases
This project uses JWT bearer token for authentication. The claims, id and sub must be set on the token and the token can be verified and decoded using the configured auth provider.
You can sign in an retrieve user tokens without having to use the client sdk.
See the Firebase REST API docs for more info on how to sign in with email and password.
Sign in with email and password is supported out of the box with Firebase Auth.
# sign in with email and password by posting
https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=[API_KEY]
# refresh token
https://securetoken.googleapis.com/v1/token?key=[API_KEY]
How permissions work.
A resource will have a permission level. A user will have a role/claim.
Routes will have their permission level defined in ./src/helpers/permissions.ts
When a user makes a request to a route the route will check the user's role/claim against the permission level of the resource.
Permissions for the user can be set using the /auth/set-permissions endpoint which sets the database userId and claims on the firebase user. The token will have access to this data to verify the user's permissions.
- Owner - Route can only be accessed by the owner of the resource. Defined by the id of the resource being accessed matching the id of the user making the request.
- User - Can access all resources with user permissions.
- Admin - Can access all resources.
A claim is defined when the user is created which defines the user's role and permissions level.
- User - default user permissions
- Admin - admin permissions
This project uses Firebase Auth for authentication. The firebase admin sdk is used to verify the token and decode the claims.
Because we are not running in a google environment we need to initialize the firebase admin sdk with a service account key file. see the Firebase Admin SDK docs for more info.
This requires a service account key file at $HOME/gcloud.json
. The GOOGLE_APPLICATION_CREDENTIALS
env variable must be set to the path of the service account key file.
You can create a service account from the firebase console and place in your home directory and set the environment variable like so:
export GOOGLE_APPLICATION_CREDENTIALS=$HOME/gcloud.json
You can also set this in the .env
file but if you have set the env variable in your shell it will take precedence.
When running in CI/CD the service account key file is stored as a secret and the env variable is set in the Dockerfile using the copied service account key file fron secrets.
A docker image can be built and deployed to a container registry. We can configure DigitalOcean to deploy the image once the registry updates using their App Platform
The following secrets will need to be added to Github Actions for a successful deployment to DigitalOcean.
DIGITALOCEAN_ACCESS_TOKEN
https://docs.digitalocean.com/reference/api/create-personal-access-token/REGISTRY_NAME
eg registry.digitalocean.com/my-container-registrySERVICE_ACCOUNT_DEV
The GOOGLE_APPLICATION_CREDENTIALS Service Account file for development env.SERVICE_ACCOUNT_PROD
The GOOGLE_APPLICATION_CREDENTIALS Service Account file for production env.IMAGE_NAME_DEV
the name of the DEV image we are pushing to the repository egexpress-api
it will be tagged with the latest version and a github sha.IMAGE_NAME_PROD
the name of the PROD image we are pushing to the repository egexpress-api
it will be tagged with the latest version and a github sha.