Add some more docs and streamline entrypoint

This commit is contained in:
Mike Conrad
2025-06-11 17:08:21 -04:00
parent 78f32b4384
commit 8327d34708
6 changed files with 52 additions and 3 deletions

View File

@ -2,7 +2,7 @@
This is a practical example of a "basic" fullstack application. This application has been fully containerized to be run locally in development mode as well as for deployment to production. It is composed of the following services: This is a practical example of a "basic" fullstack application. This application has been fully containerized to be run locally in development mode as well as for deployment to production. It is composed of the following services:
- frontend - Basic React app bootstrapped with `yarn create vite` - frontend - Basic React app bootstrapped with `yarn create vite`
- backend - Basic Express server NodeJS backend - backend - AdonisJS API Backend [https://adonisjs.com]()
- database - Postgres database - database - Postgres database
- reverse_proxy - Traefik ingress controller handling reverse proxy for the frontend and backend applications. - reverse_proxy - Traefik ingress controller handling reverse proxy for the frontend and backend applications.
@ -10,5 +10,23 @@ This is a practical example of a "basic" fullstack application. This applicatio
Clone the repo and `cd` into the `compose` directory. Clone the repo and `cd` into the `compose` directory.
```shell ```shell
cp .env.example .env cp backend/.env.example backend/.env
docker compose up
``` ```
The first time you run `docker compose up` it may take a few minutes as Docker will need to build images for the frontend and backend. Also there are some database migrations and seed data that need to happen. Those are handled by `backend/dev-entrypoint.sh`. This is handled for you automatically since it is baked into the image. You will most likely need to run `sudo docker compose up` or add yourself to the privileged `docker` group (`sudo usermod -aG docker YOUR_USER`) in order for `Traefik` to bind to port 80.
![](./homepage.png)
### Proxying and routes
You will notice that the backend is listening on `http://0.0.0.0:3333/` but the frontend is making requests to `/api/`. This is designed to mimic how you would deploy something like this in production where the backend would be behind a reverse proxy. Traefik has some really nice middleware that allows us to easily tell it to route requests destined for `/api` to `/`. The bit that handles that are these labels:
```compose
labels:
- "traefik.http.middlewares.strip-api-prefix.stripprefix.prefixes=/api"
- "traefik.http.routers.backend.rule=Host(`app.docker.localhost`) && PathPrefix(`/api`)"
- "traefik.http.routers.backend.middlewares=strip-api-prefix@docker"
```
We are defining a middleware named `strip-api-prefix` using the built in `strippreffix` Traefik middleware. We are then telling it to remove `/api` from any requests it handles. We then attach that to our backend router.

View File

@ -10,8 +10,12 @@ RUN npm ci
FROM deps AS develop FROM deps AS develop
WORKDIR /app WORKDIR /app
COPY dev-entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
RUN cat /entrypoint.sh
ENV NODE_ENV=development ENV NODE_ENV=development
EXPOSE 3333 EXPOSE 3333
ENTRYPOINT [ "/entrypoint.sh" ]
# Production only deps stage # Production only deps stage
FROM base AS production-deps FROM base AS production-deps

View File

@ -4,6 +4,11 @@ import { faker } from '@faker-js/faker'
export default class extends BaseSeeder { export default class extends BaseSeeder {
public async run () { public async run () {
const usersExist = await User.query().first()
if (usersExist) {
console.log('Database already seeded, skipping...')
return
}
const users = Array.from({ length: 1000 }).map(() => ({ const users = Array.from({ length: 1000 }).map(() => ({
fullName: faker.person.fullName(), fullName: faker.person.fullName(),
email: faker.internet.email(), email: faker.internet.email(),

View File

@ -0,0 +1,23 @@
#!/bin/sh
echo "starting up..."
node ace generate:key
# Check for pending migrations by parsing output
PENDING_MIGRATIONS=$(node ace migration:status | grep -ic 'pending')
# Run migrations only if there are pending ones
if [ "$PENDING_MIGRATIONS" -gt 0 ]; then
echo "Found $PENDING_MIGRATIONS pending migration(s). Running migrations..."
node ace migration:run --force
else
echo "No pending migrations."
fi
echo "Seeding database..."
node ace db:seed
# Start the dev server
node ace serve --watch

View File

@ -8,7 +8,6 @@ services:
volumes: volumes:
- ./backend:/app - ./backend:/app
- node_modules:/app/node_modules - node_modules:/app/node_modules
command: /bin/sh -c "node ace generate:key; node ace migration:run --force && node ace serve --watch"
depends_on: depends_on:
db: db:
condition: service_healthy condition: service_healthy

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB