Show HN: Instant API – Build type-safe web APIs with JavaScript (github.com)
This eliminates the need for most schema validation libraries, automates user input sanitization, and prevents your team from developing carpal tunnel syndrome trying to keep your OpenAPI / Swagger specifications up-to-date. We developed it as a side effect of building our own serverless API platform, where it has scaled to handle over 100M requests from users per day. This is an early release, but Instant API has had about six years of consistent development as proprietary software to get to the point it is at today. We have spent the last couple of months modernizing it.
We have command line tools that make building, testing, and deploying Instant API to Vercel or AWS a breeze. Additionally, there is a sister package, Instant ORM, that provides a Ruby on Rails-like model management, migration and querying suite for Postgres. Everything you need to build an API from scratch.
Finally -- LLMs and chatbots are the the top of the hype cycle right now. We aren't immune, and have developed a number of LLM-integrated tools ourselves. Instant API comes with first-class Server-Sent Event support for building your own assistants and a few other nifty tools that help with AI integrations and chatbot webhook responses, like executing endpoints as background jobs.
This has been a huge labor of love -- it has become our "JavaScript on Rails" suite -- and we hope y'all enjoy it!
87 comments
[ 1.6 ms ] story [ 162 ms ] threadIn serverless environments — where we have used the framework as a gateway with endpoints being executed inside of Lambdas — streaming is accomplished using Redis broadcasting and channels are managed via UUIDs. We may open source this at some point but an enterprising engineer could fork and implement this.
Re; scaling. As mentioned in README we’ve scaled horizontally to 100M requests / day or about 1,157 RPS without issue on older, proprietary versions. Candidly this early release is designed for usability over performance but I wouldn’t be surprised in either direction. :)
But OpenAPI is the “gold standard” machine readable API specification format so it makes sense that OpenAI would rely on it!
I remember when that would have been WSDL, with something like SoapUI - where you'd feed in the service description file from whatever service you want to interact with and would get a functional API client. It also had codegen that let you end up with full client stubs in Java or another language, or are able to generate the WSDL file from your server code as well: https://www.soapui.org/docs/soap-and-wsdl/working-with-wsdls...
It's nice to see OpenAPI/Swagger finally catching up, because to me the codegen aspects felt insufficient there for the longest time - if your server code determines what responses to requests it will return, why on earth would you ever want to write the OpenAPI specs manually? It's the same as with ORMs - if you already have a database schema setup on a server somewhere (with SQL migrations, say with dbmate), all of your local entity mappings should be easily generated in a schema-first approach, you shouldn't have to write a single line of code for that.
The latest service I'm building is in .NET and they actually have that covered really nicely: https://learn.microsoft.com/en-us/aspnet/core/tutorials/gett...
And the Rider IDE there also has nice schema-first tooling, even if a bit niche: https://blog.jetbrains.com/dotnet/2022/01/31/entity-framewor...
Model driven development and codegen feel like they definitely belong for boilerplate heavy uses cases like this, where you can also almost perfectly describe the end result that you need based on what you have!
I've seen a lot of hate on WSDLs throughout the years but they were honestly really productive for getting work done.
I do find myself accidentally saying one and meaning the other, in both directions too!
[1] https://en.wikipedia.org/wiki/OpenAPI_Specification
I think your way is better, but we’re very reliant on express because of our heavy Azure integration and how that’s just easier with express because Microsoft has a lot of tools for it.
While this is a neat project, it's not Type safe, as nothing in the pipeline will inform you about a breaking type error prior to running it and seeing the validation fail. That usually means in production. To catch those errors early before they hit production, you use a type system, and one that exists for JavaScript and works really well, is TypeScript, and I find it mind-boggling that there is no mention of it in this entire document discussing "type safety".
Type safety means that no operation receives "unexpected" inputs, but the definition of "unexpected" is not fixed in stone and can vary depending on the context, which makes it a fuzzy concept. If I only define + on integers, and allow `"a"+"b"`, then that's unexpected and must be prevented somehow to be type safe. If I define + on integers or strings, it is fine.
Here using "type safe" is warranted, because the system prevents you from making HTTP calls with unexpected arguments - even though the validation occurs at runtime and the result may be an exception.
(Edit: well to be strictly fair it's not really type-safe because nothing prevents the HTTP endpoint's expected arguments to change under your feet)
And you can use TypeScript all day but not be protected from missing or wrong data validation. No compiler in the world can help you there.
Edit: Parent added quotation marks and hyphens after my comment. I stand proudly by my cutesy remark.
https://github.com/thestorefront/FastAPI
As an example: `application/json` and `text/html` respresentations of a single resource may render some of the same bits of data, but the actual rendering process is entirely different. Even if the content type is the same, you may render completely different output based on other parameters, like a header used to select between different profiles of said content type, or even just something simple as `accept-language`.
If it's not possible right now, is there any plans to support routing based not just on method and URL, but on headers as well?
For example, let's say I have an api at `/weather` which when requested with the header `accept: application/json` returns something like:
Now if I open that same path in a browser, I get an html page displaying the day's temperature in a nicely formatted way for humans to read.Or another case, I want to version an API, such that I can provide different representations based on the version requested. So I look for `accept-version` and if it's `v1` for example I get the `v1` version. GitHub does this, as a real world example: https://docs.github.com/en/rest/overview/api-versions?apiVer...
In my latest project[1], we set up the types for the API. The server and client are both bound by those types, meaning that one cannot change without the other. This fixes an entire class of bugs where backend code gets updated without the corresponding front-end code changing or vice-versa. It also has the nice side-effect that all of the possible return values (including errors) are nicely documented in one file, and that the errors are very consistent. On the frontend we have a "typed fetch" which returns typed results from the API.
We are also using this for typed access to localStorage, another source of many bugs in past projects, where random keys end up being scattered and causing nondeterministic behavior on different devices.
You can see how our API type system is implemented here:
- common types for both client and server: https://github.com/stoarca/presupplied/blob/master/images/ps...
- client's typed fetch: https://github.com/stoarca/presupplied/blob/master/images/ps...
- server's typed endpoint definitions: https://github.com/stoarca/presupplied/blob/master/images/ps...
[1]: We are working on https://app.presupplied.com, a digital home-schooling curriculum to teach 3-year-olds to read. Planning to expand to math, writing, and programming.
https://news.ycombinator.com/newsguidelines.html
What I can't comprehend is wanting types and building a custom library instead of using the right tool
I also just don't get the resistance to TS that we see so often in public projects and npm libraries, it's not harder or any more complicated:
JS REPL: "node"
TS REPL: "npx ts-node"
Run JS file: "node file.js"
Run TS file: "npx ts-node file.ts"
Just do it.
Small projects maybe fine with JSDocs as a starting point.
I'm glad you asked
Nothing
I use it every day
:)
1: https://www.npmjs.com/package/zod
1: https://github.com/colinhacks/zod
Also do I understand it correctly that you need to use Instant API everywhere in order to get this "type safety" and as such any consumer implementation would be basically limited to JavaScript?
And no — type safety is applied at the HTTP interface. API consumers need no special library to get all the benefits.
So it's basically validation middleware. The HTTP protocol does not define any such thing.
There are various tools for it already, but the JS ecosystem has always been up for yet another framework.
I use a JsonObject to do type narrowing and generate appropriate error messages when I receive invalid data over the wire: https://www.npmjs.com/package/@retrohacker/json-types
In every codebase I’ve added this to, I’ve found invalid parsing logic.
I feel like this type, or something similar, should be bundled in the official TypeScript project. That untested data comes out as “any” is not a good developer experience in my opinion.
And “unknown” is basically broken for type narrowing.
How? Typescript 4.9 improved it for type narrowing significantly. https://devblogs.microsoft.com/typescript/announcing-typescr...
Last weekend I actually hacked an an experiment to codegen narrowing unknown to a specific type and it works pretty well https://github.com/joshhunt/codegen-json-validator-experimen...
https://github.com/microsoft/TypeScript/issues/25720
Working with untrusted types in a GraphQL project lead me to creating that JsonObject module.
I wrote up what I was thinking at the time here: http://www.blankenship.io/essays/2022-12-01/
Maybe this has improved.
You mean you could use a library that helps you with checking the shapes?
Using the json-types module above, you assign your incoming json object to the JsonObject type. Then you type narrow by validating the payload contains the properties you are using.
So you can:
Not sure what particular use case you have in mind but which language has built-in functionality to validate chunks of bytes without some kind of type definition? To me this is an orthogonal problem to language design. Every web framework has to serialize and deserialize data.
What I was getting at is that TypeScript is perfectly suited to define type safe data structures wihtout reinventing the wheel. If you're going to parse type definitions for your validation middleware you might as well use something that can also applied to your business logic.
It converts Zod types to OpenAPI specification.
Json schema is more universal, and it can be used in .NET, Java, etc.
I want to write definitions in the "first party syntax" - which both JSDoc and Typescript types are - and have everything else generated out from that.
Typescript's types simply aren't able to provide input validation, both because they don't exist at runtime and for lack of power ("number must be 0..10").
At least with Zod, you also get Typescript types, from the same schema.
To use Typescript or not doesnt matter, it is about runtime validation. The one like the native JS way with JSDoc, the other likes Typescript and doesnt matter the build step for the API.
Personally I’m a fan of ts-rest: https://ts-rest.com/
Write a specification of your endpoints in TypeScript, and from that one spec you get:
- Server side validation of requests/responses
- A TypeScript API client
- Specialized clients, if you like, for example a react-query client if you like using react-query
- Auto-generated docs (OAS)
- TypeScript types for requests and responses to use in your code
IMO a lot more maintainable than a jsdoc based approach. For example, you can define a type for a Person, and then re-use that type in the responses of all Person-related endpoints, and even in POST/PUT/PATCH bodies (saying things like “the POST body is a person Person, but omit the id field”). With jsdoc you’re repeating that definition a tonne, AND you’re lacking compile time type safety.
It is absolutely possible to question the necessity of a or motivation behind a project, without attacking someone. It's not that OP did ruin a million dollar project. I find your comment quite harsh. Prefacing it with the very first sentence shows that you were quite aware of that. Not a good style, in my opinion.
//Despite the downvotes, I still stand by my opinion. I don't get them, but that's fine.
Yes, it's verbose, but its advantages over Typescript is that it work without any compilation step, while being standard enough to give helpful type-hint on any good enough IDE.
Of course, if your project uses Typescript already, you should use Typescript instead, but if you just want to do a simple web page without any compilation step/package.json shenanigan, it's sometime nice to reach for JSDoc.
EDIT: to answer your question, in JSDoc you can typedef your own custom objects[0], so you thankfully don't have to repeat your types everywhere.
[0] https://devhints.io/jsdoc
How and when are these kinds of types checked? Would this error only be found at runtime?
This error would only be found if you wrote a test for it. The framework doesn't have an AI integration that checks whether or not your types are consistent with real world applications... but could.