This is an opinionated boilerplate for an api project. It involves writing mostly safe Typescript code, using a postgres database and have good integration testing.
It's also dockerized and expected to be ran in a kubernetes cluster.
Routing is powered by routing-controllers over Koa.
Conf:
Dependency injection is done by typescript-ioc. We inject the pg pool with this tool into controllers.
You're encouraged to write services to handle operations between your database layer (which is handled by zapatos) and your controller layer which is handled by routing controllers. Controllers get way cleaner this way.
Zapatos is discussed next
The database layer is handled by a few tools
First, migrations. You can write plain .sql
migration files and leave them in
the migrations
directory. You can read the documentation on how to use it.
Pretty simple.
There are two ways to apply migrations
$ yarn migrate # for rootless docker access
$ yarn prod:migrate # inside the web container
Lastly, for the database, zapatos requires us to parse the database schema to generate types. The same goes for these scripts:
$ yarn zg # rootless docker: mnemonic is zapatos-generate
$ yarn zapatos # inside the container
Any of those should get your schema into ./src/zapatos
.
Any time you wish to run queries with zapatos, you can either
@InjectValue('db')
pool: Pool
Where Pool
comes from pg
, and:
const pool: Pool = Container.getValue('db')
The first example you'd use it in a container or service class. Any class, really. The second way you'd use it where you cannot inject it. In tests or in bull queues, for example
Logging is provided by pino. There's a small utility in the logger file where you can get a logger for anything. Use it like so:
private logger = getLogger('my-logger-name')
Tests are inside the tests
directory. I try to follow a principle as much as I can:
Write tests, not too many, mostly integration.
To make requests to the testing server, just go ahead and use axios. The base url is set to the server.
If you, for some reason, need to mock axios inside your tests, let me know and let's solve it.
You might want to use a codegen tool for your schema if your frontend is also written in Typescript, and maybe you also want SwaggerUI to explore your api.
There's a default route in the index controller
called schema
where you can expose your app schema and parse it.
By default, Swagger UI is only available in development, you can check it out in the swagger ui conf file.
You might also want to check out routing-controllers-openapi in case you need to extend your schema.
Compilation is just tsc
. I wanted as much reliability on the output as I
could. If you think something else is better, please let me know.
In development, nodemon is used to recompile on file changes.
You'll notice there's two env packages. dotenv
and envalid
. Also, there's a
.env
file.
Here are the guidelines for sensitive variables:
.env
file in the repo is used only for developmentdocker-compose
should be only used for developmentI'll emphasize this as much as I can: The .env file is not meant to be used in
production. Instead, what this app does is read everything from
environment variables. That is also why docker-compose
is only meant to be
used in development. I'll discuss more about docker in the next section.
So, in order to configure your app for development, it's fine to add env
variables to the .env
file and docker-compose.yml
, maybe even exposing them.
For production, what you want to do is run your image with appropriate env variables, so that the container can pick them up.
App is dockerized to avoid any surprises in production. There's a
docker-compose
file to speed up development that currently includes a
database, but there could be more stuff there, like redis and mail servers.
We'll discuss deployment next
Firstly, if you need (for whatever reason) to deploy your application using
docker-compose
, then DO NOT use the same docker-compose.yml
file that's
included in this repo. Instead, make another one so you don't expose sensitive
data in your repo.
That said, the recommended way of deploying is the following
Note that there's no supervisor process for the yarn start
script. This is intentional because this app is meant to be deployed with Kubernetes.
However, you could easily change the production entrypoint to use something like pm2 if you're not using kubernetes.
yarn test --watchAll
works or notPlease feel free to open an issue