Skip to content

Commit ea617fc

Browse files
authored
Update README.md
1 parent af32361 commit ea617fc

File tree

1 file changed

+267
-69
lines changed

1 file changed

+267
-69
lines changed

README.md

Lines changed: 267 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,309 @@
1-
<div align="center"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/denoland/deno_logo2/main/deno_dark.svg"> <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/denoland/deno_logo2/main/deno_light.svg"> <img alt="Deno App Logo" src="https://raw.githubusercontent.com/denoland/deno_logo2/main/deno_light.svg" width="120"> </picture> <h1>Deno App</h1> <p><b>A simple, extensible Deno-powered web server with custom routing and static file support.</b></p> <p> <a href="https://deno.com/"><img alt="Deno" src="https://img.shields.io/badge/deno-%5E1.0.0-black?logo=deno&logoColor=white"></a> <a href="#"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue.svg"></a> <a href="#"><img alt="Status: Active" src="https://img.shields.io/badge/status-active-brightgreen"></a> </p> </div><div align="center"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/denoland/deno_logo2/main/deno_dark.svg"> <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/denoland/deno_logo2/main/deno_light.svg"> <img alt="Deno App Logo" src="https://raw.githubusercontent.com/denoland/deno_logo2/main/deno_light.svg" width="120"> </picture> <h1>Deno App</h1> <p><b>A simple, extensible Deno-powered web server with custom routing and static file support.</b></p> <p> <a href="https://deno.com/"><img alt="Deno" src="https://img.shields.io/badge/deno-%5E1.0.0-black?logo=deno&logoColor=white"></a> <a href="#"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue.svg"></a> <a href="#"><img alt="Status: Active" src="https://img.shields.io/badge/status-active-brightgreen"></a> </p> </div>
1+
<div align="center">
2+
<img src="https://raw.githubusercontent.com/denoland/deno_logo2/main/deno_light.svg" width="100" alt="Deno logo">
3+
<h1>Rest API From Scratch Using Deno</h1>
4+
<p><b>A simple, modular Deno-powered web server with custom routing and static file support.</b></p>
5+
<p>
6+
<img alt="Deno" src="https://img.shields.io/badge/deno-%5E1.0.0-black?logo=deno&logoColor=white">
7+
<img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue.svg">
8+
<img alt="Status: Active" src="https://img.shields.io/badge/status-active-brightgreen">
9+
</p>
10+
</div>
211

3-
📋 Table of Contents
4-
Features
12+
---
513

6-
Project Structure
14+
## 📋 Table of Contents
715

