Skip to content
Getting started

Getting started

Install Neatship, start the database, the API server, the web app and the background worker on your computer, and open the app as a sample user.

By the end of this guide, the whole app runs on your computer: the web app, the API server, the background worker and a database filled with sample data. You don't need a Clerk account or a Stripe account for this. You can add them later.

Tip: Prefer to be guided? Open Claude Code in the repository folder and type /start-here. It explains the project, runs these steps with you and checks each one. With another assistant, ask it to follow .claude/skills/start-here/SKILL.md.

What you get

Neatship is one repository that holds several packages (a package is a folder with its own code and its own settings):

PackageWhat it is
packages/app-frontThe web app your customers use (React)
packages/app-serverThe API server (NestJS, GraphQL, PostgreSQL) and its background worker
packages/app-sharedTypes and helpers used by both the web app and the server
packages/app-uiThe design system: colors, fonts, spacing, components, icons
packages/app-websiteA landing page and these docs, prerendered for search engines
packages/app-lint-rules17 custom code checks written for this starter
packages/app-e2e-testingEnd-to-end tests: a browser clicks through the real app
packages/app-dockerDocker files: local database and Redis, and the production stack
packages/app-utilsSetup, check, production and backup scripts

The app already has sign-in, workspaces (one per customer organization), settings pages, a billing page, English and French, and one complete example feature called "Projects". You copy that example to build your own features. Everything production needs is there too (rate limits, logs, alerts, nightly backups, HTTPS): when you are ready, Deploy puts it on a server and Security ends with the checklist to follow before your first real customer.

The repository also holds a rulebook for AI assistants: CLAUDE.md (also readable as AGENTS.md) and docs/conventions/. See Working with AI assistants.

Requirements

  • Node.js 24.13.0. The exact version is in .nvmrc. Node.js runs the server and all the tools. If you use nvm (a tool that switches Node versions), run nvm install then nvm use in the repository folder. The install stops with a clear error if your Node version doesn't match ^24.5.0.
  • Corepack. It ships with Node.js. It gives you the exact version of Yarn (the package manager, the tool that downloads the libraries) that the repository asks for: Yarn 4.18.0.
  • Docker Desktop, or Postgres and Redis installed on your machine. Postgres is the database. Redis is a fast in-memory store; here it holds the queue of background jobs and the rate limit counts.

The setup script prefers Docker. If Docker doesn't answer within 10 seconds, it looks for a local Postgres and Redis instead (it needs the pg_ctl, initdb and redis-server commands on your PATH). If neither is available, it stops and says so.

Tip: To force the local services even when Docker is installed, run bash packages/app-utils/setup-dev-env.sh --native. Their data then lives in .cache/dev-services at the root of the repository. Once that folder holds the data, the script keeps using it, even when Docker is back.

Make it your own repository

You have access to the starter's private repository. Your product gets a repository of its own, which keeps a link to the starter so you can bring in its updates later. Do this once per product.

  1. On GitHub, create an empty private repository for your product, for example my-app. Leave it empty: no README, no license, no .gitignore.
  2. Copy the starter into a folder named after your product, and connect that folder to your repository:
bash
git clone <the starter's address> my-app        # the address is under the green "Code" button of the starter's page
cd my-app
git remote rename origin starter                  # "starter" now names the starter's repository
git remote add origin <your repository's address>
git push -u origin main

From now on, git push sends your work to your repository. The starter's repository is only read, when you bring in an update (see "Bring in the starter's updates" below).

Warning: Don't use GitHub's "Fork" button: a fork of a private repository depends on your access to it, and its history can't be separated later. Don't copy the files either: without the shared history, every update would have to be compared file by file.

Install

Run these commands at the root of the repository:

bash
corepack enable
yarn install

The install is hardened on purpose (.yarnrc.yml):

  • installed packages can't run their own install scripts, the most common way npm malware spreads;
  • Yarn refuses any package version published less than 3 days ago;
  • Yarn checks the lockfile (yarn.lock, the list of exact versions) against the registry.

None of this needs any action from you. It only explains why yarn add sometimes picks a version that is a few days old.

Prepare the database: yarn setup

bash
yarn setup

This runs packages/app-utils/setup-dev-env.sh. It does five things:

  1. It starts Postgres on port 5433 and Redis on port 6380, in Docker or directly on your machine.
  2. It prepares two databases: default for development and test for the automated tests.
  3. It creates the .env files (the files that hold your local settings and keys) in packages/app-server, packages/app-front and packages/app-website, by copying each .env.example. An existing .env is never overwritten.
  4. It applies the database migrations (the scripts that create and change tables) to both databases.
  5. It adds the sample data to the default database.

You can run it again at any time. It only does what is missing.

Start the app: yarn start

bash
yarn start

This starts three processes at once:

  • the API server, which restarts when you save a server file;
  • the web app, which reloads in the browser when you save a front-end file;
  • the background worker, which runs queued jobs such as Stripe and Clerk webhooks.

