- Overview
- Features
- Project Structure
- Getting Started
- API Endpoints
- Routing Logic
- Configuration
- Source Files
- Contributing
- License
- Credits
This project is a minimalist Deno web server featuring:
- A custom router for clean HTTP GET route handling
- Static file serving from nested directories
- Basic user data API endpoints
- An HTML landing page
It is designed for clarity, modularity, and easy extension, following a single-file, clean interface philosophy[8][11].
- π¦ Custom lightweight router for HTTP GET routes
- ποΈ Static file serving (including images) from nested directories
- π€ User API endpoints (
/users
,/user/:id
) - π HTML landing page (
index.html
) - π οΈ Easy local development and extension
File/Folder | Purpose |
---|---|
main.ts |
Main server entrypoint, registers all routes |
Router.ts |
Custom Router class for HTTP routes |
StaticFile.ts |
Utility for serving static files (optional) |
index.html |
Landing page served at / |
deno.json |
Deno configuration and task definitions |
log.json |
Example log output |
- Deno v1.0+ installed
deno run --watch --allow-net --allow-read --allow-write main.ts
Server runs on **port 8000**.
Method | Path | Description |
---|---|---|
GET | / |
Serves the landing page |
GET | /users |
Returns all users as JSON |
GET | /user/:id |
Returns a single user by ID as JSON |
GET | /:main/:sub?/:filename |
Serves static PNG files from folders |
- All routing is managed by a custom
Router
class, supporting parameterized paths and method matching. - Static files (PNG images) can be served from nested directories via dynamic routes.
- Example:
/images/avatar.png
or/images/profile/avatar.png
will serve the corresponding PNG file.
deno.json defines tasks and imports:
{
"tasks": {
"dev": "deno run --watch --allow-net --allow-read --allow-write main.ts"
},
"imports": {
"@std/assert": "jsr:@std/assert@1"
}
}
[deno.json][1]
// Import the custom Router class for handling HTTP routes
import {Router} from "./Router.ts";
// Import the User model for interacting with user data
import {User} from "./Models/Users.ts";
// Create an instance of the Router to register routes
const router = new Router()
// Register a GET route for the root path ('/')
// This serves the 'index.html' file as a readable stream
router.get('/', async function () {
const index = Deno.open('index.html');
const readFile = (await index).readable
return new Response(readFile);
})
// Register a GET route for '/users'
// This response with a JSON array of all users
router.get('/users', async function () {
const users = JSON.stringify(await User.all());
return new Response(users, {
headers : {
'content-type' : 'application/json'
}
});
});
// Register a GET route for '/user/:id'
// This responds with a JSON object for a specific user by ID
router.get('/user/:id',async function (req) {
const user = await User.findById(Number(req.params?.id))
return new Response( JSON.stringify(user) , {
headers : {
'content-type' : 'application/json'
}
});
})
// Register a GET route for serving static files from nested directories
// Supports optional subfolder and returns the file as a PNG image
router.get('/:main/:sub?/:filename', async function (req){
const mainFolder = req.params?.main
const subFolder = req.params?.sub;
const filename = req.params?.filename
let readFile;
if (subFolder == null)
readFile = await Deno.readFile(`${mainFolder}/${filename}`);
else
readFile = await Deno.readFile(`${mainFolder}/${subFolder}/${filename}`);
return new Response(readFile, {
headers: {
'content-type': 'image/png',
}
})
})
// Example of how to serve static files using the StaticFile utility (currently commented out)
/*StaticFile.serve('/:main/:sub?/:filename')*/
// The main request handler that delegates all requests to the router
function handler(req : Request): Promise<Response> | Response {
return router.route(req)
}
// Start the Deno HTTP server on port 8000 using the handler function
Deno.serve({port: 8000}, handler)
[main.ts][4]
// Import the Routes type, which describes the structure of route definitions
import type {Routes} from "./types/Routes.ts";
// Import the Req type, representing the shape of the request object used by the router
import type { Req } from "./types/Req.ts";
// Define the Router class to handle route registration and request routing
export class Router {
// Private array to store all registered routes
private routes: Routes[] = []
// Register a GET route with a path and its callback handler
get(path: string, callback: (req: Req) => Promise<Response> | Response) {
this.routes.push({method: "GET", path: path, callback: callback});
}
// Main method to match incoming requests to registered routes and execute the appropriate callback
route(req: Req): Response | Promise<Response> {
// Parse the request URL for matching
const requestUrl = new URL(req.url);
// Iterate through all registered routes
for (const route of this.routes) {
// Check if HTTP method matches (e.g., GET)
if(req.method === route.method) {
// Create a URLPattern for the route's path
const url = new URLPattern({pathname: route.path})
// Test if the request URL matches the route pattern
if(url.test(requestUrl)) {
// Extract URL parameters if there are any
const matches = url.exec(requestUrl);
if(matches !== null){
req.params = matches.pathname.groups;
}
// If the route pattern includes a search (query) part, assign search params
if(url.search !== '') {
req.searchParams = requestUrl.searchParams;
}
// Call the registered route handler with the request object
return route.callback(req)
}
}
// If the method does not match, return a 405 Method Not Allowed response
else {
return new Response('Method Not Allowed', {status: 405})
}
}
// If no route matches, return a 404 Not Found response
return new Response('Not found', {status : 404})
}
}
[Router.ts][5]
"logs":{
[
{
"pathname": "/",
"timestamp": "2025-06-26T00:04:29.685Z"
},
{
"pathname": "/",
"timestamp": "2025-06-26T00:06:01.874Z"
}
]
}
[log.json][3]
Contributions are welcome! Please fork the repo and submit a pull request.
For major changes, open an issue first to discuss what you would like to change.
MIT
Built with Deno.
Landing page:
Welcome To Deno App
This README is styled for clarity and visual appeal, following best practices for GitHub READMEs.