Local Development Setup
This guide covers running the full Cargo stack on your machine: the api backend, the app client, and the backoffice admin console, backed by MongoDB.
Repo Layout
| Directory | What it is | Stack |
|---|---|---|
api/ |
Backend service | Spring Boot (WebFlux, GraphQL, reactive MongoDB) |
app/ |
Client-facing web app (shippers and capacity providers) | Angular |
backoffice/ |
Admin console | Angular |
nginx/ |
Reverse proxy config used in deployment | -- not needed for local dev |
docs/ |
This documentation site | mkdocs (git submodule, see Repo Setup) |
app and backoffice are separate Angular projects that both talk to the same api instance over GraphQL.
Prerequisites
- JDK 25.
api/build.gradlepins the Gradle toolchain to Java 25; if you don't have a matching JDK installed, the Gradle toolchain will download one automatically on first build. - Node.js and npm (
appandbackofficepinpackageManager: npm@11.16.0inpackage.json). - Docker, running. The backend starts a local MongoDB container for you through Spring Boot's Docker Compose support, so you don't need to run
docker composeby hand. - Git, with the shared hooks configured. See Repo Setup for the one-time
core.hooksPathstep that keeps thecargo-docssubmodule in sync.
Backend (api)
1. Configure environment variables
Copy the example env file and fill it in:
cd api
cp .env.example .env
.env is gitignored and loaded automatically on startup (ApiApplication loads it before Spring Boot starts). It is read regardless of which Spring profile is active.
| Variable | Purpose |
|---|---|
MONGODB_URI |
Connection string for MongoDB. The value in .env.example already matches the credentials in compose.yaml (root / secret, database mydatabase), so it works out of the box with the Docker-managed MongoDB. |
CORS_ALLOWED_ORIGINS |
Origins allowed to call the API. Defaults to http://localhost:4200,http://localhost:4201, which already covers backoffice and app. |
JWT_SECRET |
Signs the app's own JWTs (HS256). Must be at least 32 bytes, or the API fails to start with cargo.jwt.secret must be at least 256 bits (32 bytes) for HS256. Generate one with openssl rand -base64 32. |
JWT_EXPIRATION_MINUTES |
Token lifetime, defaults to 1440 (24 hours). |
GOOGLE_CLIENT_ID |
OAuth client ID used to verify Google sign-in tokens. Only needed if you're testing the "Sign in with Google" flow; it must match the client ID baked into app/backoffice's environment.development.ts. Email/password login works without it. |
ADMIN_BOOTSTRAP_EMAIL / ADMIN_BOOTSTRAP_PASSWORD / ADMIN_BOOTSTRAP_NAME |
On first startup, if ADMIN_BOOTSTRAP_EMAIL is set and no admin user exists yet, the API seeds one admin user with these credentials (AuthProvider.PASSWORD). Leave ADMIN_BOOTSTRAP_EMAIL blank to skip seeding. |
MEDIA_STORAGE_TYPE / MEDIA_STORAGE_DIR |
Where uploaded media is stored locally. Defaults to local storage under ./data/media. |
2. Run the API with the local profile
The local Spring profile (application-local.yaml) is what turns on Docker Compose support, so make sure it's active:
./gradlew bootRun --args='--spring.profiles.active=local'
or export SPRING_PROFILES_ACTIVE=local before running. On startup, Spring Boot reads compose.yaml and starts the mongodb container for you if it isn't already running.
The API listens on http://localhost:8080. A few useful endpoints:
- GraphQL:
http://localhost:8080/graphql - GraphiQL (interactive explorer):
http://localhost:8080/graphiql
3. Log in as the bootstrap admin
If you set ADMIN_BOOTSTRAP_EMAIL/ADMIN_BOOTSTRAP_PASSWORD, you can authenticate with the login mutation:
mutation {
login(email: "you@example.com", password: "your-password") {
token
}
}
Run it from GraphiQL, or send it as a normal GraphQL POST request. Use the returned token as a bearer token for subsequent requests.
Frontend apps
Both Angular apps expect the API at http://localhost:8080 in development (see src/environments/environment.development.ts in each project), which matches the default api setup above.
Client app (app)
cd app
npm install
npm start
Serves at http://localhost:4201.
Backoffice (backoffice)
cd backoffice
npm install
npm start
Serves at http://localhost:4200.
Putting it together
For a full local environment, run these three in separate terminals:
| Service | Command | URL |
|---|---|---|
| API | cd api && ./gradlew bootRun --args='--spring.profiles.active=local' |
http://localhost:8080 |
| Backoffice | cd backoffice && npm start |
http://localhost:4200 |
| App | cd app && npm start |
http://localhost:4201 |
MongoDB is started automatically by the API through Docker Compose; you don't need to run it separately.
Troubleshooting
- API fails to start with a JWT secret error.
JWT_SECRETin.envis missing or shorter than 32 bytes. Generate a new one withopenssl rand -base64 32. - API can't connect to MongoDB. Make sure Docker is running before you start the API, and that
MONGODB_URIin.envstill matches the credentials incompose.yaml. - Frontend requests are blocked by CORS. Check that the origin you're serving from is listed in
CORS_ALLOWED_ORIGINS. The default already covers ports 4200 and 4201. - Port already in use.
8080(api),4200(backoffice),4201(app), and27017(MongoDB) all need to be free.