I ask that you keep these three guiding principles in mind when contributing to Rodeo (from most to least important):
- KISS: don't overengineer your code. See also: DTSTTCPW, YAGNI
- Mobile-first: test on a 320px wide viewport first while you are working on the frontend. This doesn't mean to neglect the desktop layout; just make sure it works on smaller screens first and scale up (because we often forget if we do it the other way around).
- Progressive enhancement: if it doesn't cost anything, then use the browser's native ability to submit form actions so users without access to JavaScript can still use your feature.
To run Rodeo locally, you need NodeJS and PostgreSQL. Below are installation instructions for each operating system.
Previous documentation recommended using Homebrew to install NodeJS and PostgreSQL. However, this is no longer recommended as it can lead to issues with permissions and PATHs. Instead, we recommend using [NVM] (Node Version Manager) for install management and using v20 for NodeJS. Install using NVM (Node Version Manager) and Homebrew (package manager for macOS) Run the following commands in your terminal:
brew install nvm nvm install 20 nvm use 20
This will install NVM and NodeJS v20. Next, install PostgreSQL v15 and add the PostgreSQL utilities to your PATH so you can use the createdb utility. Finally, run brew services start postgresql@15 to boot PostgreSQL as a background process. brew install postgresql@15 echo 'export PATH="/usr/local/opt/postgresql@15/bin:$PATH"' >> ~/.zshrc
brew services start postgresql@15 createdb rodeo-development
Help requested for PostgreSQL and NodeJS on Windows setup instructions!
If you're using Linux, you can probably figure this out yourself.
Once you've installed PostgreSQL and NodeJS, the next steps are the same for every OS. First, create a database named rodeo-development
with createdb rodeo-development
. Next, create a file named .env
and put the following string in it, substituting username and password as appropriate. Most installations set the default username to either your operating system username or postgres
and a blank default password. If this doesn't work you can consult your package manager's documentation.
DATABASE_URL=postgres://username:password@localhost:5432/rodeo-development
DIRECT_URL=postgres://username:password@localhost:5432/rodeo-development
Now run npm install
to download our dependencies, npx prisma migrate dev
to sync up your database to our schema, and npx prisma db seed
to insert some dummy data. Finally, you can start the development server with npm run dev
and log into the sample accounts by following the instructions at the top of the the seed script.
- Use the
import/export
ESM syntax instead ofrequire
CommonJS syntax - Avoid default exports (stick with named)
- Prefer
async/await
over Promises - Always comment and annotate return type for all tRPC endpoints
- Compare enums to strings instead of enums
- When you change the Prisma schema, update the seed script as well so we always have an easy way to generate fake data
Here are some tutorials to each key component in our stack, which is roughly ordered from highest to lowest level:
- Svelte is a frontend framework that generates the HTML and provides a way to write reusable UI components.
- SvelteKit is an application framework that handles routing, server-side rendering, and all the "boring stuff" in web app development.
- Zod is a TypeScript type inference and validation library that prevents invalid data from reaching our API.
- tRPC is a backend library for full-stack TypeScript apps that enables us to write an API that can be queried in a type-safe manner, as if our endpoints were just regular TypeScript functions.
- Prisma is a suite of tools for working with databases based on a declarative Prisma schema. Prisma Client is an ORM that generates a type-safe API for interacting with your database and Prisma Migrate enables us to update our database schema in a reproducible way.
To get acquainted with the codebase, here's how to add a silly Easter egg setting to convert the homepage into sPoNgEbOb cAsE.
- Clone the repository and make a new branch with
git checkout -b spongebob-text
. - Update the schema: add a boolean field named something descriptive like spongebobCase to the Settings model in
prisma/schema.prisma
and give it a default value of false. - Perform the migration: Run
npx prisma migrate dev
and give the migration a sensible name. Refer to the Prisma Migrate documentation to learn more about migrations. You must also restart the development server by pressing CTRL-C and runningnpm run dev
again. This is very important: the development server can usually pick up changes and automatically hot reload, but after performing a migration is a case where you must restart the server manually. - Update the backend: See the
update
functionsrc/lib/trpc/routes/settings.ts
. Since it doesn't do anything special besides saving any new settings to the database, the only thing you need to do here is update the Zod schema for Settings to include your new field. Refer to the Zod documentation to see what validators are available. Since SpongeBob Case should be visible to everybody, you must also add it to thegetPublic
function. - Update the frontend: In
src/routes/admin/+page.svelte
, add a<Toggle>
for your new setting under the main<form>
. The key part is to set thename
prop of the<Toggle>
to match the name in the Prisma schema and itschecked
prop to{data.settings.spongebobCase}
. - Update the form action: In the
settings
action insrc/routes/admin/+page.server.ts
, add a new key-value pair to the call totrpc(locals.auth).settings.update
that sets your setting's name to the value of the<Toggle>
, which can be retrieved withformData.get('spongebobCase') === 'on'
.
Now if you switch the toggle and press save, it should now persist between refreshes. Let's take a break now to look at the bigger picture of data flow in a SvelteKit app, beginning with loading the admin panel and ending with toggling the SpongeBob Case setting:
- When a user requests a page, SvelteKit simply calls the
load
function in the correspondingpage.server.ts
and passes its return value as a special variable nameddata
inpage.svelte
. SvelteKit uses filesystem-based routing, so the/admin
route corresponds to the files insrc/routes/admin
. Insrc/routes/admin/+page.server.ts
, theload
function invokes thegetAll
endpoint insrc/lib/trpc/routes/settings.ts
, which reads the settings from the database. - In
src/routes/admin/+page.svelte
, the<Toggle>
reads the state of the setting by taking the value ofdata.settings.spongebobCase
. - When the user presses the save button, the contents of the form are submitted to the action corresponding to its
action
attribute. This<form>
hasaction="?/settings"
so thesettings
action insrc/routes/admin/+page.server.ts
is run, which can read the values of the inputs by callingFormData.get()
with thename
of the input as a parameter. It can then call tRPC to save the settings to the database.
- Now, in order to apply SpongeBob case to the homepage, you can transform
data.settings.homepageText
in the<script>
section ofsrc/routes/+page.svelte
by alternating the case of each character:
if (data.settings.spongebobCase) {
data.settings.homepageText = data.settings.homepageText
.split('')
.map((char, i) => (i % 2 === 0 ? char.toLowerCase() : char.toUpperCase()))
.join('');
}
- Finally, you are ready to make a merge request. Run
git add .
andgit commit
to save your work. Committing will run a series of pre-commit hooks that check for compile errors, lint for style issues, and checks that you've created the necessary migrations. Fix any issues that prevent you from committing and rungit push -u origin spongebob-text
to create a new branch on GitLab. Then open a new merge request. - QA will then review your work and leave comments on your GitLab. After addressing any issues, QA will approve and merge your request. Congratulations! You just made your first contribution to Rodeo.
Rodeo is a SvelteKit app that is built on top of a TRPC backend that uses the Prisma ORM to query a PostgreSQL database. The database schema, migration history, and seed script are located in the prisma
directory. The backend is located in the src/lib/trpc
directory, which contains a couple of TRPC boilerplate files and a routes
folder, which is where the actual endpoints are located. The frontend is located in src/routes
. SvelteKit uses filesystem-based routing, so each folder corresponds to an actual route and contains a +page.svelte
file and a +page.server.ts
file. +page.svelte
contains all the HTML, CSS, and JavaScript for a page, while the job of +page.server.ts
is twofold: it exports a load
function that fetches all the data the frontend needs from TRPC, and handlers for any forms on the page. Reuseable components in pure Svelte go in src/lib/components
.
Every commit to the master branch is deployed to https://rodeo-nightly.freetailhackers.com. In addition, every branch gets its own preview deployment that can be accessed through Deployments > Environments. To deploy to production, a user with at least Maintainer access that was NOT the author of the commit must promote a preview deployment from the Deployments > Environments tab.