Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
August 26, 2020 10:02 pm GMT

Meet tinyhttp, a 0-legacy, tiny and fast alternative to Express

What

tinyhttp is a modern Express-like web framework written in TypeScript and compiled to native ESM, that uses a bare minimum amount of dependencies trying to avoid legacy hell.

Here is a short list of most important features that tinyhttp has:

  • 2.5x faster than Express
  • Full Express middleware support
  • Async middleware support
  • Native ESM and CommonJS support
  • No legacy dependencies, just the JavaScript itself
  • Types out of the box

tinyhttp only depends on 10 packages, 3 of which are framework's internals.

tinyhttp can be used as a newer alternative to Express for your Node.js backend apps without losing the benefits of Express.

You can visit the website or a repository page to get more info, or just keep reading the article :)

Why

My initial reason to write my own web framework was that I wanted to switch from Express.

There is a list of things I don't like in Express today:

  • No ESM support. Means no named imports support.
  • No TypeScript support. I have to install @types/express
  • A lot of useless polyfills such as array-flatten or safe-buffer of features that are already in JavaScript / Node.js, plus use of deprecated properties
  • Huge and old ES5 codebase. Quite hard to read the source code (subjective / uncommon but still)

So, I started searching for alternatives of Express with the same API. The only good thing I found was Polka. It's really good but what I personally didn't like there was that it doesn't provide req / res extensions out of the box and doesn't have types. So, I decided to write my own web framework.

Get started

Setup

Because tinyhttp supports and targets ESM (but supports CJS as well), let's create a module package to be able to use import / export syntax.

mkdir my-cool-tinyhttp-appcd my-cool-tinyhttp-appecho '{ "type": "module" }' > package.jsontouch server.js

Note that you must have Node 13.2+ installed in order to use ES modules. If you don't want to upgrade Node.js, either use --experimental-modules flag or change import to require

Installation

Next we need to install tinyhttp. tinyhttp itself is split into small packages to debug / maintain things easier (for me and for a user).

All of the bare necessities to get started with tinyhttp is placed in an app module. It contains App, Router, Request and Response interfaces to be able to do routing, middleware connection and req / res extension.

To install it, simply type:

npm i @tinyhttp/app

Because tinyhttp and middlewares for it reuse packages, it's recommended to use pnpm as a package manager, although it's not obligatory.

Hello World

Alright, we have the module with us, now let's send "Hello World" from tinyhttp:

import { App } from '@tinyhttp/app'const app = new App()const PORT = 3000app  .get('/', (_, res) => void res.send('<h1>Hello World</h1>'))  .listen(PORT, () => console.log(`Started on http://localhost:${PORT}!`))

This will start a web app on 3000 port. Let's check if it works by requesting it using an HTTP client (curl or httpie, whatever your prefer)

 http localhost:3000HTTP/1.1 200 OKConnection: keep-aliveContent-Length: 20Date: Wed, 26 Aug 2020 19:56:53 GMTKeep-Alive: timeout=5etag: W/"14-SsoazAISF4H46953FT6rSL7/tvU"<h1>Hello World</h1>

And it works :)

Main Concepts

tinyhttp inherits most of the concepts from Express so if you know Express you already know tinyhttp, but it's still worth going through some sections to know technical nuances in tinyhttp.

Routing

Now let's try to do some basic routing. As in Express, you can handle a request with any method listed in the http.METHODS.

HTTP Methods

So, let's try to handle a POST request:

