A strongly typed, runtime-agnostic web framework for building structured HTTP services in JavaScript, built on Web Standards.
Starting from an empty directory:
| Runtime | Initialize | Add Drash |
|---|---|---|
| Node | npm init -y |
npm install @drashland/drash |
| Deno | deno init |
deno add jsr:@drashland/drash |
| Bun | bun init -y |
bun add @drashland/drash |
| Cloudflare Workers | npm init -y |
npm install @drashland/drash |
Cloudflare Workers also needs Wrangler,
Cloudflare's CLI, to run a Worker locally: npm install --save-dev wrangler.
npm init -y produces a CommonJS project, which is what the Node example below
expects. For ESM, add npm pkg set type=module and import rather than
require.
On Deno both steps are optional: the example below imports the full
jsr: specifier, which resolves without a deno.json. Run deno add if you
want the version pinned there and would rather import the bare
@drashland/drash/... specifier.
Drash is published to npm and
JSR. The JSR specifiers carry no file
extension, since JSR resolves through the package's export map; the npm ones
need .js.
Note: The following example uses Node and CommonJS. View the Other Runtimes section below to find an example that fits your project.
Initialize your project and install Drash.
npm init -y && npm install @drashland/drashWrite your Drash application.
// Node's `URLPattern` support varies by version, so use the polyfill entry point
const {
Application,
Resource,
} = require("@drashland/drash/modules/http.polyfill");
const { createServer } = require("node:http");
class Home extends Resource {
paths = ["/"];
GET(context) {
context.response.end("Oh so easy");
}
}
const app = Application
.builder()
.resources(Home)
.build();
const hostname = "localhost";
const port = 1447;
const server = createServer((request, response) => {
// Node's `node:http` gives you `IncomingMessage` and `ServerResponse`,
// not a Web `Request`. Drash does not convert them for you — you hand the
// app a `context` object carrying whatever your resources need. The
// app only requires `url` and `method`.
const context = {
url: `http://${hostname}:${port}${request.url}`,
method: request.method,
request,
response,
};
return app
.handle(context)
.catch(() => {
response.statusCode = 500;
response.statusMessage = "Internal Server Error";
response.end("Sorry, but we hit an error!");
});
});
server.listen(port, hostname);Run your app and head to open http://localhost:1447.
node app.js
The same application code runs everywhere; only the server around it changes. See the examples below to find code that fits your project's requirements.
Node (CJS)
// Node's `URLPattern` support varies by version, so use the polyfill entry point
const {
Application,
Resource,
} = require("@drashland/drash/modules/http.polyfill");
const { createServer } = require("node:http");
class Home extends Resource {
paths = ["/"];
GET(context) {
context.response.end("Oh so easy");
}
}
const app = Application
.builder()
.resources(Home)
.build();
const hostname = "localhost";
const port = 1447;
const server = createServer((request, response) => {
// Node's `node:http` gives you `IncomingMessage` and `ServerResponse`,
// not a Web `Request`. Drash does not convert them for you — you hand the
// app a `context` object carrying whatever your resources need. The
// app only requires `url` and `method`.
const context = {
url: `http://${hostname}:${port}${request.url}`,
method: request.method,
request,
response,
};
return app
.handle(context)
.catch(() => {
response.statusCode = 500;
response.statusMessage = "Internal Server Error";
response.end("Sorry, but we hit an error!");
});
});
server.listen(port, hostname);Node (ESM)
// Requires `npm pkg set type=module`. The ESM specifier keeps its `.js`
// extension; the CommonJS one above drops it.
import {
Application,
Resource,
} from "@drashland/drash/modules/http.polyfill.js";
import { createServer } from "node:http";
class Home extends Resource {
paths = ["/"];
GET(context) {
context.response.end("Oh so easy");
}
}
const app = Application
.builder()
.resources(Home)
.build();
const hostname = "localhost";
const port = 1447;
const server = createServer((request, response) => {
// Node's `node:http` gives you `IncomingMessage` and `ServerResponse`,
// not a Web `Request`. Drash does not convert them for you — you hand the
// app a `context` object carrying whatever your resources need. The
// app only requires `url` and `method`.
const context = {
url: `http://${hostname}:${port}${request.url}`,
method: request.method,
request,
response,
};
return app
.handle(context)
.catch(() => {
response.statusCode = 500;
response.statusMessage = "Internal Server Error";
response.end("Sorry, but we hit an error!");
});
});
server.listen(port, hostname);Deno (TS)
import {
Application,
Resource,
} from "jsr:@drashland/drash/modules/http.native";
class Home extends Resource {
paths = ["/"];
GET(request: Request) {
return new Response("Oh so easy");
}
}
const app = Application
.builder()
.resources(Home)
.build();
Deno.serve({
hostname: "localhost",
port: 1447,
handler: (request: Request): Promise<Response> => {
return app
.handle<Response>(request)
.catch(() => {
return new Response("Sorry, but we hit an error!", {
status: 500,
statusText: "Internal Server Error",
});
});
},
});Bun (TS)
import {
Application,
Resource,
} from "@drashland/drash/modules/http.polyfill.js";
class Home extends Resource {
paths = ["/"];
GET(request: Request) {
return new Response("Oh so easy");
}
}
const app = Application
.builder()
.resources(Home)
.build();
Bun.serve({
hostname: "localhost",
port: 1447,
fetch(request: Request): Promise<Response> {
return app
.handle<Response>(request)
.catch(() => {
return new Response("Sorry, but we hit an error!", {
status: 500,
statusText: "Internal Server Error",
});
});
},
});Cloudflare Workers (JS)
import {
Application,
Resource
} from "@drashland/drash/modules/http.native.js";
class Home extends Resource {
paths = ["/"];
GET(request) {
return new Response("Oh so easy");
}
}
const app = Application
.builder()
.resources(Home)
.build();
const handleRequest = (request, _bindings) => {
return app
.handle(request)
.catch(() => {
return new Response("Sorry, but we hit an error!", {
status: 500,
statusText: "Internal Server Error",
});
});
};
export default { fetch: handleRequest };app.handle() returns a promise that rejects on error, and Drash writes
nothing to the socket itself — so the .catch() block in each example
above is the only thing standing between an error and the client.
Those blocks are deliberately minimal: they answer every failure with 500,
including a 404 for an unknown path. Real applications should inspect the
error. Drash throws
HTTPError, which
carries the status it means:
app
.handle(request)
.catch((error) => {
if (error.name === "HTTPError") {
return new Response(error.message, {
status: error.status_code,
statusText: error.status_code_description,
});
}
return new Response("Internal Server Error", { status: 500 });
});See Error Handling for the per-runtime versions.