Thank you for your interest in improving OpenNota. This guide explains how to set up the project, how changes are organized, and what to expect from the review process.
This project follows the Contributor Covenant. By participating, you agree to uphold it.
Follow the Getting started section of the README. In short:
git clone https://github.com/meser1905/opennota.git
cd opennota
cp .env.example .env
pnpm setup
pnpm devYou need Node.js 20 LTS and pnpm 9 or newer. Nothing else: no Docker, no database server, no Redis.
OpenNota is developed in English but serves a Spanish-speaking audience.
- English: all source code, code comments, commit messages, branch names, pull request descriptions and documentation.
- Spanish: all user-facing strings, which live in
apps/web/messages/es.json.
Keeping these separate lets contributors collaborate in English while users see a consistent Spanish interface.
Create a branch off master using one of these prefixes:
feat/for a new feature, for examplefeat/attendance-tracking.fix/for a bug fix, for examplefix/grade-rounding.docs/for documentation only, for exampledocs/api-examples.chore/for tooling, dependencies or maintenance, for examplechore/bump-prisma.refactor/for changes that do not alter behavior, for examplerefactor/grades-service.
Commits follow Conventional Commits. This is enforced by commitlint through a Git hook. The format is:
<type>(<optional scope>): <short summary>
Common types are feat, fix, docs, chore, refactor, test and
style. Examples:
feat(grades): add batch grade entry endpoint
fix(auth): reject refresh tokens after logout
docs(readme): clarify the zero-install prerequisites
refactor(reports): extract report card PDF builder
test(grades): cover weighted average with absences
Keep the summary short and in the imperative mood ("add", not "added").
- Fork the repository and create a branch using the naming convention above.
- Make your change. Keep pull requests focused on a single concern.
- Run the full local check before pushing:
pnpm lint pnpm typecheck pnpm test pnpm build - Push your branch and open a pull request against
master. The pull request template will prompt you for a description, the type of change and a checklist. - Continuous integration runs lint, typecheck, test and build on every pull request. All checks must pass.
- A maintainer reviews the change. Address review comments by pushing additional commits to the same branch.
- The branch name uses a valid prefix.
- Commits follow Conventional Commits.
-
pnpm lint,pnpm typecheck,pnpm testandpnpm buildall pass. - New or changed behavior has test coverage.
- User-facing strings are in Spanish; code, comments and docs are in English.
- Documentation is updated if behavior or setup changed.
- TypeScript everywhere, with strict typing. Avoid
any. - Formatting is handled by Prettier; linting by ESLint. Run
pnpm formatandpnpm lintbefore committing. A pre-commit hook formats and lints staged files automatically. - Validate all external input with Zod schemas from
@opennota/shared. - Keep business logic in NestJS services, not controllers.
The API groups features into NestJS modules under apps/api/src/modules. To
add one, for example attendance:
- Create
apps/api/src/modules/attendance/with anattendance.module.ts, anattendance.controller.tsand anattendance.service.ts. Use an existing module such asevaluationsas a reference. - If the feature needs new persisted data, add the models to
packages/db/prisma/schema.prismaand create a migration withpnpm db:migrate. - Add or extend Zod schemas in
packages/shared/src/schemasand export them from the package. Controllers validate request bodies withZodValidationPipe. - Protect routes with the
@Roles(...)decorator as appropriate. Routes are authenticated by default; mark public routes with@Public(). - Register the new module in
apps/api/src/app.module.ts. - Add unit tests for the service.
The web app uses next-intl. All user-facing text is keyed in
apps/web/messages/es.json.
- Open
apps/web/messages/es.jsonand add your key under the relevant namespace, for exampleevaluations. Keep keys descriptive and the values in Spanish. - In a component, read the string with the
useTranslationshook:const t = useTranslations('evaluations'); return <h1>{t('title')}</h1>;
- Do not hard-code user-facing text in components. Only English appears in code; Spanish lives in the messages file.
The single-locale setup is intentional and ready for more locales: adding
messages/<locale>.json and resolving the locale in apps/web/i18n/request.ts
is all that a second language needs.
- Unit tests use Vitest and sit next to the code they cover, named
*.spec.tsor*.test.ts. Pure logic, such asapps/api/src/modules/grades/grade-calculation.ts, should be tested directly. - End-to-end tests use Playwright and live in the web app. Install the
browsers once with
pnpm exec playwright install, then runpnpm test:e2e. - Run
pnpm testbefore opening a pull request. The end-to-end suite is not run in continuous integration, so run it locally when your change affects user flows.
Welcome. A few tips to get started:
- Read docs/architecture.md and docs/domain-model.md for the big picture.
- Look for issues labeled
good first issue. - Small, focused pull requests are easier to review and merge.
- If you are unsure about an approach, open an issue or a draft pull request to discuss it before investing a lot of time.
- Questions are welcome in GitHub Discussions.