Linklater is a tiny Instapaper-style “read it later” app built as a take-home assignment for a job interview.
It’s both an homage to Richard Linklater and a ridiculously apt portmanteau.
Most curious adults come across dozens of interesting articles on any given day. Do they have time to read them all? Nope. Do they often forget about them? Totally.
Linklater allows these articles to quickly and easily be saved for later reading.
As an added bonus, you don’t have to use (or pay!) for a service that’s gone decidedly downhill since Marco Arment left.
- Sign up for an account with an email and password
- Save links to read later
- Search through your links to find something to read
- Archive links you’ve managed to read
- Delete links you can no longer stand the sight of
- StumbleUpon a random saved link
- Toggle between light and dark mode
- Use a bookmarklet to save links
- Delete your account and burn it to the ground
Click on a thumbnail to see the full-size screenshot:
- Front-end: React + Vite + Tailwind CSS + Font Awesome
- Back-end: NestJS
- Database: Prisma + PostgreSQL
- Authentication: Passport
- Linting: ESLint + Prettier
- Testing: Vitest (front-end) + Jest (back-end)
It’s a majestic modular monorepo!
.
├── apps
│ ├── api # NestJS back-end
│ └── web # React + Vite front-end
├── package.json # root workspace + scripts
└── README.mdLinklater supports a simple “send this page to Linklater” bookmarklet by pre-filling the url + title via query params.
Example bookmarklet:
javascript:(function(){
const base = 'https://linklater.example.com';
const url = encodeURIComponent(location.href);
const title = encodeURIComponent(document.title);
window.open(`${base}/?url=${url}&title=${title}`, '_blank','noopener,noreferrer');
})();For local development:
javascript:(function(){
const base = 'http://localhost:5173';
const url = encodeURIComponent(location.href);
const title = encodeURIComponent(document.title);
window.open(`${base}/?url=${url}&title=${title}`, '_blank','noopener,noreferrer');
})();- Node.js (22.x recommended)
- PostgreSQL running locally with a “linklater” database
# from the repo root
npm installCreate apps/api/.env:
DATABASE_URL="postgresql://YOUR_PG_USER:YOUR_PG_PASSWORD@localhost:5432/linklater?schema=public"
JWT_SECRET="dev-secret-change-me"Create apps/web/.env:
VITE_API_BASE_URL="http://localhost:3000"cd apps/api
npx prisma migrate dev --name init# from the repo root
npm run devThis uses concurrently to run:
- NestJS on http://localhost:3000
- Vite on http://localhost:5173
Open http://localhost:5173 in your web browser.
Both the front and back-end use ESLint and Prettier:
# lint everything
npm run lint
# lint front-end only
npm run lint --workspace @linklater/web
# lint back-end only
npm run lint --workspace @linklater/apiVitest is used to test the front-end:
cd apps/web
npm run testJest is used to test the back-end:
cd apps/api
npm run testGitHub Actions lint and test on pushes and PRs to main.
- Created a monorepo to keep local dev simple and straightforward
- Used email/pass authentication for speed and simplicity
- No email verification or 2FA
- Vite makes it real easy to spin up a UI and wire up Tailwind for styles
- Kept the UX focused on clarity
- One main “Links” view with all actions
- Separate “Settings” view for user account changes
- Light/dark theme persistence via localStorage
- Basic responsive layout
- Back-end tests mock Prisma to avoid DB dependencies
- Front-end tests cover core UI behaviors
- Add OAuth2 support to login with Google, GitHub, etc.
- Fetch and store link metadata via a background job
- Link tags and collections
- Improve the full-text search
- Create browser extensions for one-click saving
- Higher contrast light and dark mode color palettes
- Use Tailwind's
darkvariant w/ overrides
- Use Tailwind's
- THEMES
ChatGPT was instrumental in rapidly prototyping this project.
An initial prompt of 600 words (!?) was used to create a plan of attack for the features and design considerations.
Additionally, ChatGPT was used to analyze several authentication frameworks and provide rationale for their use in the project.
A 300 word (!) prompt was written to address a handful of fast-follows to improve and refine the user experience.