A full-stack web application that lets users customise and discover jokes by category, format, and content filters β powered by the free JokeAPI v2. Built as a capstone project for the Complete Web Development Bootcamp by Angela Yu.
- β¨ Features
- π΄ Live Demo
- π οΈ Tech Stack
- π Project Structure
- βοΈ How It Works
- π Getting Started
- π API Reference
- π¨ Design Highlights
- π Error Handling
- π¦ Deployment
- π Learning Objectives
- π Credits
| Feature | Description |
|---|---|
| π² 7 Joke Categories | Any, Programming, Miscellaneous, Dark, Pun, Spooky, Christmas |
| π Joke Formats | One-liners or interactive Setup & Punchline |
| π₯ Tap-to-Reveal | Two-part jokes hide the punchline until the user is ready |
| π‘οΈ Content Filters | Blacklist unwanted themes: NSFW, Religious, Political, Racist, Sexist, Explicit |
| π Graceful Errors | Friendly messages when the API is down or no jokes match |
| π± Fully Responsive | Works beautifully on mobile, tablet, and desktop |
| β‘ Zero Auth Required | No API keys needed β works out of the box |
| π Live on Vercel | Deployed and accessible anywhere in the world |
Open the link, pick your filters, hit the button β and get a joke instantly.
Frontend β HTML5 Β· CSS3 Β· Vanilla JavaScript Β· EJS Templating
Backend β Node.js Β· Express.js
HTTP Client β Axios
API β JokeAPI v2 (free, no auth, CORS-enabled)
Fonts β Google Fonts (Fraunces + Nunito)
Hosting β Vercel
- Express.js β minimal, fast server framework ideal for routing and middleware
- Axios β cleaner API than
fetchfor server-side HTTP requests, with built-in error handling - EJS β simple templating that lets the server inject dynamic data directly into HTML
- JokeAPI β free, no API key needed, CORS-enabled, and returns structured JSON with rich filtering options
jokester/
β
βββ index.js β Express server (routes, Axios calls, error handling)
β
βββ views/
β βββ index.ejs β Main EJS template (all dynamic rendering)
β
βββ public/
β βββ css/
β β βββ style.css β All styles (neo-brutalist design system)
β βββ js/
β βββ app.js β Client-side JS (punchline reveal, pill sync)
β
βββ package.json β Dependencies and npm scripts
βββ vercel.json β Vercel deployment configuration
βββ README.md β You are here
User fills form β POST /joke β Express builds API URL β Axios calls JokeAPI
β
Browser renders β EJS renders β Express passes joke data β Response received
- User visits
GET /β the home page renders with an empty state and the filter form. - User configures their joke preferences (category, format, blacklist flags) and submits.
- Express receives the
POST /jokerequest and readsreq.body. - The server constructs a dynamic JokeAPI URL:
https://v2.jokeapi.dev/joke/{category}?blacklistFlags={flags}&type={type} - Axios sends the GET request to JokeAPI and awaits the response.
- The data is passed into the EJS template, which renders the joke differently based on type (
singlevstwopart). - For two-part jokes, a JavaScript button hides the punchline until the user taps to reveal it.
Make sure you have the following installed:
- Node.js v18 or higher
- npm (comes with Node.js)
git clone https://github.com/razazaheer12/Jokester.git
cd Jokesternpm install# With auto-reload (recommended)
npx nodemon index.js
# Or run once without nodemon
node index.jshttp://localhost:3000
That's it β no .env file, no API keys, no additional setup required.
This project uses JokeAPI v2 β completely free, no authentication required.
https://v2.jokeapi.dev/joke/{category}
| Parameter | Type | Example | Description |
|---|---|---|---|
category |
path | Programming |
Joke category (or Any) |
type |
query | twopart |
single or twopart |
blacklistFlags |
query | nsfw,explicit |
Comma-separated content flags to exclude |
GET https://v2.jokeapi.dev/joke/Programming?blacklistFlags=nsfw,explicit&type=twopart
{
"error": false,
"category": "Programming",
"type": "twopart",
"setup": "Why did the programmer quit his job?",
"delivery": "Because he didn't get arrays.",
"flags": {
"nsfw": false,
"religious": false,
"political": false,
"racist": false,
"sexist": false,
"explicit": false
},
"id": 210,
"safe": true,
"lang": "en"
}Any Β· Programming Β· Misc Β· Dark Β· Pun Β· Spooky Β· Christmas
nsfw Β· religious Β· political Β· racist Β· sexist Β· explicit
The UI uses a neo-brutalist design language with a bright, playful twist:
- Typography β
Fraunces(serif, italic) for jokes;Nunito(rounded sans-serif) for UI - Color palette β Sunshine yellow
#FFE14D, coral#FF6B6B, mint#4ECDC4, and navy#1A1A2E - Cards β thick
2.5pxborders + hard6pxoffset box shadows for that chunky, tactile feel - Animated blobs β blurred color blobs float in the background using CSS
@keyframes - Interactive pills β category and filter buttons toggle with smooth hover + active states
- Reveal animation β the joke card pops in with a spring cubic-bezier animation on each new result
- Mobile-first β layout adapts gracefully from 320px screens upward
Errors are handled at two levels:
try {
const response = await axios.get(url);
const data = response.data;
if (data.error) {
// JokeAPI returned no results for these filters
return res.render("index", { joke: null, error: "No jokes found...", query: req.body });
}
res.render("index", { joke: data, error: null, query: req.body });
} catch (err) {
// Network failure or API down
console.error("JokeAPI error:", err.message);
res.render("index", { joke: null, error: "Couldn't reach the Joke API right now.", query: req.body });
}<% if (error) { %>
<div class="alert alert-error">
<span class="alert-icon">π
</span>
<p><%= error %></p>
</div>
<% } %>The form also preserves user selections after an error, so they don't have to re-configure their filters.
This project is deployed on Vercel with a custom vercel.json configuration to support the Express server.
{
"version": 2,
"builds": [{ "src": "index.js", "use": "@vercel/node" }],
"routes": [{ "src": "/(.*)", "dest": "index.js" }]
}# Install Vercel CLI
npm install -g vercel
# Deploy from your project root
vercel
# Follow the prompts β your site will be live in secondsThis project was built to demonstrate:
| Skill | Implementation |
|---|---|
| Express routing | GET / and POST /joke endpoints in index.js |
| Axios HTTP client | Server-side API calls with async/await and try/catch |
| EJS templating | Dynamic rendering of joke data, error states, and form state preservation |
| API integration | Consuming a public REST API with query parameter construction |
| Error handling | Both API-level (data.error) and network-level (catch) errors handled gracefully |
| Static file serving | express.static("public") for CSS and JS |
| Form handling | express.urlencoded middleware + req.body parsing |
| Deployment | Vercel serverless deployment with vercel.json config |
| Code organisation | Structured directory layout with separation of concerns |
| Resource | Link |
|---|---|
| Course | The Complete Web Development Bootcamp β Angela Yu |
| API | JokeAPI v2 by sv443 |
| Fonts | Google Fonts β Fraunces & Nunito |
| Hosting | Vercel |
Made with π and β by razazaheer12
If this project made you laugh even once β it did its job.
β Star the repo if you enjoyed it!