Open the web app at http://localhost:5180.

ServiceAddress
Web apphttp://localhost:5180
API serverhttp://localhost:3100
GraphQL APIhttp://localhost:3100/graphql (open it in a browser in development to get GraphiQL, an interactive query editor)
Health checkhttp://localhost:3100/healthz
Postgreslocalhost:5433, user app, databases default and test
Redislocalhost:6380

Two more things run on demand, not with yarn start:

bash
npx nx start app-website                 # the landing page, on http://localhost:5190
npx nx run app-ui:storybook:serve:dev    # the design system catalog, on http://localhost:6008

The sample user and the two optional services

The app runs right away because sign-in and billing each have a stand-in mode:

FeatureWithout an account (default)With an account
Sign-inAUTH_PROVIDER=mock: every request is signed in as Ada Lovelace, admin of the "Acme team" workspaceAUTH_PROVIDER=clerk: real accounts, workspaces and invitations. See Authentication.
BillingBILLING_PROVIDER=none: the Billing page shows the plans and the upgrade buttons say "Soon"BILLING_PROVIDER=stripe: Stripe Checkout and Stripe's customer portal. See Billing.

The sample data (from npx nx run app-server:database:seed) has two workspaces:

WorkspacePersonEmailRole
Acme teamAda Lovelaceada@example.comadmin
Acme teamGrace Hoppergrace@example.comadmin
Acme teamAlan Turingalan@example.commember
GlobexBob Smithbob@example.comadmin

Acme team has 3 projects (one of them archived). Globex has 1. Globex exists to prove that one workspace never sees the data of another.

In mock mode, tools and tests can act as another seeded person with the x-mock-user-email header. The value anonymous simulates a signed-out request. For example, to list projects as Alan (a member):

bash
curl -s http://localhost:3100/graphql \
  -H 'content-type: application/json' \
  -H 'x-mock-user-email: alan@example.com' \
  -d '{"query":"{ projects { name status } }"}'

Warning: The mock signs people in without any password. The server refuses to start with AUTH_PROVIDER=mock when NODE_ENV=production.

Everyday commands

Run them at the root of the repository.

bash
yarn start                                   # server, web app and worker
yarn verify                                  # the definition of done: checks what changed
npx nx run-many -t lint typecheck test       # every check, every package
npx nx lint app-front --configuration=fix    # fix lint and formatting in one package
npx nx test app-server                       # unit tests of one package
npx nx run app-server:test:integration       # API tests against the test database
npx nx run app-server:database:migrate       # apply pending migrations
npx nx run app-server:database:seed          # add the sample data again (safe to rerun)
npx nx run app-front:lingui:extract          # after adding or changing on-screen text
npx nx run app-front:lingui:compile
yarn check:forbidden-words                   # the word check that CI also runs
bash packages/app-utils/setup-dev-env.sh --down    # stop Postgres and Redis
bash packages/app-utils/setup-dev-env.sh --reset   # delete the local data and start fresh

npx nx runs Nx, the tool that runs tasks for each package. It only reruns what changed since the last run.

Several products on one computer

Each copy of the starter keeps its own database and job queue. In Docker they are named after the folder: my-app gets the Docker project my-app-dev. Without Docker, they live in the copy's own .cache/dev-services folder.

Every copy uses the same ports (3100, 5180, 5433 and 6380), so only one runs at a time. To switch from one product to another:

bash
# in the product you leave: press Ctrl+C where yarn start runs, then
bash packages/app-utils/setup-dev-env.sh --down
# in the product you open:
yarn setup
yarn start

If you forget, yarn setup and yarn start stop with a message that names the program using the port and its folder.

Bring in the starter's updates

Updates are published in the starter's repository. You bring them into your product when you choose, on a branch, like any other change:

bash
git switch main && git pull
git fetch starter
git log --oneline main..starter/main     # the starter's changes you don't have yet
git switch -c update-from-starter
git merge starter/main
  • If git reports conflicts (files that both you and the starter changed, often the website's copy, README.md or CLAUDE.md), ask your AI assistant: "Resolve the merge conflicts with the starter's update, keeping my product's changes."
  • Then run yarn install, yarn setup (it applies the new migrations) and yarn verify:full.
  • Push the branch, open a pull request, and merge it once CI passes.

When something goes wrong

  • "Start Docker Desktop, or install Postgres and Redis locally": the setup script found neither Docker nor a local Postgres and Redis. Start Docker Desktop, then run yarn setup again.
  • "Node version … doesn't match the required version": switch to the version in .nvmrc.
  • "Port … is already used by …": another copy of the starter, or an earlier yarn start, is still running. The message names the program and its folder. See "Several products on one computer".
  • Type errors in a package you didn't touch, after switching branches: rebuild the shared package with npx nx build app-shared --skip-nx-cache. Its build output is not stored in git.
  • You changed a key in a .env file and nothing happened: stop yarn start and start it again. The server and the web app read .env files at start-up only.

Where to go next