7 ms·
Show HN: Instant API – Build type-safe web APIs with JavaScript
Hey there HN! I just wrapped up an all-day documentation spree for Instant API, a JavaScript framework for easily building web APIs that implements type safety at the HTTP interface. It uses a function-as-a-service approach combined with a slightly modified JSDoc spec to automatically enforce parameter and schema validation before HTTP requests make it through to your code. You just write comments above your endpoint functions and voila, enforced type safety contracts between your API endpoints and your developers. OpenAPI specifications are generated automatically as a byproduct of simply building your endpoints.
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!
- mlmilleratmit 3y agoNeat. I like the fresh take on validation. It’s not necessarily unique, but it may be cleaner / more efficient than previous attempts. The mixing of backend processing and the http layer reminds me of my CouchDB days. @stream seems potentially tricky to implement at scale but very useful. And a few nits here and there (eg maybe return a 202 instead of a 200 on accept), but overall pretty slick. Any numbers on performance and/or scalability in production?
- keithwhor 3y ago@stream is actually straightforward in Node; it’s just part of the http request. Typically Instant API waits until the full request is processed to return headers and body as the endpoint implementation could modify headers; but with streaming headers are sent immediately and then the rest of the response streamed with .stream() injecting text. In 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. :)
- tppiotrowski 3y agoI was looking at ChatGPT plugins today and they use the OpenAPI convention. Is it called OpenAPI because it was created by "Open"AI or it's something completely independent of OpenAI that they chose to adopt?
- keithwhor 3y agoThe naming is completely coincidental and probably a little confusing / unfortunate. OpenAPI is a rebranding of Swagger, a really popular open source API specification. Incidentally the OpenAPI initiative only predates OpenAI by a month so I don’t think Sam, Greg & co could have known better at the time. But OpenAPI is the “gold standard” machine readable API specification format so it makes sense that OpenAI would rely on it!
- KronisLV 3y ago> But OpenAPI is the “gold standard” machine readable API specification format 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/ 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/getting-started-with-swashbuckle?view=aspnetcore-7.0&tabs=visual-studio 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-framework-core-inside-rider-ui-way/#existing-database-scaffolding 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!
- devjab 3y agoI think it’s very cool. We went a similar, yet different route into the realm of code-generation rather than building our own api framework. Basically we have something which can generate, types, SQLscripts, data and type conversion and security and an express API service/controller based on a mermaid table input. 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.
- keithwhor 3y agoWhen you say “we” who are you referring to? Would love to learn more about what you’ve built. I haven’t built easy deployment rails for Azure but if you’re interested in playing with our solution I’d be down for a call! email keith@instant.dev
- stagas 3y agoType Safety != Runtime Type Validation. Something being Type Safe means that I, the programmer, am Safe from making type mistakes while developing and maintaining the software. For that to be possible, you need a type system that is embedded in the language and the editor and can continously analyze all of your code types to be correct. Type safe means that any breaking change to any interface will immediately error and point me to all the locations I need to visit, and therefore I am safe from making any type errors. 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".
- keithwhor 3y agoMy claim is only type-safety at the HTTP layer. This claim is intentionally specific! As per another comment in this thread, pedantry is the most valuable currency on HN, so this sort of feedback is not only expected but welcome.
- stagas 3y agoIt's not pedantry, you're using the term wrong. I understand why it can be confusing if you are just using JavaScript and you haven't had the experience of type-safe codebases. Type-safe does not refer to runtime anything, it refers to the developer being able to refactor their interfaces safely, meaning that the type system informs you of any errors across the entire codebase and all of the dependants of that software. Type safety really means "developing with type safety". Type validation on the other hand, can mean runtime type validation, which is what this software is doing, runtime type validation at the HTTP layer, which is a cool project I admit, very useful stuff, but the term type-safe here is misleading.
- keithwhor 3y agoFair enough; if I were to change the qualifiers associated with the project, what’s cool about what exists here and how would you describe it in a way that’s maximally appealing? Is “type-safe” an acceptable generalization given an audience less sophisticated than you or can I use a different framing to the same effect?
- firechickenbird 3y agoPlease don’t call this mess “type-safe”
- whinvik 3y agoBoth the name and intent seem highly inspired from FastAPI!
- keithwhor 3y agoInterestingly I published a Ruby framework called FastAPI before Python’s FastAPI existed ;) — been thinking about this stuff for a long time. https://github.com/thestorefront/FastAPI https://github.com/thestorefront/FastAPI
- deleted 3y ago[deleted]
- mstade 3y agoNeat! Does this framework have any tools for content negotiation? I get that you could do this in the handlers themselves, but in my experience different representations are often so different in implementation that aside from maybe a tiny bit of shared code to produce some underlying dataset, the actual rendering of that data shares very little. 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?
- keithwhor 3y agoI’m not quite sure I grok the use case, can you give a more precise example? Either way, if you’re interested in the approach I *very welcome* issues, PRs and contributors. I think it would be pretty straightforward to create tests for whatever you’re looking for.
- mstade 3y agoHere's a simple use case: human friendly and machine friendly representations at the same path. For example, let's say I have an api at `/weather` which when requested with the header `accept: application/json` returns something like: { "temperature": 25, "unit": "c" } 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?apiVersion=2022-11-28 https://docs.github.com/en/rest/overview/api-versions?apiVer...
- keithwhor 3y agoEntirely possible! I mean right now we restrict endpoint definitions to HTTP methods but in theory you could define unique endpoints for any combination of parameters. What I would want to find is the most effective / used combinations as opposed to unlimited flexibility.
- tsergiu 3y agoWe tried for years to do things like this, and in the end an approach using typescript is way, way better than anything else I've seen attempted. 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/psapp/common/apitypes.ts https://github.com/stoarca/presupplied/blob/master/images/ps... - client's typed fetch: https://github.com/stoarca/presupplied/blob/master/images/psapp/client/src/typedFetch.ts https://github.com/stoarca/presupplied/blob/master/images/ps... - server's typed endpoint definitions: https://github.com/stoarca/presupplied/blob/master/images/psapp/server/src/index.ts https://github.com/stoarca/presupplied/blob/master/images/ps... [1]: We are working on https://app.presupplied.com https://app.presupplied.com, a digital home-schooling curriculum to teach 3-year-olds to read. Planning to expand to math, writing, and programming.
- keithwhor 3y agoOur approach doesn’t preclude TypeScript! In fact I think it would integrate well with it. If you’re interested in a TS approach could you email me at keith@instant.dev ?
- deleted 3y ago[deleted]
- flashback2199 3y ago[flagged]
- keithwhor 3y agoIf you’d like to help build a TS integration, welcome! What’s the MRR of your company? Did you found it or are you an employee? Any specific tips on how TS projects can significantly improve either revenue acquisition or retention over JS projects?
- deleted 3y ago[deleted]
- fintechie 3y agoThis comment encapsulates perfectly what's wrong about the ts talibans... Astonishing to see how their minds can't comprehend that businesses can and do thrive with js.
- dang 3y agoPlease don't respond to a bad comment by breaking the site guidelines yourself. That only makes things worse. https://news.ycombinator.com/newsguidelines.html https://news.ycombinator.com/newsguidelines.html
- flashback2199 3y agoI can definitely comprehend it, I have met some TS talibans and I'm not one of them 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.
- PDSCodes 3y agoWhat stops you from using TS? It starts with JS and JSDoc as a starting point and then you can add TS. Small projects maybe fine with JSDocs as a starting point.
- sandGorgon 3y agowould this be "pydantic-for-javascript" ?
- keithwhor 3y agoGood call! Do you think Python developers would feel more comfortable venturing into JavaScript with this framing?
- Loeffelmann 3y agoZod[1] is probably the closest 1: https://www.npmjs.com/package/zod https://www.npmjs.com/package/zod 1: https://github.com/colinhacks/zod https://github.com/colinhacks/zod
- TheFragenTaken 3y agoHappy to see more projects embrace OpenAPI.
- keithwhor 3y ago<3
- codemusings 3y agoSeems like you put a lot of work into this and as such I'm hesitant to criticize. But what in the world made you think this is superior to using an actual type-safe language like TypeScript? /** * Streams results for our lovable assistant * @param {string} query The question for our assistant * @stream {object} chunk * @stream {string} chunk.id * @stream {string} chunk.object * @stream {integer} chunk.created * @stream {string} chunk.model * @stream {object[]} chunk.choices * @stream {integer} chunk.choices[].index * @stream {object} chunk.choices[].delta * @stream {?string} chunk.choices[].delta.role * @stream {?string} chunk.choices[].delta.content * @returns {object} message * @returns {string} message.content */ Is a developer supposed to type that out for every single endpoint that uses the chunk type? 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?
- keithwhor 3y agoNo not necessarily! I just haven’t written type import support yet; though you’re welcome to help. And no — type safety is applied at the HTTP interface. API consumers need no special library to get all the benefits.
- codemusings 3y ago> And no — type safety is applied at the HTTP interface. So it's basically validation middleware. The HTTP protocol does not define any such thing.
- jfengel 3y agoNo, and neither does Typescript. One of the biggest weaknesses in Typescript is that it can't validate data across the wire. Which is a major use case for the language. There are various tools for it already, but the JS ecosystem has always been up for yet another framework.
- 3y ago
- mkl 3y agoI spotted a bug in the README's first example: @param {number{-90,90}} coords.lng Longitude The range for longitude is incorrect, and only covers half the globe. How and when are these kinds of types checked? Would this error only be found at runtime?
- keithwhor 3y agoThanks for the tip! Fixed this; just missed this when writing the example. 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.
- ebalit 3y agoI know the answer is a bit tongue in cheek, but that would be very valuable and cool, just in case someone from Cursor or Refact is reading this thread ^^
- ac360 3y agoNice work, Keith.