Writing a type safe Node.JS JSON API without any framework

Gabriel Vaquer
4 min readApr 4, 2021

There are dozens of fully featured frameworks for developing JSON APIs, like Express, Hapi, Fastify, Koa, etc. But some times, just for fun, we like to write our own stuff from scratch to see how things work. Let’s take a look at how we can do just that.

First thing’s first, let’s decide our goals and how our library should look like.

  • It should be fast
  • It should handle data validation
  • It should be type safe (TypeScript)

Here’s how I’d like the API to look like:

Pseudo code of the way our library will look like

Awesome. Let’s start by taking care of the core server functionality.

Core server API

Here, we export two methods: bind and unbind. bind() starts the server or “binds the server to a given port” and unbind() simply closes or ends the server.

Next, we should have a way to add routes to our application. Since we’d like to have dynamic routes like express does (“/products/:id), we’ll use a library called path-to-regexp to help us implement this feature. Since we are at it, we should define some interfaces to help us better describe how our library works.

Nice, we now have a way to add routes to our app. But, what’s the deal with the handler function? I’d like the handler function to be asynchronous and accept some kind of context where I can have access to incoming data. Also, I don’t particularly like reading and writing to streams like req and res. I want something like “a context comes in, a reply comes out” type of thing.

Cool, we have a handler that accepts a context and returns a response. But, how are we taking care of validating incoming data, like body or params? Well, what if instead of the having body and params as properties, we turn them into functions that accepts a schema and returns the validated data. This way, those properties become “lazy evaluated” because aren’t executed until they’re called inside the handler. In the case of body, the request doesn’t get parsed until it is called and in the case of params, the query string isn’t parsed either. But what validation library can we use? There are plenty. Yup, Joi, JSON schema, io-ts, etc. In my case, I’ll go with zod. Zod is very typescript friendly and it is heavily inspired by io-ts (I think) but without the functional approach.

Fantastic, we can now validate incoming data. This is great because typescript will infer the types from the schema and you absolutely know that if you get to that point of execution, the data is what you asked for and if not, a 400 “Bad Request” status will be returned by the request handler that we’ll write next.

We can now proceed to implement the entry point of our library, the request handler.

Not too much, right? There are still two other functions we need to implement here though: reply and createContext. Reply is just a helper function that takes the response object, a status code, a body and headers and sends the proper response. Let’s tackle reply first.

The reply function basically checks the type of response and set some headers accordingly. Theoretically, you should be able to send text, JSON, a buffer or stream something like a file. That leaves us with the createContext function.

You may have noticed a new dependency: co-body. As you may guested it, we used it to parse the request body. We could definitely do this our selves, bu the library is so tiny that it ain’t worth it. You may also have notices that we do not handle any errors around co-body. This is because co-body throws an error with a 400 status field which is handled by our request handler. Both body and params functions are very similar: Check the cache, parse data, validate data, cache data and return data.

That’s it. You have built your own little framework for toying around or maybe even small personal projects. The code for this is available on GitHub because it is a real project I built several days ago for a personal project and I wanted to write about it. Finally, here’s how you would use it:

Cheers 🍻

--

--

Gabriel Vaquer
0 Followers

I'm a React and Node.JS developer working as a freelancer