8-
Getting Started
16+
- [Overview](#overview)
17+
- [Features](#features)
18+
- [Project Structure](#project-structure)
19+
- [Getting Started](#getting-started)
20+
- [API Endpoints](#api-endpoints)
21+
- [Routing Logic](#routing-logic)
22+
- [Configuration](#configuration)
23+
- [Source Files](#source-files)
24+
- [Contributing](#contributing)
25+
- [License](#license)
26+
- [Credits](#credits)
927

10-
API Endpoints
28+
---
1129

12-
Routing Logic
30+
## 📝 Overview
1331

14-
Configuration
32+
This project is a minimalist Deno web server featuring:
1533

16-
Screenshots
34+
- A custom router for clean HTTP GET route handling
35+
- Static file serving from nested directories
36+
- Basic user data API endpoints
37+
- An HTML landing page
1738

18-
Contributing
39+
It is designed for clarity, modularity, and easy extension, following a single-file, clean interface philosophy[8][11].
1940

20-
License
41+
---
2142

22-
Credits
43+
## ✨ Features
2344

24-
✨ Features
25-
🚦 Custom lightweight router for HTTP GET routes
45+
- 🚦 **Custom lightweight router** for HTTP GET routes
46+
- 🗂️ **Static file serving** (including images) from nested directories
47+
- 👤 **User API endpoints** (`/users`, `/user/:id`)
48+
- 🏠 **HTML landing page** (`index.html`)
49+
- 🛠️ **Easy local development** and extension
2650

27-
🗂️ Static file serving (including images) from nested directories
51+
---
2852

29-
👤 User API endpoints (/users, /user/:id)
53+
## 🗂️ Project Structure
3054

31-
🏠 HTML landing page (index.html)
55+
| File/Folder | Purpose |
56+
|-------------------|--------------------------------------------------------|
57+
| `main.ts` | Main server entrypoint, registers all routes |
58+
| `Router.ts` | Custom Router class for HTTP routes |
59+
| `StaticFile.ts` | Utility for serving static files (optional) |
60+
| `index.html` | Landing page served at `/` |
61+
| `deno.json` | Deno configuration and task definitions |
62+
| `log.json` | Example log output |
3263

33-
🛠️ Easy local development and extension
64+
---
3465

35-
🗂️ Project Structure
36-
File/Folder Purpose
37-
main.ts Main server entrypoint, route registration
38-
Router.ts Custom Router class for HTTP routes
39-
StaticFile.ts Utility for serving static files (optional)
40-
index.html Landing page served at /
41-
Models/Users.ts User model (referenced in code)
42-
deno.json Deno configuration and task definitions
43-
log.json Example log output
44-
🚀 Getting Started
45-
Prerequisites
46-
Deno v1.0+ installed
66+
## 🚀 Getting Started
4767

48-
Run the Development Server
49-
text
50-
deno task dev
51-
or directly:
68+
### Prerequisites
5269

53-
text
54-
deno run --watch --allow-net --allow-read --allow-write main.ts
55-
Server runs on port 8000.
70+
- [Deno](https://deno.com/) v1.0+ installed
5671

57-
📡 API Endpoints
58-
Method Path Description
59-
GET / Serves the landing page
60-
GET /users Returns all users as JSON
61-
GET /user/:id Returns a single user by ID as JSON
62-
GET /:main/:sub?/:filename Serves static PNG files from folders
63-
🛣️ Routing Logic
64-
All routing is managed by a custom Router class, supporting parameterized paths and method matching.
72+
### Run the Development Server
6573

66-
Static files (PNG images) can be served from nested directories via dynamic routes.
74+
`deno run --watch --allow-net --allow-read --allow-write main.ts`
6775

68-
Example: /images/avatar.png or /images/profile/avatar.png will serve the corresponding PNG file.
76+
`Server runs on **port 8000**.`
6977

70-
⚙️ Configuration
71-
deno.json defines tasks and imports:
78+
---
7279

73-
json
80+
## 📡 API Endpoints
81+
82+
| Method | Path | Description |
83+
|--------|-----------------------------|------------------------------------------|
84+
| GET | `/` | Serves the landing page |
85+
| GET | `/users` | Returns all users as JSON |
86+
| GET | `/user/:id` | Returns a single user by ID as JSON |
87+
| GET | `/:main/:sub?/:filename` | Serves static PNG files from folders |
88+
89+
---
90+
91+
## 🛣️ Routing Logic
92+
93+
- All routing is managed by a custom `Router` class, supporting parameterized paths and method matching.
94+
- Static files (PNG images) can be served from nested directories via dynamic routes.
95+
- Example: `/images/avatar.png` or `/images/profile/avatar.png` will serve the corresponding PNG file.
96+
97+
---
98+
99+
## ⚙️ Configuration
100+
101+
**deno.json** defines tasks and imports:
102+
```json
74103
{
75104
"tasks": {
76-
"dev": "deno run --watch --allow-net --allow-read --allow-write main.ts"
105+
"dev": "deno run --watch --allow-net --allow-read --allow-write main.ts"
77106
},
78107
"imports": {
79-
"@std/assert": "jsr:@std/assert@1"
108+
"@std/assert": "jsr:@std/assert@1"
80109
}
81110
}
82-
View deno.json for details
111+
```
112+
113+
[deno.json][1]
114+
115+
---
116+
117+
## 📂 Source Files
118+
119+
### `main.ts`
120+
```ts
121+
// Import the custom Router class for handling HTTP routes
122+
import {Router} from "./Router.ts";
123+
124+
// Import the User model for interacting with user data
125+
import {User} from "./Models/Users.ts";
126+
127+
// Create an instance of the Router to register routes
128+
const router = new Router()
129+
130+
131+
// Register a GET route for the root path ('/')
132+
// This serves the 'index.html' file as a readable stream
133+
router.get('/', async function () {
134+
const index = Deno.open('index.html');
135+
const readFile = (await index).readable
136+
return new Response(readFile);
137+
})
138+
139+
// Register a GET route for '/users'
140+
// This response with a JSON array of all users
141+
router.get('/users', async function () {
142+
const users = JSON.stringify(await User.all());
143+
return new Response(users, {
144+
headers : {
145+
'content-type' : 'application/json'
146+
}
147+
});
148+
});
149+
150+
// Register a GET route for '/user/:id'
151+
// This responds with a JSON object for a specific user by ID
152+
router.get('/user/:id',async function (req) {
153+
const user = await User.findById(Number(req.params?.id))
154+
return new Response( JSON.stringify(user) , {
155+
headers : {
156+
'content-type' : 'application/json'
157+
}
158+
});
159+
})
160+
161+
// Register a GET route for serving static files from nested directories
162+
// Supports optional subfolder and returns the file as a PNG image
163+
router.get('/:main/:sub?/:filename', async function (req){
164+
const mainFolder = req.params?.main
165+
const subFolder = req.params?.sub;
166+
const filename = req.params?.filename
167+
let readFile;
168+
169+
if (subFolder == null)
170+
readFile = await Deno.readFile(`${mainFolder}/${filename}`);
171+
else
172+
readFile = await Deno.readFile(`${mainFolder}/${subFolder}/${filename}`);
173+
174+
return new Response(readFile, {
175+
headers: {
176+
'content-type': 'image/png',
177+
}
178+
})
179+
})
180+
181+
// Example of how to serve static files using the StaticFile utility (currently commented out)
182+
/*StaticFile.serve('/:main/:sub?/:filename')*/
183+
184+
// The main request handler that delegates all requests to the router
185+
function handler(req : Request): Promise<Response> | Response {
186+
return router.route(req)
187+
}
188+
189+
// Start the Deno HTTP server on port 8000 using the handler function
190+
Deno.serve({port: 8000}, handler)
191+
```
192+
[main.ts][4]
193+
194+
---
195+
196+
### `Router.ts`
197+
198+
```ts
199+
// Import the Routes type, which describes the structure of route definitions
200+
import type {Routes} from "./types/Routes.ts";
201+
202+
// Import the Req type, representing the shape of the request object used by the router
203+
import type { Req } from "./types/Req.ts";
204+
205+
// Define the Router class to handle route registration and request routing
206+
export class Router {
207+
208+
// Private array to store all registered routes
209+
private routes: Routes[] = []
210+
211+
// Register a GET route with a path and its callback handler
212+
get(path: string, callback: (req: Req) => Promise<Response> | Response) {
213+
this.routes.push({method: "GET", path: path, callback: callback});
214+
}
215+
216+
// Main method to match incoming requests to registered routes and execute the appropriate callback
217+
route(req: Req): Response | Promise<Response> {
218+
// Parse the request URL for matching
219+
const requestUrl = new URL(req.url);
220+
221+
// Iterate through all registered routes
222+
for (const route of this.routes) {
223+
// Check if HTTP method matches (e.g., GET)
224+
if(req.method === route.method) {
225+
// Create a URLPattern for the route's path
226+
const url = new URLPattern({pathname: route.path})
227+
228+
// Test if the request URL matches the route pattern
229+
if(url.test(requestUrl)) {
230+
// Extract URL parameters if there are any
231+
const matches = url.exec(requestUrl);
232+
233+
if(matches !== null){
234+
req.params = matches.pathname.groups;
235+
}
236+
237+
// If the route pattern includes a search (query) part, assign search params
238+
if(url.search !== '') {
239+
req.searchParams = requestUrl.searchParams;
240+
}
241+
242+
// Call the registered route handler with the request object
243+
return route.callback(req)
244+
}
245+
}
246+
// If the method does not match, return a 405 Method Not Allowed response
247+
else {
248+
return new Response('Method Not Allowed', {status: 405})
249+
}
250+
}
251+
252+
// If no route matches, return a 404 Not Found response
253+
return new Response('Not found', {status : 404})
254+
}
83255

84-
🖼️ Screenshots
85-
Landing page preview:
256+
}
257+
```
258+
[Router.ts][5]
259+
260+
---
261+
### `log.json` (sample)
262+
```ts
263+
"logs":{
264+
[
265+
{
266+
"pathname": "/",
267+
"timestamp": "2025-06-26T00:04:29.685Z"
268+
},
269+
{
270+
"pathname": "/",
271+
"timestamp": "2025-06-26T00:06:01.874Z"
272+
}
273+
]
274+
}
275+
```
276+
[log.json][3]
86277

87-
![Landing Page](https://raw.githubusercontent.com/denoland/deno_logo2/main 🤝 Contributing
278+
---
88279

89-
Contributions are welcome! Please fork the repo and submit a pull request.
280+
## 🤝 Contributing
281+
282+
Contributions are welcome! Please fork the repo and submit a pull request.
90283
For major changes, open an issue first to discuss what you would like to change.
91284

92-
📄 License
93-
MIT
285+
---
94286

95-
🙏 Credits
96-
Built with Deno.
97-
Landing page:
287+
## 📄 License
98288

99-
Welcome To Deno App2
289+
MIT
100290

101-
<sub>This README is styled for clarity and visual appeal, following [best practices for GitHub READMEs].</sub>
291+
---
102292

103-
Tips for further styling:
293+
## 🙏 Credits
104294

105-
Replace the logo URL and screenshot with your own assets if available.
295+
Built with [Deno](https://deno.com/).
296+
Landing page:
297+
> Welcome To Deno App
106298

107-
Add more badges (e.g., build status, code coverage) as needed.
299+
<sub>_This README is styled for clarity and visual appeal, following best practices for GitHub READMEs._</sub>
108300

109-
For advanced styling, use HTML blocks sparingly for centering or responsive images, as shown above.
301+
<!--
302+
[1]: deno.json
303+
[2]: index.html
304+
[3]: log.json
305+
[4]: main.ts
306+
[5]: Router.ts
307+
[6]: StaticFile.ts
308+
-->
110309

111-
GitHub Flavored Markdown supports only limited inline CSS and HTML, but the above layout ensures compatibility and readability across devices.

0 commit comments

Comments
 (0)