Thanks for your interest in contributing! This document explains how to set up the project locally, the conventions we follow, and how to get a change merged.
By participating in this project you agree to abide by our Code of Conduct.
- Ways to contribute
- Project setup
- Repository layout
- Coding conventions
- Commit messages
- Submitting a pull request
- Reporting bugs
- Suggesting features
- Adding a new language
- Report bugs or unexpected behavior
- Suggest or design new features
- Improve documentation (this file included!)
- Fix bugs or implement features from open issues
- Add or improve translations
- Improve test coverage
If you're not sure whether a change is wanted, open an issue first to discuss it before investing time in a pull request.
See the README for full setup instructions (database, environment variables, running the client and server). In short:
# server
cd server
npm install
npm run dev
# client (in another terminal)
cd client
npm install
npm run devThis is an npm-workspaces-free monorepo with two independent Node projects:
client/— Vue 3 + Vuetify 3 SPA (Vite, TypeScript)server/— Express REST API (TypeScript, PostgreSQL)
See the Architecture section of the README for how the code is organized inside each and how they communicate.
- TypeScript everywhere. Avoid
anywhere a real type is easy to express; existing//@ts-ignoreusages in older code are not a license to add new ones. - Follow existing patterns. The server is layered
types/ → repositories/ → services/ → controllers/ → routes/:types/<resource>.tsholds the interfaces a repository returns;repositories/<Resource>Repository.tsis data access (SQL or an external API call) behind plain exported functions takingPool | PoolClientas the first argument;services/<Resource>Service.tsis business logic, throwing aDomainErrorsubclass (seeserver/src/errors/DomainError.ts) for expected failures and calling only repositories, never Express types;controllers/<Resource>Controller.tsis thin HTTP↔service glue that mapsDomainErrortores.status(err.httpStatus);routes/<Resource>Route.tsshrinks to wiring (router.verb(path, middleware, controller.method)) and stays registered inRoutes.ts. New backend resources should follow this same split, mirroring the existing resources (books, authors, categories, ...). New frontend features should follow theview/controller/service/modelsplit already used for existing pages. - Keep the client thin. Business logic and data access belong in the server; the client should call the REST API rather than talking to the database or external APIs (Google Books/Open Library) directly.
- Security-sensitive code (auth, password handling, session/cookie logic,
anything touching
AuthMiddleware.tsorAuthRoute.ts) should be changed carefully and called out explicitly in your PR description. - Lint: the server has a
tslintconfig (npm run lintinserver/).vue-tsc --build(npm run type-checkinclient/) should pass with no new type errors. - Don't commit
.envfiles, real credentials, or personal data/book covers used only for local testing.
Write short, imperative commit messages describing what changed (e.g.
Add ISBN lookup fallback to Open Library, Fix stock status not updating on return). Group unrelated changes into separate commits/PRs where practical.
- Fork the repository and create a branch from
main:git checkout -b my-feature - Make your change, following the conventions above.
- Run the relevant checks:
cd server && npm run lint cd client && npm run type-check
- Push your branch and open a pull request against
main. Describe:- What the change does and why
- How you tested it (steps, screenshots for UI changes)
- Any schema changes (see below) or breaking changes
- Be responsive to review feedback. Small, focused PRs are easier to review and merge quickly than large ones.
If your change requires a schema change, add it to both:
assets/db/databaseSchema.sql— always current, the only thing a fresh install runs.assets/db/upgrade/X.Y.Z.sql(orX.Y.Z/N.sqlif that version needs more than one file) — upgrade file for existing installs, named after the version it ships in. Seeassets/db/upgrade/README.mdfor the full convention.
Treat a file under assets/db/upgrade/ as append-only once it's part of a
release — mention the new/updated file in your PR description.
Open a GitHub issue with:
- A clear description of the problem and the expected behavior
- Steps to reproduce (and, if relevant, sample data/ISBN)
- Environment details (OS, Node/PostgreSQL versions, browser)
- Relevant logs or console/network errors, with any secrets redacted
For security vulnerabilities, do not open a public issue — see SECURITY.md instead.
Open an issue describing the problem you're trying to solve (not just the solution) so it can be discussed before implementation starts. This helps avoid wasted effort on features that don't fit the project's direction.
Check docs/ROADMAP.md first — it lists feature ideas that are already wanted but don't have anyone working on them yet.
UI labels live in the app_languages / app_labels tables (see
assets/db/databaseSchema.sql) and are loaded by the client's Vue I18n setup in
client/src/plugins/i18n/. To add a language:
- Insert a row into
app_languagesfor the new language code. - Add the corresponding
app_labelsrows for every existingcode, translated. - Add translated Markdown files for the
/docshelp pages (seeclient/src/views/docs/). - Verify the language selector in Settings picks up the new locale.
Thank you for contributing to Vaultisse!