import { App } from '@tinyhttp/app'import { once } from 'events'const app = new App()const PORT = 3000app.get('/', (req, res) => {    res.send('Sent a GET!')})app.post('/', async (req, res) => {    // Nothing complex here, we just listen to 'data' event and return the data as a promise to a `data` variable    const data = await once(req, 'data').then(d => d.toString())    // And then we send it    res.end(`Sent some data: ${data}`)})app.listen(PORT, () => console.log(`Started on http://localhost:${PORT}!`))

And in the console we try it:

 echo 'tinyhttp is epic' | http POST localhost:3000HTTP/1.1 200 OKConnection: keep-aliveContent-Length: 33Date: Wed, 26 Aug 2020 20:09:15 GMTKeep-Alive: timeout=5Sent some data: tinyhttp is epic

And as you see, it works too :D. It's a very basic example and I wouldn't recommend using such request body handling, better use a body parser like Express body-parser.

Route paths and params

Also, you can use both route paths and route params, just like in Express.

Note that instead of an old path-to-regexp tinyhttp uses a faster and newer regexparam module that does exactly the same (matches routes with regex) but does it more quickly.

Example with route paths:

import { App } from '@tinyhttp/app'const app = new App()const PORT = 3000const handler = (req, res) => void res.send('Hello World')app.get('/route1', handler)app.get('/route2', handler)app.listen(PORT, () => console.log(`Started on http://localhost:${PORT}!`))

Example with route params:

import { App } from '@tinyhttp/app'const app = new App()const PORT = 3000app.get('/user/:id', (req, res) => {    res.send(`Hello ${req.params.id}!`)})app.listen(PORT, () => console.log(`Started on http://localhost:${PORT}!`))

And if we try to request /user/v1rtl, we get this:

http -b localhost:3000/user/v1rtlHello v1rtl!

As I mentioned earlier, tinyhttp uses regexparam inside, so if you run into issues with something not matching when it should, go check out the author's repo.

Middlewares

As in any backend application, we should be able to use middlewares, both external and custom ones. Because tinyhttp's Request and Response interfaces extend built-in IncomingMessage and ServerResponse, everything is compatible with Express middlewares, including typings (at least I haven't run into issues with using express typescript wares).

Custom wares

But before trying Express middleware, let's first create a custom one and try to attach it to an app. We'll write a very simple logger that will send messages to console once a request gets finished:

import { App } from '@tinyhttp/app'const app = new App()const PORT = 3000function logger(req, res, next) {  res.on('finish', () => console.log(`${req.method} ${req.url} ${res.statusCode}`))  next()}app  .use(logger)  .get((req, res) => res.send('Hello World'))app.listen(PORT, () => console.log(`Started on http://localhost:${PORT}!`))

As you see, a middleware uses next function. It's required for tinyhttp to understand that we finished doing things in this function and that we need to switch to the next middleware.

Now let's launch our Node.js application and do some random requests at the same time:

 http -b localhost:3000Hello World http POST localhost:3000 -bNot Found http -b localhost:3000/aaaNot Found

And in the logs we'll see this:

node index.jsStarted on http://localhost:3000!GET / 200POST / 404GET /aaa 404

Our logger works as expected, yay!

By the way, if you're looking for a good, yet simple logger, you can try our own @tinyhttp/logger. Although, any logger for Express (and HTTP apps in general, if they use (req, res, next) for wares) will work.

Express wares

So, now let's try to use some express-specific middleware, for instance express-graphql:

npm i graphql express-graphql
import { App } from '@tinyhttp/app'import graphql from 'graphql'import expressGraphQL from 'express-graphql'const app = new App()const port = parseInt(process.env.PORT) || 3000const schema = graphql.buildSchema(`  type Query {    hello: String  }`)const rootValue = {  hello: () => 'Hello world!',}app.use(  '/graphql',  expressGraphQL.graphqlHTTP({    schema,    graphiql: { headerEditorEnabled: true },    rootValue,  }))app.listen(port, () => console.log(`Listening on http://localhost:${port}`))

And if we try to query hello, we'll get Hello world! as expected:

 curl 'http://localhost:3000/graphql?query=%7B%0A%20%20hello%20%20%0A%7D%0A'{  "data": {    "hello": "Hello world!"  }}

Async wares and error handling

You may noticed previously that we used async for a middleware handler function. tinyhttp supports async handlers, but keep in mind that you better wrap them in try...catch to prevent hanging Unhandled Promise Rejection errors. You can use next function to pass errors to a generic error handler (which is defined in App constructor, see docs for more info)

import { App } from '@tinyhttp/app'import { promises as fs } from 'fs'const app = new App()const PORT = 3000app    .use(async (req, res, next) => {        let file        try {            file = await fs.readFile('express 2020.txt')            res.send(file.toString())        } catch (e) {            console.log(`Failed to open ${e.path} file`)            next(e)        }    })app.listen(PORT, () => console.log(`Started on http://localhost:${PORT}!`))

If we try to open a file with making a request to / (without assuming you created it) we'll get a 500 server error with this message in the console:

http localhost:3000HTTP/1.1 500 Internal Server ErrorConnection: keep-aliveContent-Length: 21Date: Wed, 26 Aug 2020 20:55:35 GMTKeep-Alive: timeout=5Internal Server Error
// node server.jsFailed to open express 2020.txt file

Request / Response extensions

As you remember, Express contains a lot of helper functions in their req a res objects. tinyhttp has them too and is in progress of transferring all of them, but at the moment the most common is already here (for detailed descriptions with examples see docs).

Here's a list of what is present in Request:

  • req.hostname
  • req.query
  • req.route
  • req.params
  • req.protocol
  • req.secure
  • req.xhr
  • req.fresh
  • req.stale
  • req.accepts
  • req.get

and in Response:

  • res.cookie
  • res.clearCookie
  • res.end
  • res.json
  • res.send
  • res.status
  • res.set
  • res.links
  • res.location

I recommend to go through documentation to read more about the extensions you want to use in tinyhttp, but most of them works identically to Express ones.

aaaannd that's all you need to know about tinyhttp!

Conclusion

You can start using it today for backend applications, the repository contains a lot of examples, including MongoDB and GraphQL integrations.

Plans for the future

I'm thinking of keeping working on the framework until 1.0 release. By now, I have to cover all of the codebase in tests (atm only 47% is covered), make more examples and implement all of the middlewares from the list to become independent from Express ecosystem.

Feedback

Let me know what you think about tinyhttp! If you decided to try it, but something didn't work as it should, please consider opening an issue!

Thanks for reading this article :)

and please don't forget to star tinyhttp on github!


Original Link: https://dev.to/talentlessguy/meet-tinyhttp-a-0-legacy-tiny-and-fast-alternative-to-express-4m92

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To