
A micro web server so fast, it'll make you dance! :dancers:
Polka is an extremely minimal, highly performant Express.js alternative. Yes, you're right, Express is already super fast & not that big :thinking: — but Polka shows that there was (somehow) room for improvement!
Essentially, Polka is just a native HTTP server with added support for routing, middleware, and sub-applications. That's it! :tada:
And, of course, in mandatory bullet-point format:
$ npm install --save polka
const polka = require('polka');
function one(req, res, next) {
req.hello = 'world';
next();
}
function two(req, res, next) {
req.foo = '...needs better demo 😔';
next();
}
polka()
.use(one, two)
.get('/users/:id', (req, res) => {
console.log(`~> Hello, ${req.hello}`);
res.end(`User: ${req.params.id}`);
})
.listen(3000, err => {
if (err) throw err;
console.log(`> Running on localhost:3000`);
});
Polka extends Trouter which means it inherits its API, too!
Returns an instance of Polka~!
Type: Server
A custom, instantiated server that the Polka instance should attach its handler to. This is useful if you have initialized a server elsewhere in your application and want Polka to use it instead of creating a new http.Server.
Polka only updates the server when polka.listen is called. At this time, Polka will create a http.Server if a server was not already provided via options.server.
Important: The
serverkey will beundefineduntilpolka.listenis invoked, unless a server was provided.
Type: Function
A catch-all error handler; executed whenever a middleware throws an error. Change this if you don't like default behavior.
Its signature is (err, req, res, next), where err is the String or Error thrown by your middleware.
Caution: Use
next()to bypass certain errors at your own risk!
You must be certain that the exception will be handled elsewhere or can be safely ignored.
Otherwise your response will never terminate!
Type: Function
A handler when no route definitions were matched. Change this if you don't like default behavior, which sends a 404 status & Not found response.
Its signature is (req, res) and requires that you terminate the response.
Attach middleware(s) and/or sub-application(s) to the server. These will execute before your routes' handlers.
Important: If a base pathname is provided, all functions within the same use() block will only execute when the req.path matches the base path.
Type: String
Default: undefined
The base path on which the following middleware(s) or sub-application should be mounted.
Type: Function|Array
You may pass one or more functions at a time. Each function must have the standardized (req, res, next) signature.
You may also pass a sub-application, which must be accompanied by a base pathname.
Please see Middleware and Express' middleware examples for more info.
Returns: Object or undefined
As of v0.5.0, this is an alias of the @polka/url module. For nearly all cases, you'll notice no changes.
But, for whatever reason, you can quickly swap in parseurl again:
const app = polka();
app.parse = require('parseurl');
//=> Done!
Returns: Polka
Boots (or creates) the underlying http.Server for the first time. All arguments are passed to server.listen directly with no changes.
As of v0.5.0, this method no longer returns a Promise. Instead, the current Polka instance is returned directly, allowing for chained operations.
// Could not do this before 0.5.0
const { server, handler } = polka().listen();
// Or this!
const app = polka().listen(PORT, onAppStart);
app.use('users', require('./users'))
.get('/', (req, res) => {
res.end('Pretty cool!');
});
The main Polka IncomingMessage handler. It receives all requests and tries to match the incoming URL against known routes.
If the req.url is not immediately matched, Polka will look for sub-applications or middleware groups matching the req.url's base path. If any are found, they are appended to the loop, running after any global middleware.
Note: Any middleware defined within a sub-application is run after the main app's (aka, global) middleware and before the sub-application's route handler.
At the end of the loop, if a middleware hasn't yet terminated the response (or thrown an error), the route handler will be executed, if found — otherwise a (404) Not found response is returned, configurable via options.onNoMatch.
Type: IncomingMessage
Type: ServerResponse
Type: Object
Optionally provide a parsed URL object. Useful if you've already parsed the incoming path. Otherwise, app.parse (aka parseurl) will run by default.
Routes are used to define how an application responds to varying HTTP methods and endpoints.
If you're coming from Express, there's nothing new here!
However, do check out Comparisons for some pattern changes.
Each route is comprised of a path pattern, a HTTP method, and a handler (aka, what you want to do).
In code, this looks like:
app.METHOD(pattern, handler);
wherein:
app is an instance of polkaMETHOD is any valid HTTP/1.1 method, lowercasedpattern is a routing pattern (string)handler is the function to execute when pattern is matchedAlso, a single pathname (or pattern) may be reused with multiple METHODs.
The following example demonstrates some simple routes.
const app = polka();
app.get('/', (req, res) => {
res.end('Hello world!');
});
app.get('/users', (req, res) => {
res.end('Get all users!');
});
app.post('/users', (req, res) => {
res.end('Create a new User!');
});
app.put('/users/:id', (req, res) => {
res.end(`Update User with ID of ${req.params.id}`);
});
app.delete('/users/:id', (req, res) => {
res.end(`CY@ User ${req.params.id}!`);
});
Unlike the very popular path-to-regexp, Polka uses string comparison to locate route matches. While faster & more memory efficient, this does also prevent complex pattern matching.
However, have no fear! :boom: All the basic and most commonly used patterns are supported. You probably only ever used these patterns in the first place. :wink:
See Comparisons for the list of
RegExp-based patterns that Polka does not support.
The supported pattern types are:
/users)/users/:id)/users/:id/books/:title)/users/:id?/books/:title?)/users/*)Any named parameters included within your route pattern will be automatically added to your incoming req object. All parameters will be found within req.params under the same name they were given.
Important: Your parameter names should be unique, as shared names will overwrite each other!
app.get('/users/:id/books/:title', (req, res) => {
let { id, title } = req.params;
res.end(`User: ${id} && Book: ${title}`);
});
$ curl /users/123/books/Narnia
#=> User: 123 && Book: Narnia
Any valid HTTP/1.1 method is supported! However, only the most common methods are used throughout this documentation for demo purposes.
Note: For a full list of valid METHODs, please see this list.
Request handlers accept the incoming IncomingMessage and the formulating ServerResponse.
Every route definition must contain a valid handler function, or else an error will be thrown at runtime.
Important: You must always terminate a
ServerResponse!
It's a very good practice to always terminate your response (res.end) inside a handler, even if you expect a middleware to do it for you. In the event a response is/was not terminated, the server will hang & eventually exit with a TIMEOUT error.
Note: This is a native
httpbehavior.
If using Node 7.4 or later, you may leverage native async and await syntax! :heart_eyes_cat:
No special preparation is needed — simply add the appropriate keywords.
const app = polka();
const sleep = ms => new Promise(r => setTimeout(r, ms));
async function authenticate(req, res, next) {
let token = req.headers['authorization'];
if (!token) return (res.statusCode=401,res.end('No token!'));
req.user = await Users.find(token); // <== fake
next(); // done, woot!
}
app
.use(authenticate)
.get('/', async (req, res) => {
// log middleware's findings
console.log('~> current user', req.user);
// force sleep, because we can~!
await sleep(500);
// send greeting
res.end(`Hello, ${req.user.name}`);
});
Middleware are functions that run in between (hence "middle") receiving the request & executing your route's handler response.
Coming from Express? Use any middleware you already know & love! :tada:
The middleware signature receives the request (req), the response (res), and a callback (next).
These can apply mutations to the req and res objects, and unlike Express, have access to req.params, req.path, req.search, and req.query!
Most importantly, a middleware must either call next() or terminate the response (res.end). Failure to do this will result in a never-ending response, which will eventually crash the http.Server.
// Log every request
function logger(req, res, next) {
console.log(`~> Received ${req.method} on ${req.url}`);
next(); // move on
}
function authorize(req, res, next) {
// mutate req; available later
req.token = req.headers['authorization'];
req.token ? next() : ((res.statusCode=401) && res.end('No token!'));
}
polka().use(logger, authorize).get('*', (req, res) => {
console.log(`~> user token: ${req.token}`);
res.end('Hello, valid user');
});
$ curl /
# ~> Received GET on /
#=> (401) No token!
$ curl -H "authorization: secret" /foobar
# ~> Received GET on /foobar
# ~> user token: secret
#=> (200) Hello, valid user
In Polka, middleware functions are organized into tiers.
Unlike Express, Polka middleware are tiered into "global", "filtered", and "route-specific" groups.
.use('/', ...fn) or .use(...fn), which are synonymous.Because every request's pathname begins with a /, this tier is always triggered.
pathname that's more specific than '/'. For example, defining .use('/users', ...fn) will run on any /users/**/* request.These functions will execute after "global" middleware but before the route-specific handler.
method action.Once the chain of middleware handler(s) has been composed, Polka will iterate through them sequentially until all functions have run, until a chain member has terminated the response early, or until a chain member has thrown an error.
Contrast this with Express, which does not tier your middleware and instead iterates through your entire application in the sequence that you composed
$ claude mcp add polka \
-- python -m otcore.mcp_server <graph>