feat: add projects functionality for backend roadmap (#6412)

* Add empty projects listing and cards

* Projects page header

* Projects listing and filtering

* Add project detail page

* Add a task tracker CLI project

* UI changes

* Add projects for expense and task tracker

* Add new projects

* Add blogging platform API project

* Project page UI changes

* Add todo list api project

* Add todo list api project

* Add a project idea for weather service

* Add expense tracker API project

* Add more details to expense tracker

* Add markdown note taking app idea

* Add URL shortener service project

* Add broadcast server project

* Add ecommerce project idea

* Add project on other roadmaps

* Add workout tracker project

* Add image processing project idea

* Add movie reservation system ap

* Add movie reservation system project

* Add realtime leader board system project

* Add details for a database backup utility

* Add project contribution docs

* Fix UI of the discover page
pull/6413/head
Kamran Ahmed 4 months ago committed by GitHub
parent abeea1a5b5
commit ad3f28a8c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 35
      .github/ISSUE_TEMPLATE/05-project-contribution.yml
  2. 5
      contributing.md
  3. 27
      src/components/Badge.tsx
  4. 4
      src/components/DiscoverRoadmaps/DiscoverRoadmaps.tsx
  5. 2
      src/components/Projects/EmptyProjects.tsx
  6. 38
      src/components/Projects/ProjectCard.tsx
  7. 133
      src/components/Projects/ProjectsList.tsx
  8. 4
      src/components/RoadmapHeader.astro
  9. 216
      src/data/projects/blogging-platform-api.md
  10. 59
      src/data/projects/broadcast-server.md
  11. 75
      src/data/projects/database-backup-utility.md
  12. 54
      src/data/projects/ecommerce-api.md
  13. 64
      src/data/projects/expense-tracker-api.md
  14. 85
      src/data/projects/expense-tracker.md
  15. 69
      src/data/projects/fitness-workout-tracker.md
  16. 56
      src/data/projects/github-user-activity.md
  17. 152
      src/data/projects/image-processing-service.md
  18. 42
      src/data/projects/markdown-note-taking-app.md
  19. 70
      src/data/projects/movie-reservation-system.md
  20. 74
      src/data/projects/personal-blog.md
  21. 48
      src/data/projects/realtime-leaderboard-system.md
  22. 124
      src/data/projects/task-tracker.md
  23. 211
      src/data/projects/todo-list-api.md
  24. 52
      src/data/projects/unit-converter.md
  25. 168
      src/data/projects/url-shortening-service.md
  26. 50
      src/data/projects/weather-api-wrapper-service.md
  27. 2
      src/lib/link-group.ts
  28. 95
      src/lib/project.ts
  29. 3
      src/pages/[roadmapId]/index.astro
  30. 36
      src/pages/[roadmapId]/projects.astro
  31. 129
      src/pages/projects/[projectId].astro
  32. 14
      src/styles/global.css

@ -0,0 +1,35 @@
name: "🙏 Submit a Project Idea"
description: Help us add project ideas to roadmaps.
labels: [project contribution]
assignees: []
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to submit a project idea! Please fill out the information below and we'll get back to you as soon as we can.
- type: input
id: roadmap-title
attributes:
label: What Roadmap is this project for?
placeholder: e.g. Backend Roadmap
validations:
required: true
- type: select
id: project-difficulty
attributes:
label: Project Difficulty
options:
- Beginner
- Intermediate
- Advanced
validations:
required: true
- type: textarea
id: roadmap-description
attributes:
label: Add Project Details
description: Please write a detailed description of the project in 3rd person e.g. "You are required to build a..."
placeholder: |
e.g. You are required to build a RESTful API...
validations:
required: true

@ -4,6 +4,7 @@ First of all thank you for considering to contribute. Please look at the details
- [New Roadmaps](#new-roadmaps) - [New Roadmaps](#new-roadmaps)
- [Existing Roadmaps](#existing-roadmaps) - [Existing Roadmaps](#existing-roadmaps)
- [Adding Projects](#adding-projects)
- [Adding Content](#adding-content) - [Adding Content](#adding-content)
- [Guidelines](#guidelines) - [Guidelines](#guidelines)
@ -22,6 +23,10 @@ For the existing roadmaps, please follow the details listed for the nature of co
**Note:** Please note that our goal is <strong>not to have the biggest list of items</strong>. Our goal is to list items or skills most relevant today. **Note:** Please note that our goal is <strong>not to have the biggest list of items</strong>. Our goal is to list items or skills most relevant today.
## Adding Projects
If you have a project idea that you think we should add to the roadmap, feel fre to open an issue with as much details about the project as possible and the roadmap you think it should be added to.
## Adding Content ## Adding Content
Find [the content directory inside the relevant roadmap](https://github.com/kamranahmedse/developer-roadmap/tree/master/src/data/roadmaps). Please keep the following guidelines in mind when submitting content: Find [the content directory inside the relevant roadmap](https://github.com/kamranahmedse/developer-roadmap/tree/master/src/data/roadmaps). Please keep the following guidelines in mind when submitting content:

@ -0,0 +1,27 @@
type BadgeProps = {
variant: 'blue' | 'green' | 'red' | 'yellow' | 'grey' | 'white';
text: string;
};
export function Badge(type: BadgeProps) {
const { variant, text } = type;
const colors = {
blue: 'bg-blue-100 text-blue-700 border-blue-200',
green: 'bg-green-100 text-green-700 border-green-200',
red: 'bg-red-100 text-red-700 border-red-200',
yellow: 'bg-yellow-100 text-yellow-700 border-yellow-200',
grey: 'bg-gray-100 text-gray-700 border-gray-200',
white: 'bg-white text-black border-gray-200',
teal: 'bg-teal-100 text-teal-700 border-teal-200',
black: 'bg-gray-500 text-white border-gray-500',
};
return (
<span
className={`rounded-md border capitalize ${colors[variant]} px-1 py-0.5 text-xs tracking-wide`}
>
{text}
</span>
);
}

@ -124,10 +124,10 @@ export function DiscoverRoadmaps(props: DiscoverRoadmapsProps) {
<div className="border-b bg-white pt-10 pb-7"> <div className="border-b bg-white pt-10 pb-7">
<div className="container text-left"> <div className="container text-left">
<div className="flex flex-col items-start bg-white"> <div className="flex flex-col items-start bg-white">
<h1 className="mb-1 text-2xl font-bold sm:text-4xl"> <h1 className="mb-2 text-3xl font-bold sm:text-4xl">
Community Roadmaps Community Roadmaps
</h1> </h1>
<p className="mb-3 text-base text-gray-500"> <p className="mb-5 text-base text-gray-500">
A selected list of community-created roadmaps A selected list of community-created roadmaps
</p> </p>
<div className="relative"> <div className="relative">

@ -15,7 +15,7 @@ export function EmptyProjects() {
}, []); }, []);
return ( return (
<div className="flex flex-col items-center justify-center"> <div className="relative my-2.5 flex min-h-[400px] flex-col items-center justify-center rounded-lg border bg-white">
<FolderKanbanIcon className="h-14 w-14 text-gray-300" strokeWidth={1.5} /> <FolderKanbanIcon className="h-14 w-14 text-gray-300" strokeWidth={1.5} />
<h2 className="mb-0.5 mt-2 text-center text-base font-medium text-gray-900 sm:text-xl"> <h2 className="mb-0.5 mt-2 text-center text-base font-medium text-gray-900 sm:text-xl">
<span className="hidden sm:inline">Projects are coming soon!</span> <span className="hidden sm:inline">Projects are coming soon!</span>

@ -0,0 +1,38 @@
import { Badge } from '../Badge.tsx';
import type {
ProjectDifficultyType,
ProjectFileType,
} from '../../lib/project.ts';
type ProjectCardProps = {
project: ProjectFileType;
};
const badgeVariants: Record<ProjectDifficultyType, string> = {
beginner: 'yellow',
intermediate: 'green',
advanced: 'blue',
};
export function ProjectCard(props: ProjectCardProps) {
const { project } = props;
const { frontmatter, id } = project;
return (
<a
href={`/projects/${id}`}
className="flex flex-col rounded-md border bg-white p-3 transition-colors hover:border-gray-300 hover:bg-gray-50"
>
<span className="flex justify-between gap-1.5">
<Badge
variant={badgeVariants[frontmatter.difficulty] as any}
text={frontmatter.difficulty}
/>
<Badge variant={'grey'} text={frontmatter.nature} />
</span>
<span className="mb-1 mt-2.5 font-medium">{frontmatter.title}</span>
<span className="text-sm text-gray-500">{frontmatter.description}</span>
</a>
);
}

@ -0,0 +1,133 @@
import { ProjectCard } from './ProjectCard.tsx';
import { HeartHandshake, Trash2 } from 'lucide-react';
import { cn } from '../../lib/classname.ts';
import { useMemo, useState } from 'react';
import {
projectDifficulties,
type ProjectDifficultyType,
type ProjectFileType,
} from '../../lib/project.ts';
import {
deleteUrlParam,
getUrlParams,
setUrlParams,
} from '../../lib/browser.ts';
type DifficultyButtonProps = {
difficulty: ProjectDifficultyType;
isActive?: boolean;
onClick?: () => void;
};
function DifficultyButton(props: DifficultyButtonProps) {
const { difficulty, onClick, isActive } = props;
return (
<button
onClick={onClick}
className={cn(
'rounded-md border bg-white px-3 py-1 text-sm capitalize transition-colors hover:border-gray-300 hover:bg-gray-100',
{
'border-black bg-gray-100 hover:border-black hover:bg-gray-100 hover:text-black':
isActive,
},
)}
>
{difficulty}
</button>
);
}
type ProjectsListProps = {
projects: ProjectFileType[];
};
export function ProjectsList(props: ProjectsListProps) {
const { projects } = props;
const { difficulty: urlDifficulty } = getUrlParams();
const [difficulty, setDifficulty] = useState<
ProjectDifficultyType | undefined
>(urlDifficulty);
const projectsByDifficulty: Map<ProjectDifficultyType, ProjectFileType[]> =
useMemo(() => {
const result = new Map<ProjectDifficultyType, ProjectFileType[]>();
for (const project of projects) {
const difficulty = project.frontmatter.difficulty;
if (!result.has(difficulty)) {
result.set(difficulty, []);
}
result.get(difficulty)?.push(project);
}
return result;
}, [projects]);
const matchingProjects = difficulty
? projectsByDifficulty.get(difficulty) || []
: projects;
return (
<div className="flex flex-col">
<div className="my-2.5 flex items-center justify-between">
<div className="flex flex-wrap gap-1">
{projectDifficulties.map((projectDifficulty) => (
<DifficultyButton
onClick={() => {
setDifficulty(projectDifficulty);
setUrlParams({ difficulty: projectDifficulty });
}}
difficulty={projectDifficulty}
isActive={projectDifficulty === difficulty}
/>
))}
{difficulty && (
<button
onClick={() => {
setDifficulty(undefined);
deleteUrlParam('difficulty');
}}
className="flex items-center gap-1.5 rounded-md border border-red-500 bg-transparent px-2 py-0.5 text-sm text-red-500 transition-colors hover:bg-red-500 hover:text-white"
>
<Trash2 className="h-3.5 w-3.5" strokeWidth={2.25} />
Clear
</button>
)}
</div>
<a
href={''}
className="hidden items-center gap-2 rounded-md border border-transparent px-2 py-0.5 text-sm underline underline-offset-2 hover:bg-black hover:text-white hover:no-underline sm:flex"
>
<HeartHandshake className="h-4 w-4" />
Submit a Project Idea
</a>
</div>
<div className="mb-24 grid grid-cols-1 gap-1.5 sm:grid-cols-2 lg:grid-cols-3">
{matchingProjects.length === 0 && (
<div className="col-span-3 rounded-md border bg-white p-4 text-left text-sm text-gray-500">
<p>No matching projects found.</p>
</div>
)}
{matchingProjects
.sort((project) => {
return project.frontmatter.difficulty === 'beginner'
? -1
: project.frontmatter.difficulty === 'intermediate'
? 0
: 1;
})
.sort((a, b) => {
return a.frontmatter.sort - b.frontmatter.sort;
})
.map((matchingProject) => (
<ProjectCard project={matchingProject} />
))}
</div>
</div>
);
}

@ -21,6 +21,7 @@ export interface Props {
roadmapId: string; roadmapId: string;
isUpcoming?: boolean; isUpcoming?: boolean;
hasSearch?: boolean; hasSearch?: boolean;
projectCount?: number;
question?: RoadmapFrontmatter['question']; question?: RoadmapFrontmatter['question'];
hasTopics?: boolean; hasTopics?: boolean;
isForkable?: boolean; isForkable?: boolean;
@ -35,6 +36,7 @@ const {
isUpcoming = false, isUpcoming = false,
note, note,
hasTopics = false, hasTopics = false,
projectCount = 0,
question, question,
activeTab = 'roadmap', activeTab = 'roadmap',
} = Astro.props; } = Astro.props;
@ -123,7 +125,7 @@ const hasTnsBanner = !!tnsBannerLink;
icon={FolderKanbanIcon} icon={FolderKanbanIcon}
text='Projects' text='Projects'
isActive={activeTab === 'projects'} isActive={activeTab === 'projects'}
badgeText='soon' badgeText={projectCount > 0 ? '' : 'soon'}
/> />
</div> </div>

@ -0,0 +1,216 @@
---
title: 'Blogging Platform API'
description: 'Build a RESTful API for a personal blogging platform'
isNew: false
sort: 7
difficulty: 'beginner'
nature: 'API'
skills:
- 'Programming Language'
- 'CRUD'
- 'RESTful API'
- 'Database'
seo:
title: 'Blogging Platform API Project Idea'
description: 'Build a RESTful API for a personal blogging platform. Users can create, read, update, and delete blog posts using the API.'
keywords:
- 'blogging platform api'
- 'backend project idea'
roadmapIds:
- 'backend'
- 'nodejs'
- 'python'
- 'java'
- 'golang'
- 'spring-boot'
---
You are required to create a simple RESTful API with basic CRUD operations for a personal blogging platform. CRUD stands for Create, Read, Update, and Delete.
## Goals
The goals of this project are to help you:
- Understand what the RESTful APIs are including best practices and conventions
- Learn how to create a RESTful API
- Learn about common HTTP methods like GET, POST, PUT, PATCH, DELETE
- Learn about status codes and error handling in APIs
- Learn how to perform CRUD operations using an API
- Learn how to work with databases
## Requirements
You should create a RESTful API for a personal blogging platform. The API should allow users to perform the following operations:
- Create a new blog post
- Update an existing blog post
- Delete an existing blog post
- Get a single blog post
- Get all blog posts
- Filter blog posts by a search term
Given below are the details for each API operation.
### Create Blog Post
Create a new blog post using the `POST` method
```plaintext
POST /posts
{
"title": "My First Blog Post",
"content": "This is the content of my first blog post.",
"category": "Technology",
"tags": ["Tech", "Programming"]
}
```
Each blog post should have the following fields:
```json
{
"title": "My First Blog Post",
"content": "This is the content of my first blog post.",
"category": "Technology",
"tags": ["Tech", "Programming"]
}
```
The endpoint should validate the request body and return a `201 Created` status code with the newly created blog post i.e.
```json
{
"id": 1,
"title": "My First Blog Post",
"content": "This is the content of my first blog post.",
"category": "Technology",
"tags": ["Tech", "Programming"],
"createdAt": "2021-09-01T12:00:00Z",
"updatedAt": "2021-09-01T12:00:00Z"
}
```
or a `400 Bad Request` status code with error messages in case of validation errors.
## Update Blog Post
Update an existing blog post using the `PUT` method
```plaintext
PUT /posts/1
{
"title": "My Updated Blog Post",
"content": "This is the updated content of my first blog post.",
"category": "Technology",
"tags": ["Tech", "Programming"]
}
```
The endpoint should validate the request body and return a `200 OK` status code with the updated blog post i.e.
```json
{
"id": 1,
"title": "My Updated Blog Post",
"content": "This is the updated content of my first blog post.",
"category": "Technology",
"tags": ["Tech", "Programming"],
"createdAt": "2021-09-01T12:00:00Z",
"updatedAt": "2021-09-01T12:30:00Z"
}
```
or a `400 Bad Request` status code with error messages in case of validation errors. It should return a `404 Not Found` status code if the blog post was not found.
### Delete Blog Post
Delete an existing blog post using the `DELETE` method
```plaintext
DELETE /posts/1
```
The endpoint should return a `204 No Content` status code if the blog post was successfully deleted or a `404 Not Found` status code if the blog post was not found.
### Get Blog Post
Get a single blog post using the `GET` method
```plaintext
GET /posts/1
```
The endpoint should return a `200 OK` status code with the blog post i.e.
```json
{
"id": 1,
"title": "My First Blog Post",
"content": "This is the content of my first blog post.",
"category": "Technology",
"tags": ["Tech", "Programming"],
"createdAt": "2021-09-01T12:00:00Z",
"updatedAt": "2021-09-01T12:00:00Z"
}
```
or a `404 Not Found` status code if the blog post was not found.
### Get All Blog Posts
Get all blog posts using the `GET` method
```plaintext
GET /posts
```
The endpoint should return a `200 OK` status code with an array of blog posts i.e.
```json
[
{
"id": 1,
"title": "My First Blog Post",
"content": "This is the content of my first blog post.",
"category": "Technology",
"tags": ["Tech", "Programming"],
"createdAt": "2021-09-01T12:00:00Z",
"updatedAt": "2021-09-01T12:00:00Z"
},
{
"id": 2,
"title": "My Second Blog Post",
"content": "This is the content of my second blog post.",
"category": "Technology",
"tags": ["Tech", "Programming"],
"createdAt": "2021-09-01T12:30:00Z",
"updatedAt": "2021-09-01T12:30:00Z"
}
]
```
You don't have to implement pagination, authentication or authorization for this project. You can focus on the core functionality of the API.
While retrieving posts, user can also filter posts by a search term. You should do a wildcard search on the title, content or category fields of the blog posts. For example:
```plaintext
GET /posts?term=tech
```
This should return all blog posts that have the term "tech" in their title, content or category. You can use a simple SQL query if you are using a SQL database or a similar query for a NoSQL database.
<hr />
## Tech Stack
Feel free to use any programming language, framework, and database of your choice for this project. Here are some suggestions:
- If you are using JavaScript, you can use Node.js with Express.js
- If you are using Python, you can use Flask or Django
- If you are using Java, you can use Spring Boot
- If you are using Ruby, you can use Ruby on Rails
For databases, you can use:
- MySQL if you are using SQL
- MongoDB if you are using NoSQL

@ -0,0 +1,59 @@
---
title: 'Broadcast Server'
description: 'Build a server that can broadcast messages to connected clients.'
isNew: false
sort: 12
difficulty: 'intermediate'
nature: 'CLI'
skills:
- 'Programming Language'
- 'Networking'
- 'Websockets'
- 'HTTP'
seo:
title: 'Broadcast Server Project Idea'
description: 'Build a server that can broadcast messages to connected clients.'
keywords:
- 'broadcast server'
- 'backend project idea'
roadmapIds:
- 'backend'
- 'nodejs'
- 'python'
- 'java'
- 'golang'
- 'spring-boot'
---
You are required to create a simple broadcast server that will allow clients to connect to it, send messages that will be broadcasted to all connected clients.
## Goal
The goal of this project is to help you understand how to work with websockets and implement real-time communication between clients and servers. This will help you understand how the real-time features of applications like chat applications, live scoreboards, etc., work.
## Requirements
You are required to build a CLI based application that can be used to either start the server or connect to the server as a client. Here are the sample commands that you can use:
- `broadcast-server start` - This command will start the server.
- `broadcast-server connect` - This command will connect the client to the server.
When the server is started using the `broadcast-server start` command, it should listen for client connections on a specified port (you can configure that using command options or hardcode for simplicity). When a client connects and sends a message, the server should broadcast this message to all connected clients.
The server should be able to handle multiple clients connecting and disconnecting gracefully.
## Implementation
You can use any programming language to implement this project. Here are some of the steps that you can follow to implement this project:
1. Create a server that listens for incoming connections.
2. When a client connects, store the connection in a list of connected clients.
3. When a client sends a message, broadcast this message to all connected clients.
4. Handle client disconnections and remove the client from the list of connected clients.
5. Implement a client that can connect to the server and send messages.
6. Test the server by connecting multiple clients and sending messages.
7. Implement error handling and graceful shutdown of the server.
<hr />
This project will help you understand how to work with websockets and implement real-time communication between clients and servers. You can extend this project by adding features like authentication, message history, etc.

@ -0,0 +1,75 @@
---
title: 'Database Backup Utility'
description: 'Build a database backup utility that can backup and restore any DB'
isNew: false
sort: 18
difficulty: 'advanced'
nature: 'CLI'
skills:
- 'Programming Language'
- 'Databases'
- 'CLI'
seo:
title: 'Database Backup Utility Project Idea'
description: ''
keywords:
- 'multiplayer game server'
- 'backend project idea'
roadmapIds:
- 'backend'
- 'nodejs'
- 'python'
- 'java'
- 'golang'
- 'spring-boot'
---
You are required to build a command-line interface (CLI) utility for backing up any type of database. The utility will support various database management systems (DBMS) such as MySQL, PostgreSQL, MongoDB, SQLite, and others. The tool will feature automatic backup scheduling, compression of backup files, storage options (local and cloud), and logging of backup activities.
## Project Requirements
The CLI tool should support the following features:
### Database Connectivity
- **Support for Multiple DBMS:** Provide support for connecting to various types of databases (e.g., MySQL, PostgreSQL, MongoDB).
- **Connection Parameters:** Allow users to specify database connection parameters. Parameters may include host, port, username, password, and database name.
- **Connection Testing:** Validate credentials based on the database type before proceeding with backup operations.
- **Error Handling:** Implement error handling for database connection failures.
### Backup Operations
- **Backup Types:** Support full, incremental, and differential backup types based on the database type and user preference.
- **Compression:** Compress backup files to reduce storage space.
### Storage Options
- **Local Storage:** Allow users to store backup files locally on the system.
- **Cloud Storage:** Provide options to store backup files on cloud storage services like AWS S3, Google Cloud Storage, or Azure Blob Storage.
### Logging and Notifications
- **Logging:** Log backup activities, including start time, end time, status, time taken, and any errors encountered.
- **Notifications:** Optionally send slack notification on completion of backup operations.
### Restore Operations
- **Restore Backup:** Implement a restore operation to recover the database from a backup file.
- **Selective Restore:** Provide options for selective restoration of specific tables or collections if supported by the DBMS.
## Constraints
Feel free to use any programming language or framework of your choice to implement the database backup utility. Ensure that the tool is well-documented and easy to use. You can leverage existing libraries or tools for database connectivity and backup operations.
- The tool should be designed to handle large databases efficiently.
- Ensure that the backup and restore operations are secure and reliable.
- The utility should be user-friendly and provide clear instructions for usage (e.g., help command).
- Consider the performance implications of backup operations on the database server.
- Implement proper error handling and logging mechanisms to track backup activities.
- Ensure compatibility with different operating systems (Windows, Linux, macOS).
<hr />
Working on this project will help you gain a deeper understanding of database management systems, backup strategies, command-line interface development, and error handling. You will also learn about cloud storage integration and logging mechanisms. This project will enhance your skills in programming, database management, and system administration.

@ -0,0 +1,54 @@
---
title: 'E-Commerce API'
description: 'E-Commerce platform with cart and payment gateway integration.'
isNew: false
sort: 13
difficulty: 'intermediate'
nature: 'API'
skills:
- 'Programming Language'
- 'Database'
- 'Payment Gateway'
- 'Inventory Management'
seo:
title: 'E-Commerce API Project Idea'
description: 'E-Commerce platform with cart and payment gateway integration.'
keywords:
- 'backend project idea'
roadmapIds:
- 'backend'
- 'nodejs'
- 'python'
- 'java'
- 'golang'
- 'spring-boot'
---
You are required to build an API for an e-commerce platform. If you have developed the other projects in this roadmap, you will have to keep in mind everything you have learned so far:
- JWT authentication to ensure many users can interact with it.
- Implementing simple CRUD operations.
- Interaction with external services. Here you’ll be integrating with payment gateways such as Stripe.
- A complex data model that can handle products, shopping carts, and more.
The goal of this project is to help you understand how to build a logic-heavy application with complex data models. You will also learn how to interact with external services and handle user authentication.
![E-Commerce API](https://assets.roadmap.sh/guest/simple-ecommerce-api-thzqo.png)
## Requirements
Here is a rough list of requirements for this project:
- Ability for users to sign up and log in.
- Ability to add products to a cart.
- Ability to remove products from a cart.
- Ability to view and search for products.
- Ability for users to checkout and pay for products.
You should also have some sort of admin panel where only you can add products, set the prices, manage inventory, and more.
Start with building the API first and then frontend; you can use tools like Postman to interact with your API. Alternatively, build a simple frontend with HTML, CSS and some templating engine like Jinja or EJS.
<hr />
This project is a great way to learn how to build a complex application with a lot of moving parts. I will highly recommend you to complete this project before moving on to more advanced projects.

@ -0,0 +1,64 @@
---
title: 'Expense Tracker API'
description: 'Build an API for an expense tracker application.'
isNew: false
sort: 9
difficulty: 'beginner'
nature: 'API'
skills:
- 'Programming Language'
- 'Data modeling'
- 'User Authentication'
seo:
title: 'Expense Tracker API Project Idea'
description: 'Build an API for an expense tracker application.'
keywords:
- 'backend project idea'
roadmapIds:
- 'backend'
- 'nodejs'
- 'python'
- 'java'
- 'golang'
- 'spring-boot'
---
Build an API for an expense tracker application. This API should allow users to create, read, update, and delete expenses. Users should be able to sign up and log in to the application. Each user should have their own set of expenses.
![Expense Tracker API](https://assets.roadmap.sh/guest/expense-tracker-api-m72p5.png)
## Features
Here are the features that you should implement in your Expense Tracker API:
- Sign up as a new user.
- Generate and validate JWT tokens for handling authentication and user session.
- List and filter your past expenses. You can add the following filters:
- Past week
- Past month
- Last 3 months
- Custom (to specify a start and end date of your choosing).
- Add a new expense
- Remove existing expenses
- Update existing expenses
## Constraints
You can use any programming language and framework of your choice. You can use a database of your choice to store the data. You can use any ORM or database library to interact with the database.
Here are some constraints that you should follow:
- You’ll be using [JWT (JSON Web Token)](https://itnext.io/demystifying-jwt-a-guide-for-front-end-developers-ead6574531c3) to protect the endpoints and to identify the requester.
- For the different expense categories, you can use the following list (feel free to decide how to implement this as part of your data model):
- Groceries
- Leisure
- Electronics
- Utilities
- Clothing
- Health
- Others
<hr />
This is the last "beginner" project in the backend roadmap. If you have completed all the projects in the backend roadmap, you should have a good understanding of how to build a backend application. You can now move on to the "intermediate" projects in the backend roadmap.

@ -0,0 +1,85 @@
---
title: 'Expense Tracker'
description: 'Build a simple expense tracker to manage your finances.'
isNew: false
sort: 3
difficulty: 'beginner'
nature: 'CLI'
skills:
- 'Programming Language'
- 'CLI'
- 'Filesystem'
- 'Logic Building'
seo:
title: 'Expense Tracker'
description: 'Build a simple expense tracker application to manage your finances.'
keywords:
- 'expense tracker cli'
- 'backend project idea'
roadmapIds:
- 'backend'
- 'nodejs'
- 'python'
- 'java'
- 'golang'
- 'spring-boot'
---
Build a simple expense tracker application to manage your finances. The application should allow users to add, delete, and view their expenses. The application should also provide a summary of the expenses.
## Requirements
Application should run from the command line and should have the following features:
- Users can add an expense with a description and amount.
- Users can update an expense.
- Users can delete an expense.
- Users can view all expenses.
- Users can view a summary of all expenses.
- Users can view a summary of expenses for a specific month (of current year).
Here are some additional features that you can add to the application:
- Add expense categories and allow users to filter expenses by category.
- Allow users to set a budget for each month and show a warning when the user exceeds the budget.
- Allow users to export expenses to a CSV file.
The list of commands and their expected output is shown below:
```bash
$ expense-tracker add --description "Lunch" --amount 20
# Expense added successfully (ID: 1)
$ expense-tracker add --description "Dinner" --amount 10
# Expense added successfully (ID: 2)
$ expense-tracker list
# ID Date Description Amount
# 1 2024-08-06 Lunch $20
# 2 2024-08-06 Dinner $10
$ expense-tracker summary
# Total expenses: $30
$ expense-tracker delete --id 1
# Expense deleted successfully
$ expense-tracker summary
# Total expenses: $20
$ expense-tracker summary --month 8
# Total expenses for August: $20
```
## Implementation
You can implement the application using any programming language of your choice. Here are some suggestions:
- Use any programming language for any available module for parsing command arguments (e.g. python with the `argparse`, node.js with `commander` etc).
- Use a simple text file to store the expenses data. You can use JSON, CSV, or any other format to store the data.
- Add error handling to handle invalid inputs and edge cases (e.g. negative amounts, non-existent expense IDs, etc).
- Use functions to modularize the code and make it easier to test and maintain.
<hr />
This project idea is a great way to practice your logic building skills and learn how to interact with the filesystem using a CLI application. It will also help you understand how to manage data and provide useful information to users in a structured way.

@ -0,0 +1,69 @@
---
title: 'Workout Tracker'
description: 'App to let users track their workouts and progress.'
isNew: false
sort: 14
difficulty: 'intermediate'
nature: 'API'
skills:
- 'Programming Language'
- 'Date and Time'
- 'Database'
- 'Seeder'
- 'OpenAPI'
seo:
title: 'Workout Tracker Project Idea'
description: 'App to let users track their workouts and progress.'
keywords:
- 'backend project idea'
roadmapIds:
- 'backend'
- 'nodejs'
- 'python'
- 'java'
- 'golang'
- 'spring-boot'
---
This project involves creating a backend system for a workout tracker application where users can sign up, log in, create workout plans, and track their progress. The system will feature JWT authentication, CRUD operations for workouts, and generate reports on past workouts.
## Requirements
You are required to develop an API for a workout tracker application that allows users to manage their workouts and track their progress. Your first task is to think about the database schema and the API endpoints that will be needed to support the application's functionality. Here are some of the key features you should consider:
### Exercise Data
You should write a data seeder to populate the database with a list of exercises. Each exercise should have a name, description, and category (e.g., cardio, strength, flexibility) or muscle group (e.g., chest, back, legs). Exercises will be used to create workout plans.
### User Authentication and Authorization
Users will be able to sign up, log in, and log out of the application. You should use JWT tokens for authentication and authorization. Only authenticated users should be able to create, update, and delete workout plans. Needless to say, users should only be able to access their own workout plans.
- **Sign-Up**: Allow users to create an account.
- **Login**: Allow users to log in to their account.
- **JWT**: Use JSON Web Tokens for authentication.
### Workout Management
Users will be able to create their workout plans. Workout plans should consist of multiple exercises, each with a set number of repetitions, sets, and weights. Users should be able to update and delete their workout plans. Additionally, users should be able to schedule workouts for specific dates and times.
- Create Workout: Allow users to create workouts composed of multiple exercises.
- Update Workout: Allow users to update workouts and add comments.
- Delete Workout: Allow users to delete workouts.
- Schedule Workouts: Allow users to schedule workouts for specific dates and times.
- List Workouts: List active or pending workouts sorted by date and time.
- Generate Reports: Generate reports on past workouts and progress.
## Constraints
You are free to choose the programming language and database of your choice. Actual decisions for the database schema, API endpoints, and other implementation details are up to you. However, you should consider the following constraints:
- **Database**: Use a relational database to store user data, workout plans, and exercise data.
- **API**: Develop a RESTful API to interact with the database.
- **Security**: Implement JWT authentication to secure the API endpoints.
- **Testing**: Write unit tests to ensure the correctness of your code.
- **Documentation**: Learn about OpenAPI Specs. Document your API endpoints and provide examples of how to use them.
<hr />
This project is a great way to practice building a backend system for a real-world application. You will learn how to design a database schema, implement user authentication, and create RESTful API endpoints. Additionally, you will gain experience in writing unit tests and documenting your code.

@ -0,0 +1,56 @@
---
title: 'GitHub User Activity'
description: 'Use GitHub API to fetch user activity and display it in the terminal.'
isNew: false
sort: 2
difficulty: 'beginner'
nature: 'CLI'
skills:
- 'Programming Language'
- 'API Consumption'
seo:
title: 'GitHub User Activity CLI'
description: 'Build a command line interface (CLI) to fetch and display GitHub user activity.'
keywords:
- 'github user activity cli'
- 'backend project idea'
roadmapIds:
- 'backend'
- 'nodejs'
- 'python'
- 'java'
- 'golang'
- 'spring-boot'
---
In this project, you will build a simple command line interface (CLI) to fetch the recent activity of a GitHub user and display it in the terminal. This project will help you practice your programming skills, including working with APIs, handling JSON data, and building a simple CLI application.
## Requirements
The application should run from the command line, accept the GitHub username as an argument, fetch the user's recent activity using the GitHub API, and display it in the terminal. The user should be able to:
- Provide the GitHub username as an argument when running the CLI.
```bash
github-activity <username>
```
- Fetch the recent activity of the specified GitHub user using the GitHub API. You can use the following endpoint to fetch the user's activity:
```
# https://api.github.com/users/<username>/events
# Example: https://api.github.com/users/kamranahmedse/events
```
- Display the fetched activity in the terminal.
```
Output:
- Pushed 3 commits to kamranahmedse/developer-roadmap
- Opened a new issue in kamranahmedse/developer-roadmap
- Starred kamranahmedse/developer-roadmap
- ...
```
You can [learn more about the GitHub API here](https://docs.github.com/en/rest/activity/events?apiVersion=2022-11-28).
- Handle errors gracefully, such as invalid usernames or API failures.
- Use a programming language of your choice to build this project.
- Do not use any external libraries or frameworks to fetch the GitHub activity.
<hr />
If you are looking to build a more advanced version of this project, you can consider adding features like filtering the activity by event type, displaying the activity in a more structured format, or caching the fetched data to improve performance. You can also explore other endpoints of the GitHub API to fetch additional information about the user or their repositories.

@ -0,0 +1,152 @@
---
title: 'Image Processing Service'
description: 'Build a service that allows users to upload and process images.'
isNew: false
sort: 15
difficulty: 'intermediate'
nature: 'API'
skills:
- 'Programming Language'
- 'Image Processing'
- 'Database'
- 'Queues'
seo:
title: 'Image Processing Service Project Idea'
description: 'Build a service that allows users to upload and process images.'
keywords:
- 'image processing service'
- 'backend project idea'
roadmapIds:
- 'backend'
- 'nodejs'
- 'python'
- 'java'
- 'golang'
- 'spring-boot'
---
This project involves creating a backend system for an image processing service similar to Cloudinary. The service will allow users to upload images, perform various transformations, and retrieve images in different formats. The system will feature user authentication, image upload, transformation operations, and efficient retrieval mechanisms.
## Requirements
Here is the list of features that you should implement in this project:
### User Authentication
- **Sign-Up**: Allow users to create an account.
- **Log-In**: Allow users to log into their account.
- **JWT Authentication**: Secure endpoints using JWT tokens for authenticated access.
### Image Management
- **Upload Image**: Allow users to upload images.
- **Transform Image**: Allow users to perform various transformations (resize, crop, rotate, watermark etc.).
- **Retrieve Image**: Allow users to retrieve a saved image in different formats.
- **List Images**: List all uploaded images by the user with metadata.
### Image Transformation
Here is the list of transformations that you can implement:
- Resize
- Crop
- Rotate
- Watermark
- Flip
- Mirror
- Compress
- Change format (JPEG, PNG, etc.)
- Apply filters (grayscale, sepia, etc.)
Feel free to add more transformations based on your interest and expertise.
## How to Implement
Here is the list of endpoints that you can implement for this project:
### Authentication Endpoints
Register a new user:
```
POST /register
{
"username": "user1",
"password": "password123"
}
```
Response should be the user object with a JWT token.
Log in an existing user:
```
POST /login
{
"username": "user1",
"password": "password123"
}
```
Response should be the user object with a JWT token.
### Image Management Endpoints
#### Upload an image:
```
POST /images
Request Body: Multipart form-data with image file
Response: Uploaded image details (URL, metadata).
```
#### Apply transformations to an image:
```
POST /images/:id/transform
{
"transformations": {
"resize": {
"width": "number",
"height": "number"
},
"crop": {
"width": "number",
"height": "number",
"x": "number",
"y": "number"
},
"rotate": "number",
"format": "string",
"filters": {
"grayscale": "boolean",
"sepia": "boolean"
}
}
}
```
User can apply one or more transformations to the image. Response should be the transformed image details (URL, metadata).
#### Retrieve an image:
```
GET /images/:id
```
Response should be the image actual image detail.
#### Get a paginated list of images:
```
GET /images?page=1&limit=10
```
## Tips
- Use a cloud storage service like AWS S3, Cloudflare R2, or Google Cloud Storage to store images.
- Use some image processing libraries to apply transformations.
- Put a rate limit on image transformations to prevent abuse.
- Consider caching transformed images to improve performance.
- Implement error handling and validation for all endpoints.
- Optionally use a message queue like RabbitMQ or Kafka to process image transformations asynchronously.
<hr />
This project will help you understand how to build a scalable image processing service with user authentication and image transformation capabilities. You can use this project to showcase your backend development skills and learn about image processing techniques.

@ -0,0 +1,42 @@
---
title: 'Markdown Note-taking App'
description: 'Build a note-taking app that uses markdown for formatting.'
isNew: false
sort: 10
difficulty: 'intermediate'
nature: 'API'
skills:
- 'Programming Language'
- 'Text Processing'
- 'Markdown libraries'
- 'File Uploads'
seo:
title: 'Markdown Note-taking App Project Idea'
description: 'Build an API for an expense tracker application.'
keywords:
- 'backend project idea'
roadmapIds:
- 'backend'
- 'nodejs'
- 'python'
- 'java'
- 'golang'
- 'spring-boot'
---
You are required to build a simple note-taking app that lets users upload markdown files, check the grammar, save the note, and render it in HTML. The goal of this project is to help you learn how to handle file uploads in a RESTful API, parse and render markdown files using libraries, and check the grammar of the notes.
![Markdown Note-taking App](https://assets.roadmap.sh/guest/markdown-note-taking-app-tymi3.png)
## Features
You have to implement the following features:
- You’ll provide an endpoint to check the grammar of the note.
- You’ll also provide an endpoint to save the note that can be passed in as Markdown text.
- Provide an endpoint to list the saved notes (i.e. uploaded markdown files).
- Return the HTML version of the Markdown note (rendered note) through another endpoint.
## Tips to Get Started
Feel free to use any programming language and framework of your choice. Use the package manager of the chosen language to install the required libraries for parsing and rendering markdown files.

@ -0,0 +1,70 @@
---
title: 'Movie Reservation System'
description: 'Build a system that allows users to reserve movie tickets.'
isNew: false
sort: 16
difficulty: 'advanced'
nature: 'API'
skills:
- 'Programming Language'
- 'Database'
- 'Scheduling'
- 'Authentication'
seo:
title: 'Movie Reservation System Project Idea'
description: 'Build a system that allows users to reserve movie tickets.'
keywords:
- 'movie reservation system'
- 'backend project idea'
roadmapIds:
- 'backend'
- 'nodejs'
- 'python'
- 'java'
- 'golang'
- 'spring-boot'
---
You are required to build backend system for a movie reservation service. The service will allow users to sign up, log in, browse movies, reserve seats for specific showtimes, and manage their reservations. The system will feature user authentication, movie and showtime management, seat reservation functionality, and reporting on reservations.
## Goal
The goal of this project is to help you understand how to implement complex business logic i.e. seat reservation and scheduling, thinking about the data model and relationships, and complex queries.
## Requirements
We have intentionally left out the implementation details to encourage you to think about the design and implementation of the system. However here are some requirements that you can consider:
### User Authentication and Authorization
- Users should be able to sign up and log in.
- You also need roles for users, such as admin and regular user. Admins should be able to manage movies and showtimes.
- Regular users should be able to reserve seats for a showtime.
> You can create the initial admin using seed data. Only admins should be able to promote other users to admin and be able to do things related to movie management, reporting, etc.
### Movie Management
- Admins should be able to add, update, and delete movies.
- Each movie should have a title, description, and poster image.
- Movies should be categorized by genre.
- Movies should have showtimes.
### Reservation Management
- Users should be able to get the movies and their show times for a specific date.
- Users should be able to reserve seats for a showtime, see the available seats, and select the seats they want.
- Users should be able to see their reservations and cancel them (only upcoming ones).
- Admins should be able to see all reservations, capacity, and revenue.
## Implementation Considerations
- Think about the data model and relationships between entities.
- Think about how you will avoid overbooking, and how you will handle seat reservations.
- Think about how you will handle the scheduling of showtimes.
- Think about how you will handle the reporting of reservations.
- Think about how you will handle the authentication and authorization of users.
<hr />
This project is quite complex and will require you to think about the design and implementation of the system. You can use any programming language and database of your choice. I would recommend using a relational database such as MySQL or PostgreSQL. Once you have finished this project, you will have a good understanding of how to implement complex business logic, think about the data model and relationships, and complex queries. You can also extend this project by adding more features such as payment processing, email notifications, etc.

@ -0,0 +1,74 @@
---
title: 'Personal Blog'
description: 'Build a personal blog to write and publish articles on various topics.'
isNew: false
sort: 5
difficulty: 'beginner'
nature: 'Web App'
skills:
- 'Programming Language'
- 'Basic Authentication'
- 'Markdown'
- 'Filesystem'
- 'Libraries'
seo:
title: 'Personal Blog Project Idea'
description: 'Build a personal blog to write and publish articles on various topics. Learn how to create a blog using a programming language and frontend technologies.'
keywords:
- 'personal blog project'
- 'backend project idea'
roadmapIds:
- 'backend'
- 'nodejs'
- 'python'
- 'java'
- 'golang'
- 'spring-boot'
---
You are required to build a personal blog where you can write and publish articles. The blog will have two sections: a guest section and an admin section.
**Guest Section** — A list of pages that can be accessed by anyone:
- **Home Page:** This page will display the list of articles published on the blog.
- **Article Page:** This page will display the content of the article along with the date of publication.
**Admin Section** — are the pages that only you can access to publish, edit, or delete articles.
- **Dashboard:** This page will display the list of articles published on the blog along with the option to add a new article, edit an existing article, or delete an article.
- **Add Article Page:** This page will contain a form to add a new article. The form will have fields like title, content, and date of publication.
- **Edit Article Page:** This page will contain a form to edit an existing article. The form will have fields like title, content, and date of publication.
Here are the mockups to give you an idea of the different pages of the blog.
Pages that anyone can access
![Personal Blog](https://assets.roadmap.sh/guest/blog-guest-pages.png)
Pages that only the admin can access
![Personal Blog](https://assets.roadmap.sh/guest/blog-admin-pages.png)
## How to Implement
Here are some guidelines to help you implement the personal blog:
### Storage
To keep things simple for now, you can use the filesystem to store the articles. Each article will be stored as a separate file in a directory. The file will contain the title, content, and date of publication of the article. You can use JSON or Markdown format to store the articles.
### Backend
You can use any programming language to build the backend of the blog. You don't have to make it as an API for this project, we have other projects for that. You can have pages that render the HTML directly from the server and forms that submit data to the server.
### Frontend
For the frontend, you can use HTML and CSS (no need for JavaScript for now). You can use any templating engine to render the articles on the frontend.
### Authentication
You can implement basic authentication for the admin section. You can either use the [standard HTTP basic authentication](https://youtu.be/mwccHwUn7Gc?t=20) or simply hardcode the username and password in the code for now and create a simple login page that will create a session for the admin.
<hr />
After completing this project, you will have practised templating, filesystem operations, basic authentication, form handling, and rendering HTML pages from the server. You can extend this project further by adding features like comments, categories, tags, search functionality, etc. Make sure to check the other backend projects that go into more advanced topics like databases, APIs, security best practices etc.

@ -0,0 +1,48 @@
---
title: 'Real-time Leaderboard'
description: 'Create a real-time leaderboard system for ranking and scoring.'
isNew: false
sort: 17
difficulty: 'advanced'
nature: 'API'
skills:
- 'Programming Language'
- 'Database'
- 'Scheduling'
- 'Authentication'
seo:
title: 'Real-time Leaderboard System Idea'
description: 'Create a real-time leaderboard system that updates scores in real-time.'
keywords:
- 'movie reservation system'
- 'backend project idea'
roadmapIds:
- 'backend'
- 'nodejs'
- 'python'
- 'java'
- 'golang'
- 'spring-boot'
---
This project involves creating a backend system for a real-time leaderboard service. The service will allow users to compete in various games or activities, track their scores, and view their rankings on a leaderboard. The system will feature user authentication, score submission, real-time leaderboard updates, and score history tracking. Redis sorted sets will be used to manage and query the leaderboards efficiently.
## Project Requirements
You are to build an imaginary real-time leaderboard system that ranks users based on their scores in various games or activities. The system should meet the following requirements:
1. **User Authentication**: Users should be able to register and log in to the system.
2. **Score Submission**: Users should be able to submit their scores for different games or activities.
3. **Leaderboard Updates**: Display a global leaderboard showing the top users across all games.
4. **User Rankings**: Users should be able to view their rankings on the leaderboard.
5. **Top Players Report:** Generate reports on the top players for a specific period.
## Tip - Use Redis Sorted Sets
- **Leaderboard Storage:** Use Redis sorted sets to store and manage leaderboards.
- **Real-Time Updates:** Utilize Redis sorted sets for efficient real-time updates and queries.
- **Rank Queries:** Use Redis commands to query user ranks and leaderboard positions.
<hr />
After finishing this project, you will have a good understanding of how to create a real-time leaderboard system that updates scores in real-time. You will also gain experience working with Redis sorted sets and implementing user authentication and score submission features.

@ -0,0 +1,124 @@
---
title: 'Task Tracker'
description: 'Build a CLI app to track your tasks and manage your to-do list.'
isNew: false
sort: 1
difficulty: 'beginner'
nature: 'CLI'
skills:
- 'Programming Language'
- 'CLI'
- 'Filesystem'
seo:
title: 'Task Tracker CLI'
description: 'Build a command line interface (CLI) to track your tasks and manage your to-do list.'
keywords:
- 'task tracker cli'
- 'backend project idea'
roadmapIds:
- 'backend'
- 'nodejs'
- 'python'
- 'java'
- 'golang'
- 'spring-boot'
---
Task tracker is a project used to track and manage your tasks. In this task, you will build a simple command line interface (CLI) to track what you need to do, what you have done, and what you are currently working on. This project will help you practice your programming skills, including working with the filesystem, handling user inputs, and building a simple CLI application.
## Requirements
The application should run from the command line, accept user actions and inputs as arguments, and store the tasks in a JSON file. The user should be able to:
- Add, Update, and Delete tasks
- Mark a task as in progress or done
- List all tasks
- List all tasks that are done
- List all tasks that are not done
- List all tasks that are in progress
Here are some constraints to guide the implementation:
- You can use any programming language to build this project.
- Use positional arguments in command line to accept user inputs.
- Use a JSON file to store the tasks in the current directory.
- The JSON file should be created if it does not exist.
- Use the native file system module of your programming language to interact with the JSON file.
- Do not use any external libraries or frameworks to build this project.
- Ensure to handle errors and edge cases gracefully.
### Example
The list of commands and their usage is given below:
```bash
# Adding a new task
task-cli add "Buy groceries"
# Output: Task added successfully (ID: 1)
# Updating and deleting tasks
task-cli update 1 "Buy groceries and cook dinner"
task-cli delete 1
# Marking a task as in progress or done
task-cli mark-in-progress 1
task-cli mark-done 1
# Listing all tasks
task-cli list
# Listing tasks by status
task-cli list done
task-cli list todo
task-cli list in-progress
```
### Task Properties
Each task should have the following properties:
- `id`: A unique identifier for the task
- `description`: A short description of the task
- `status`: The status of the task (`todo`, `in-progress`, `done`)
- `createdAt`: The date and time when the task was created
- `updatedAt`: The date and time when the task was last updated
Make sure to add these properties to the JSON file when adding a new task and update them when updating a task.
<hr />
## Getting Started
Here are a few steps to help you get started with the Task Tracker CLI project:
### Set Up Your Development Environment
- Choose a programming language you are comfortable with (e.g., Python, JavaScript, etc.).
- Ensure you have a code editor or IDE installed (e.g., VSCode, PyCharm).
### Project Initialization
- Create a new project directory for your Task Tracker CLI.
- Initialize a version control system (e.g., Git) to manage your project.
### Implementing Features
- Start by creating a basic CLI structure to handle user inputs.
- Implement each feature one by one, ensuring to test thoroughly before moving to the next e.g. implement adding task functionality first, listing next, then updating, marking as in progress, etc.
### Testing and Debugging
- Test each feature individually to ensure they work as expected. Look at the JSON file to verify that the tasks are being stored correctly.
- Debug any issues that arise during development.
### Finalizing the Project
- Ensure all features are implemented and tested.
- Clean up your code and add comments where necessary.
- Write a good readme file on how to use your Task Tracker CLI.
<hr />
By the end of this project, you will have developed a practical tool that can help you or others manage tasks efficiently. This project lays a solid foundation for more advanced programming projects and real-world applications.
Happy coding!

@ -0,0 +1,211 @@
---
title: 'Todo List API'
description: 'Build a RESTful API to allow users to manage their to-do list.'
isNew: false
sort: 7
difficulty: 'beginner'
nature: 'API'
skills:
- 'RESTful API'
- 'Database'
- 'CRUD Operations'
- 'Data Modeling'
- 'Authentication'
seo:
title: 'Todo List API Project Idea'
description: 'Build a RESTful API to allow users to manage their to-do list.'
keywords:
- 'todo list api'
- 'backend project idea'
roadmapIds:
- 'backend'
- 'nodejs'
- 'python'
- 'java'
- 'golang'
- 'spring-boot'
---
In this project you are required to develop a RESTful API to allow users to manage their to-do list. The previous backend projects have only focused on the CRUD operations, but this project will require you to implement user authentication as well.
![Todo List API](https://assets.roadmap.sh/guest/todo-list-api-bsrdd.png)
## Goals
The skills you will learn from this project include:
- User authentication
- Schema design and Databases
- RESTful API design
- CRUD operations
- Error handling
- Security
## Requirements
You are required to develop a RESTful API with following endpoints
- User registration to create a new user
- Login endpoint to authenticate the user and generate a token
- CRUD operations for managing the to-do list
- Implement user authentication to allow only authorized users to access the to-do list
- Implement error handling and security measures
- Use a database to store the user and to-do list data (you can use any database of your choice)
- Implement proper data validation
- Implement pagination and filtering for the to-do list
Given below is a list of the endpoints and the details of the request and response:
### User Registration
Register a new user using the following request:
```
POST /register
{
"name": "John Doe",
"email": "john@doe.com"
"password": "password"
}
```
This will validate the given details, make sure the email is unique and store the user details in the database. Make sure to hash the password before storing it in the database. Respond with a token that can be used for authentication if the registration is successful.
```json
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
}
```
The token can either be a JWT token or a random string that can be used for authentication. We leave it up to you to decide the implementation details.
### User Login
Authenticate the user using the following request:
```
POST /login
{
"email": "john@doe.com",
"password": "password"
}
```
This will validate the given email and password, and respond with a token if the authentication is successful.
```json
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
}
```
### Create a To-Do Item
Create a new to-do item using the following request:
```
POST /todos
{
"title": "Buy groceries",
"description": "Buy milk, eggs, and bread"
}
```
User must send the token received from the login endpoint in the header to authenticate the request. You can use the `Authorization` header with the token as the value. In case the token is missing or invalid, respond with an error and status code 401.
```json
{
"message": "Unauthorized"
}
```
Upon successful creation of the to-do item, respond with the details of the created item.
```json
{
"id": 1,
"title": "Buy groceries",
"description": "Buy milk, eggs, and bread"
}
```
### Update a To-Do Item
Update an existing to-do item using the following request:
```
PUT /todos/1
{
"title": "Buy groceries",
"description": "Buy milk, eggs, bread, and cheese"
}
```
Just like the create todo endpoint, user must send the token received. Also make sure to validate the user has the permission to update the to-do item i.e. the user is the creator of todo item that they are updating. Respond with an error and status code `403` if the user is not authorized to update the item.
```json
{
"message": "Forbidden"
}
```
Upon successful update of the to-do item, respond with the updated details of the item.
```json
{
"id": 1,
"title": "Buy groceries",
"description": "Buy milk, eggs, bread, and cheese"
}
```
### Delete a To-Do Item
Delete an existing to-do item using the following request:
```
DELETE /todos/1
```
User must be authenticated and authorized to delete the to-do item. Upon successful deletion, respond with the status code `204`.
### Get To-Do Items
Get the list of to-do items using the following request:
```
GET /todos?page=1&limit=10
```
User must be authenticated to access the tasks and the response should be paginated. Respond with the list of to-do items along with the pagination details.
```json
{
"data": [
{
"id": 1,
"title": "Buy groceries",
"description": "Buy milk, eggs, bread"
},
{
"id": 2,
"title": "Pay bills",
"description": "Pay electricity and water bills"
}
],
"page": 1,
"limit": 10,
"total": 2
}
```
## Bonus
- Implement filtering and sorting for the to-do list
- Implement unit tests for the API
- Implement rate limiting and throttling for the API
- Implement refresh token mechanism for the authentication
<hr />
This project will help you understand how to design and implement a RESTful API with user authentication. You will also learn how to work with databases, handle errors, and implement security measures.

@ -0,0 +1,52 @@
---
title: 'Unit Converter'
description: 'Unit converter to convert between different units of measurement.'
isNew: false
sort: 4
difficulty: 'beginner'
nature: 'Web App'
skills:
- 'Programming Language'
- 'HTML'
- 'Server'
- 'Logic Building'
seo:
title: 'Unit Converter'
description: 'Build a Unit Converter to convert between different units of measurement.'
keywords:
- 'unit converter'
- 'backend project idea'
roadmapIds:
- 'backend'
- 'nodejs'
- 'python'
- 'java'
- 'golang'
- 'spring-boot'
---
You are required to build a simple web app that can convert between different units of measurement. It can convert units of length, weight, volume, area, temperature, and more. The user can input a value and select the units to convert from and to. The application will then display the converted value.
## Requirements
Build a simple web page that will have different sections for different units of measurement. The user can input a value to convert, select the units to convert from and to, and view the converted value.
- The user can input a value to convert.
- The user can select the units to convert from and to.
- The user can view the converted value.
- The user can convert between different units of measurement like length, weight, temperature, etc (more given below).
You can include the following units of measurement to convert between:
- Length: millimeter, centimeter, meter, kilometer, inch, foot, yard, mile.
- Weight: milligram, gram, kilogram, ounce, pound.
- Temperature: Celsius, Fahrenheit, Kelvin.
## How it works
You don't need to use any database for this project. There will be a simple webpage that will submit the form to the server and get the converted value back and display it on the webpage.
![Unit Converter](https://assets.roadmap.sh/guest/unit-converter-be-project.png)
You can have 3 webpages for each type of unit conversion (length, weight, temperature) with forms to input the value and select the units to convert from and to. Submitting a form will submit the data to the current page (i.e. `target="_self"`) and display the converted value. You can do this using the backend programming language of your choice i.e. check if the form is submitted and then calculate the converted value and display it on the webpage, if not submitted then display the form.

@ -0,0 +1,168 @@
---
title: 'URL Shortening Service'
description: 'Build a URL Shortener API that helps shorten long URLs.'
isNew: false
sort: 11
difficulty: 'intermediate'
nature: 'API'
skills:
- 'Programming Language'
- 'Database Indexing'
- 'HTTP Redirects'
seo:
title: 'URL Shortening Service Project Idea'
description: 'Build a URL Shortener API that helps shorten long URLs.'
keywords:
- 'url shortening service'
- 'backend project idea'
roadmapIds:
- 'backend'
- 'nodejs'
- 'python'
- 'java'
- 'golang'
- 'spring-boot'
---
You are required to create a simple RESTful API that allows users to shorten long URLs. The API should provide endpoints to create, retrieve, update, and delete short URLs. It should also provide statistics on the number of times a short URL has been accessed.
![URL Shortening Service](https://assets.roadmap.sh/guest/url-shortener-architecture-u72mu.png)
## Requirements
You should create a RESTful API for a URL shortening service. The API should allow users to perform the following operations:
- Create a new short URL
- Retrieve an original URL from a short URL
- Update an existing short URL
- Delete an existing short URL
- Get statistics on the short URL (e.g., number of times accessed)
You can optionally setup a minimal frontend to interact with the API and setup redirects for the short URLs to the original URLs.
## API Endpoints
Given below are the details for each API operation.
### Create Short URL
Create a new short URL using the `POST` method
```plaintext
POST /shorten
{
"url": "https://www.example.com/some/long/url"
}
```
The endpoint should validate the request body and return a `201 Created` status code with the newly created short URL i.e.
```json
{
"id": "1",
"url": "https://www.example.com/some/long/url",
"shortCode": "abc123",
"createdAt": "2021-09-01T12:00:00Z",
"updatedAt": "2021-09-01T12:00:00Z"
}
```
or a `400 Bad Request` status code with error messages in case of validation errors. Short codes must be unique and should be generated randomly.
## Retrieve Original URL
Retrieve the original URL from a short URL using the `GET` method
```plaintext
GET /shorten/abc123
```
The endpoint should return a `200 OK` status code with the original URL i.e.
```json
{
"id": "1",
"url": "https://www.example.com/some/long/url",
"shortCode": "abc123",
"createdAt": "2021-09-01T12:00:00Z",
"updatedAt": "2021-09-01T12:00:00Z"
}
```
or a `404 Not Found` status code if the short URL was not found. Your frontend should be responsible for retrieving the original URL using the short URL and redirecting the user to the original URL.
## Update Short URL
Update an existing short URL using the `PUT` method
```plaintext
PUT /shorten/abc123
{
"url": "https://www.example.com/some/updated/url"
}
```
The endpoint should validate the request body and return a `200 OK` status code with the updated short URL i.e.
```json
{
"id": "1",
"url": "https://www.example.com/some/updated/url",
"shortCode": "abc123",
"createdAt": "2021-09-01T12:00:00Z",
"updatedAt": "2021-09-01T12:30:00Z"
}
```
or a `400 Bad Request` status code with error messages in case of validation errors. It should return a `404 Not Found` status code if the short URL was not found.
### Delete Short URL
Delete an existing short URL using the `DELETE` method
```plaintext
DELETE /shorten/abc123
```
The endpoint should return a `204 No Content` status code if the short URL was successfully deleted or a `404 Not Found` status code if the short URL was not found.
### Get URL Statistics
Get statistics for a short URL using the `GET` method
```plaintext
GET /shorten/abc123/stats
```
The endpoint should return a `200 OK` status code with the statistics i.e.
```json
{
"id": "1",
"url": "https://www.example.com/some/long/url",
"shortCode": "abc123",
"createdAt": "2021-09-01T12:00:00Z",
"updatedAt": "2021-09-01T12:00:00Z",
"accessCount": 10
}
```
or a `404 Not Found` status code if the short URL was not found.
<hr />
## Tech Stack
Feel free to use any programming language, framework, and database of your choice for this project. Here are some suggestions:
- If you are using JavaScript, you can use Node.js with Express.js
- If you are using Python, you can use Flask or Django
- If you are using Java, you can use Spring Boot
- If you are using Ruby, you can use Ruby on Rails
For databases, you can use:
- MySQL if you are using SQL
- MongoDB if you are using NoSQL
Your job is to implement the core functionality of the API, focusing on creating, retrieving, updating, and deleting short URLs, as well as tracking and retrieving access statistics. You don't have to implement authentication or authorization for this project.

@ -0,0 +1,50 @@
---
title: 'Weather API'
description: 'Build a weather API that fetches and returns weather data.'
isNew: false
sort: 7
difficulty: 'beginner'
nature: 'API'
skills:
- 'Programming Language'
- '3rd Party APIs'
- 'Caching'
- 'Environment Variables'
seo:
title: 'Weather API Project Idea'
description: 'Build a weather API that fetches and returns weather data from a 3rd party API.'
keywords:
- 'weather api'
- 'backend project idea'
roadmapIds:
- 'backend'
- 'nodejs'
- 'python'
- 'java'
- 'golang'
- 'spring-boot'
---
In this project, instead of relying on our own weather data, we will build a weather API that fetches and returns weather data from a 3rd party API. This project will help you understand how to work with 3rd party APIs, caching, and environment variables.
![Weather API](https://assets.roadmap.sh/guest/weather-api-f8i1q.png)
As for the actual weather API to use, you can use your favorite one, as a suggestion, here is a link to [Visual Crossing’s API](https://www.visualcrossing.com/weather-api), it’s completely FREE and easy to use.
Regarding the in-memory cache, a pretty common recommendation is to use [Redis](https://redis.io/), you can read more about it [here](https://redis.io/docs/manual/client-side-caching/), and as a recommendation, you could use the city code entered by the user as the key, and save there the result from calling the API.
At the same time, when you “set” the value in the cache, you can also give it an expiration time in seconds (using the `EX` flag on the `SET` command). That way the cache (the keys) will automatically clean itself when the data is old enough (for example, giving it a 12-hours expiration time).
<hr />
## Some Tips
Here are some tips to help you get started:
- Start by creating a simple API that returns a hardcoded weather response. This will help you understand how to structure your API and how to handle requests.
- Use environment variables to store the API key and the Redis connection string. This way, you can easily change them without having to modify your code.
- Make sure to handle errors properly. If the 3rd party API is down, or if the city code is invalid, make sure to return the appropriate error message.
- Use some package or module to make HTTP requests e.g. if you are using Node.js, you can use the `axios` package, if you are using Python, you can use the `requests` module.
- Implement rate limiting to prevent abuse of your API. You can use a package like `express-rate-limit` if you are using Node.js or `flask-limiter` if you are using Python.
This project will help you understand how to work with 3rd party APIs, caching, and environment variables. It will also help you understand how to structure your API and how to handle requests.

@ -25,7 +25,7 @@ function linkGroupPathToId(filePath: string): string {
* @returns Promisifed linkGroup files * @returns Promisifed linkGroup files
*/ */
export async function getAllLinkGroups(): Promise<LinkGroupFileType[]> { export async function getAllLinkGroups(): Promise<LinkGroupFileType[]> {
const linkGroups = await import.meta.glob<LinkGroupFileType>( const linkGroups = import.meta.glob<LinkGroupFileType>(
'/src/data/link-groups/*.md', '/src/data/link-groups/*.md',
{ {
eager: true, eager: true,

@ -0,0 +1,95 @@
import type { MarkdownFileType } from './file';
import { getRoadmapById, type RoadmapFileType } from './roadmap.ts';
export const projectDifficulties = [
'beginner',
'intermediate',
'advanced',
] as const;
export type ProjectDifficultyType = (typeof projectDifficulties)[number];
export interface ProjectFrontmatter {
title: string;
description: string;
isNew: boolean;
sort: number;
difficulty: ProjectDifficultyType;
nature: string;
skills: string[];
seo: {
title: string;
description: string;
keywords: string[];
ogImageUrl: string;
};
roadmapIds: string[];
}
export type ProjectFileType = MarkdownFileType<ProjectFrontmatter> & {
id: string;
roadmaps: RoadmapFileType[];
};
/**
* Generates id from the given project file
* @param filePath Markdown file path
*
* @returns unique project identifier
*/
function projectPathToId(filePath: string): string {
const fileName = filePath.split('/').pop() || '';
return fileName.replace('.md', '');
}
export async function getProjectsByRoadmapId(
roadmapId: string,
): Promise<ProjectFileType[]> {
const projects = await getAllProjects();
return projects.filter((project) =>
project.frontmatter?.roadmapIds?.includes(roadmapId),
);
}
let tempProjects: ProjectFileType[] = [];
/**
* Gets all the projects sorted by the publishing date
* @returns Promisifed project files
*/
export async function getAllProjects(): Promise<ProjectFileType[]> {
if (tempProjects.length) {
return tempProjects;
}
const projects = import.meta.glob<ProjectFileType>(
'/src/data/projects/*.md',
{
eager: true,
},
);
tempProjects = Object.values(projects).map((projectFile) => ({
...projectFile,
id: projectPathToId(projectFile.file),
}));
return tempProjects;
}
export async function getProjectById(
groupId: string,
): Promise<ProjectFileType> {
const project = await import(`../data/projects/${groupId}.md`);
const roadmapIds = project.frontmatter.roadmapIds || [];
const roadmaps = await Promise.all(
roadmapIds.map((roadmapId: string) => getRoadmapById(roadmapId)),
);
return {
...project,
roadmaps: roadmaps,
id: projectPathToId(project.file),
};
}

@ -17,6 +17,7 @@ import { type RoadmapFrontmatter, getRoadmapIds } from '../../lib/roadmap';
import RoadmapNote from '../../components/RoadmapNote.astro'; import RoadmapNote from '../../components/RoadmapNote.astro';
import { RoadmapTitleQuestion } from '../../components/RoadmapTitleQuestion'; import { RoadmapTitleQuestion } from '../../components/RoadmapTitleQuestion';
import ResourceProgressStats from '../../components/ResourceProgressStats.astro'; import ResourceProgressStats from '../../components/ResourceProgressStats.astro';
import { getProjectsByRoadmapId } from '../../lib/project';
export async function getStaticPaths() { export async function getStaticPaths() {
const roadmapIds = await getRoadmapIds(); const roadmapIds = await getRoadmapIds();
@ -68,6 +69,7 @@ const ogImageUrl =
const question = roadmapData?.question; const question = roadmapData?.question;
const note = roadmapData.note; const note = roadmapData.note;
const projects = await getProjectsByRoadmapId(roadmapId);
--- ---
<BaseLayout <BaseLayout
@ -110,6 +112,7 @@ const note = roadmapData.note;
isUpcoming={roadmapData.isUpcoming} isUpcoming={roadmapData.isUpcoming}
isForkable={roadmapData.isForkable} isForkable={roadmapData.isForkable}
question={roadmapData.question} question={roadmapData.question}
projectCount={projects.length}
/> />
<div class='container mt-2.5'> <div class='container mt-2.5'>

@ -6,10 +6,12 @@ import RelatedRoadmaps from '../../components/RelatedRoadmaps.astro';
import RoadmapHeader from '../../components/RoadmapHeader.astro'; import RoadmapHeader from '../../components/RoadmapHeader.astro';
import { FolderKanbanIcon } from 'lucide-react'; import { FolderKanbanIcon } from 'lucide-react';
import { EmptyProjects } from '../../components/Projects/EmptyProjects'; import { EmptyProjects } from '../../components/Projects/EmptyProjects';
import { ProjectsList } from '../../components/Projects/ProjectsList';
import ShareIcons from '../../components/ShareIcons/ShareIcons.astro'; import ShareIcons from '../../components/ShareIcons/ShareIcons.astro';
import { TopicDetail } from '../../components/TopicDetail/TopicDetail'; import { TopicDetail } from '../../components/TopicDetail/TopicDetail';
import { UserProgressModal } from '../../components/UserProgress/UserProgressModal'; import { UserProgressModal } from '../../components/UserProgress/UserProgressModal';
import BaseLayout from '../../layouts/BaseLayout.astro'; import BaseLayout from '../../layouts/BaseLayout.astro';
import { getProjectsByRoadmapId } from '../../lib/project';
import { import {
generateArticleSchema, generateArticleSchema,
generateFAQSchema, generateFAQSchema,
@ -46,14 +48,32 @@ const ogImageUrl =
group: 'roadmap', group: 'roadmap',
resourceId: roadmapId, resourceId: roadmapId,
}); });
const descriptionNoun = {
'AI and Data Scientist': 'AI and Data Science',
'Game Developer': 'Game Development',
'Technical Writer': 'Technical Writing',
'Product Manager': 'Product Management',
};
const title = `${roadmapData.briefTitle} Projects`;
const description = `Project ideas to take you from beginner to advanced in ${descriptionNoun[roadmapData.briefTitle] || roadmapData.briefTitle}`;
// `Seeking backend projects to enhance your skills? Explore our top 20 project ideas, from simple apps to complex systems. Start building today!`
const seoTitle = `${roadmapData.briefTitle} Projects`;
const nounTitle =
descriptionNoun[roadmapData.briefTitle] || roadmapData.briefTitle;
const seoDescription = `Seeking ${nounTitle.toLowerCase()} projects to enhance your skills? Explore our top 20 project ideas, from simple apps to complex systems. Start building today!`;
const projects = await getProjectsByRoadmapId(roadmapId);
--- ---
<BaseLayout <BaseLayout
permalink={`/${roadmapId}`} permalink={`/${roadmapId}`}
title={roadmapData?.seo?.title} title={seoTitle}
description={seoDescription}
briefTitle={roadmapData.briefTitle} briefTitle={roadmapData.briefTitle}
ogImageUrl={ogImageUrl} ogImageUrl={ogImageUrl}
description={roadmapData.seo.description}
keywords={roadmapData.seo.keywords} keywords={roadmapData.seo.keywords}
noIndex={true} noIndex={true}
resourceId={roadmapId} resourceId={roadmapId}
@ -61,8 +81,8 @@ const ogImageUrl =
> >
<div class='bg-gray-50'> <div class='bg-gray-50'>
<RoadmapHeader <RoadmapHeader
title={roadmapData.title} title={title}
description={roadmapData.description} description={description}
note={roadmapData.note} note={roadmapData.note}
tnsBannerLink={roadmapData.tnsBannerLink} tnsBannerLink={roadmapData.tnsBannerLink}
roadmapId={roadmapId} roadmapId={roadmapId}
@ -71,14 +91,12 @@ const ogImageUrl =
isForkable={roadmapData.isForkable} isForkable={roadmapData.isForkable}
question={roadmapData.question} question={roadmapData.question}
activeTab='projects' activeTab='projects'
projectCount={projects.length}
/> />
<div class='container'> <div class='container'>
<div {projects.length === 0 && <EmptyProjects client:load />}
class='relative my-2.5 flex min-h-[400px] flex-col items-center justify-center rounded-lg border bg-white' {projects.length > 0 && <ProjectsList projects={projects} client:load />}
>
<EmptyProjects client:load />
</div>
</div> </div>
</div> </div>
</BaseLayout> </BaseLayout>

@ -0,0 +1,129 @@
---
import { EditorRoadmap } from '../../components/EditorRoadmap/EditorRoadmap';
import FrameRenderer from '../../components/FrameRenderer/FrameRenderer.astro';
import RelatedRoadmaps from '../../components/RelatedRoadmaps.astro';
import RoadmapHeader from '../../components/RoadmapHeader.astro';
import ShareIcons from '../../components/ShareIcons/ShareIcons.astro';
import { TopicDetail } from '../../components/TopicDetail/TopicDetail';
import { UserProgressModal } from '../../components/UserProgress/UserProgressModal';
import BaseLayout from '../../layouts/BaseLayout.astro';
import { Badge } from '../../components/Badge';
import {
generateArticleSchema,
generateFAQSchema,
} from '../../lib/jsonld-schema';
import { getOpenGraphImageUrl } from '../../lib/open-graph';
import { type RoadmapFrontmatter, getRoadmapIds } from '../../lib/roadmap';
import RoadmapNote from '../../components/RoadmapNote.astro';
import { RoadmapTitleQuestion } from '../../components/RoadmapTitleQuestion';
import ResourceProgressStats from '../../components/ResourceProgressStats.astro';
import {
getAllProjects,
getProjectById,
getProjectsByRoadmapId,
ProjectFrontmatter,
} from '../../lib/project';
import AstroIcon from '../../components/AstroIcon.astro';
import MarkdownFile from '../../components/MarkdownFile.astro';
import Github from '../github.astro';
export async function getStaticPaths() {
const projects = await getAllProjects();
return projects
.map((project) => project.id)
.map((projectId) => ({
params: { projectId },
}));
}
interface Params extends Record<string, string | undefined> {
projectId: string;
}
const { projectId } = Astro.params as Params;
const project = await getProjectById(projectId);
const projectData = project.frontmatter as ProjectFrontmatter;
let jsonLdSchema = [];
const ogImageUrl = projectData?.seo?.ogImageUrl || '/images/og-img.png';
const githubUrl = `https://github.com/kamranahmedse/developer-roadmap/tree/master/src/data/projects/${projectId}.md`;
---
<BaseLayout
permalink={`/projects/${projectId}`}
title={projectData?.seo?.title}
briefTitle={projectData.title}
ogImageUrl={ogImageUrl}
description={projectData.seo.description}
keywords={projectData.seo.keywords}
jsonLd={jsonLdSchema}
resourceId={projectId}
resourceType='project'
>
<div class='bg-gray-50'>
<div class='container'>
<div
class='my-3 flex flex-wrap flex-row items-center gap-1.5 rounded-md border bg-white px-2 py-2 text-sm'
>
<AstroIcon icon='map' class='h-4 w-4' />
Relevant roadmaps <span class='flex flex-row flex-wrap gap-1'>
{
project.roadmaps.map((roadmap) => (
<a
class='bg-gray-500 text-white text-sm px-1.5 rounded hover:bg-black transition-colors'
href={`/${roadmap.id}`}
>
{roadmap.frontmatter?.briefTitle}
</a>
))
}
</span>
</div>
<div class='mb-3 overflow-hidden rounded-lg border bg-white p-5'>
<div class='relative -mx-2 -mt-2 mb-5 rounded-lg bg-gray-100/70 p-5'>
<div class='absolute right-2 top-2'>
<Badge variant='yellow' text={projectData.difficulty} />
</div>
<div class='mb-5'>
<h1 class='mb-1.5 text-3xl font-semibold'>{projectData.title}</h1>
<p class='text-gray-500'>{projectData.description}</p>
</div>
<div class='mt-4'>
<div class='flex flex-row gap-1.5 flex-wrap'>
{
projectData.skills.map((skill) => (
<Badge variant='green' text={skill} />
))
}
</div>
</div>
</div>
<div
class='prose max-w-full prose-blockquote:font-normal prose-blockquote:text-gray-500 prose-h2:mb-3 prose-h2:mt-5 prose-h3:mb-1 prose-h3:mt-5 prose-p:mb-2 prose-pre:my-3 prose-ul:my-3.5 prose-hr:my-5 [&>ul>li]:my-1'
>
<project.Content />
</div>
<div
class='mt-5 flex flex-wrap items-center justify-center rounded-lg bg-yellow-100 p-2.5 text-sm'
>
<AstroIcon class='mr-2 inline-block h-5 w-5' icon='github' />
Found a mistake?
<a
class='ml-1 underline underline-offset-2'
href={githubUrl}
target='_blank'
>
Help us improve this page
</a>
</div>
</div>
</div>
</div>
</BaseLayout>

@ -74,6 +74,16 @@ a > code:before {
) )
hsla(203, 11%, 95%, 0.4); hsla(203, 11%, 95%, 0.4);
} }
.striped-bg {
background-image: repeating-linear-gradient(
-45deg,
transparent,
transparent 5px,
hsla(0, 0%, 0%, 0.025) 5px,
hsla(0, 0%, 0%, 0.025) 10px
);
background-size: 200% 200%;
}
.striped-loader { .striped-loader {
background-image: repeating-linear-gradient( background-image: repeating-linear-gradient(
@ -92,8 +102,8 @@ a > code:before {
-45deg, -45deg,
transparent, transparent,
transparent 5px, transparent 5px,
hsla(0, 0%, 0%, 0.20) 5px, hsla(0, 0%, 0%, 0.2) 5px,
hsla(0, 0%, 0%, 0.20) 10px hsla(0, 0%, 0%, 0.2) 10px
); );
background-size: 200% 200%; background-size: 200% 200%;
animation: barberpole 15s linear infinite; animation: barberpole 15s linear infinite;

Loading…
Cancel
Save