Hacker News new | ask | show | jobs
Show HN: REST Alternative to GraphQL and tRPC (openapistack.co)
123 points by anttiviljami 976 days ago
19 comments

GraphQL is well over a decade old. I'm not sure "tried and tested" is a meaningful contrast between the two approaches at this point, especially as it relates to API requests from the frontend. It's okay for people to choose REST just because they like it better, and/or aren't interested in learning something different.
GraphQL's first draft release was 8 years ago. [1]

It's first non-draft release was 5 years ago. [2]

It's first release under community governance was 2 years ago. [3]

[1] https://github.com/graphql/graphql-spec/releases/tag/July201...

[2] https://github.com/graphql/graphql-spec/releases/tag/June201...

[3] https://github.com/graphql/graphql-spec/releases/tag/October...

I think I shipped my first production GraphQL server around this time 8 years ago, but by all accounts it was in use at Facebook a couple of years before it was opened up. So overall ~10 years old sounds right, though the released spec is a little different to the internal version that predates it.
By that standard MyCorpInnerPlatform is very mature.
Agree. GraphQL is well mature by this point.
What I really like with graphql that IMO all the other spec lack is a nice human readable format. When as a team you design a new API no way I am going to write openapi spec, but I can reason in GraphQL schema. Then each side takes the contract and go implement the frontend and the backend and it just works.
There are many, many frameworks for IPC/RPC or even REST that have human-readable schemas.

GraphQL is a performance nightmare, and while it might have nice and flashy dev tooling, it's fundamentally unoptimizable without extensive development investment - something that isn't an issue with REST.

> GraphQL is a performance nightmare, and while it might have nice and flashy dev tooling, it's fundamentally unoptimizable without extensive development investment - something that isn't an issue with REST.

That feels really true. I worked on a really large gql api, and it was a performance nightmare, lots of 10+ second queries that thrashed our database.

Fine grained security was a nightmare. We found no existing solutions, and rolled a few different solutions for this that were all awful and leaky. Eventually we threw a bunch of money at h1 to tease it all out.

Citations very much needed for REST. JsonSchema is not human readable. Some RPC yes but their support on web tech is meh (grpc-web for example is a mess).

Having written large code bases in graphql I really disagree with this statement. It is just a different mindset and I like it. It does make you think way more in terms of data and their relations instead of endpoints. Sure you might get N+1 problems but its easy to mitigate and I prefer doing that on the backend instead of the frontend doing those calls.

If you want to do that, try gRPC and Proto.

But I feel like REST and OpenAPI are fine especially if you start from the backend. I think with .NET Web APIs, the story is particularly good since it has out-of-the-box tooling for OpenAPI spec generation based on your exposed endpoints and models and then the option to customize that as needed.

I've recently been working with it trying to get hot reload working on the frontend client generation and it's a fantastic DX with minimal wiring: https://chrlschn.dev/blog/2023/10/end-to-end-type-safety-wit...

Grpc browser support is pretty bad, I still can't wrap my head around the whole grpc-web thing that looks like giant hacks.

Openapi is not human readable IMO and the point is you design the API waaaay before you write any line of code. The api should express the business needs and be a two way discussion with the frontend people.

OpenAPI itself doesn't need to be human readable because it's readily translated into human readable form using tools like Redocly, RapiDoc, or any other host of tools that create output documents from input OpenAPI spec.
There’s also Twirp, if gRPC doesn’t fit your requirements.
> no way I am going to write openapi spec

I don't get the stubbornness here. OpenAPI is not hard??? It's just JSON schema + some routes and effectively conveys the same information as a GraphQL schema, possibly more if you're not using custom directives and such.

> Then each side takes the contract and go implement the frontend and the backend and it just works

This is not unique to GraphQL. There's almost zero difference between programming to an OpenAPI spec vs a GraphQL schema - they're both API interface definitions.

Auto generate the contract. That might be easier in some languages like TypeScript or Java, but it’s just done as part of the build.
You don’t actually want that.

Code should not inform design.

Design informs code.

Use the code to do the design. Don’t implement the damn thing. Just work in a nicer medium like Typescript. Add attributes. Remove attributes. Move things around in the refactoring tool. Then generate the doc. Forget about the whole “this is what the database does” stuff.
Cool to see more of these kinds of projects, nice work OP!

I'm a huge fan of this general concept, so you're definitely on the right path imo. That said, two things are jumping out at me:

- Users would still be writing OpenAPI specs/JSON Schema by hand, an incredibly annoying and tedious process (unless using a nicer DSL)

- Generation/build steps are annoying (but likely unavoidable here)

As pointed out by many other comments, an unfortunate amount of teams aren't writing OAPI specs. I personally feel this is a major mistake for anyone building an API, but that's a discussion for another day.

I've been using https://www.ts-rest.com, a similar project, for a few months now. Instead of relying on OpenAPI specs as the source, you define the "contracts" using Zod. These contracts are essentially OpenAPI specs, but Zod makes for a MUCH better DSL. I really like that...

- Real-time type checking without generator steps, both server & client-side. XHR clients like `fetch`, `react-query`, etc clients are inferred from the contract.

- The Zod contracts _can_ generate/output OpenAPI specs and Mock Service Worker mocks for testing (as needed, not required)

- (Optional) Automatic request & response validation based on Zod contract definitions, no additional code needed.

- (Node) Backend agnostic (partially, anyway: NextJS, Express, Nest and Fastify supported atm)

- Works very well with `react-hook-form` & `ZodResolver`, so e.g an invalid value on client-side shows the same error as the API would have if request were fired.

- Zod types can be used as typescript (z.infer), a wonderful way to re-use e.g model types in client-side components.

This ts-rest experience has fundamentally solidified a belief in me: One single source of truth, propagating through the system, is _the_ best way to build safely and efficiently.

I am almost ashamed to look back on the many, many projects I've worked on where APIs and client-side did not share truth. Duplication of (hand-rolled) types, error messages, etc is horrific in retrospect.

I don't want to think about the amount of bugs I've come across due to this dual (at best) truth approach.

Thank you for your encouraging words and insights!

There are indeed popular DSLs and code to openapi solutions out there. Many of which are easy to plug in to the openapi-stack libraries btw!

I guess I personally always found it frustrating to try to control the generated OpenAPI output using additional tooling and ended up preferring yaml + a visualisation tool as the api design workflow. (e.g. swagger editor)

But something like https://buildwithfern.com, or using zod as substitute for json schema may indeed be worth a try as a step before emitting openapi.

Good point, and one that counts towards ts-rest atm; If you're bringing your own OpenAPI spec, there is (not yet) an OpenAPI->Zod converter available.

The great thing about OAPI is there's _so much tooling_ available, but it can be daunting and very frustrating to find the "right one". I spent more hours than I'd care to count wading through the ecosystem,

Perhaps it'd be a good idea to promote a few tools via your project? I suspect many potential users would fall off early because they (imo wrongly) believe the upfront cost of writing the OAPI spec is too much to ask. I do understand the reaction if they don't know of good DSLs, though.

IMO, https://openapistack.co/docs/examples/building-apis/#writing... would be a good place to add that.

PS: ts-rest's video on the front page is what immediately convinced me to try it out. Your current interactive example is nice, _but_ it doesn't product type errors for me so the value isn't as immediately obvious (I'm assuming watch doesn't work in the sandbox?).

> Users would still be writing OpenAPI specs/JSON Schema by hand, an incredibly annoying and tedious process (unless using a nicer DSL)

My big foot gun for this is that you can manually write jsonschema that doesn't have a nicely serializable java representation, making it hard to use cross-language, and you don't find out that that's the case until you try

After looking at tRPC, and having done a lot in a few jobs with GraphQL codegen & gRPC codegen, I always wondered why there were so few recent OpenAPI based codegen/client-server-contract tools. I think I was searching for this exact tool.

Love that it makes the client somewhat backend agnostic, but the tools for the OpenAPI backend also look great.

Thank you for this, I hope it is successful in achieving some adoption!

Thanks for the encouraging words! So far it’s already nice to see these libraries being adopted slowly by more and more companies. Not nearly as big as graphql and trpc of course.
I want this, but language agnostic.

Let me explain: standards are great, type safety is great, API contracts are great. Locked into a particular language is not great. In fact it's bad for the author. tRPC and now this assume my team has the desire and time to move to another language. The best frameworks assume none of that.

I'm not asking for implementations for every possible language given a framework. I'm asking for a framework for which any possible language can be built upon because the constructs are simple and easy to follow: graphql does this pretty well despite some of the nuances. Bonus points if the author provides examples out of the box in a variety of languages.

There are libraries out there for a lot of languages that can generate OpenAPI specs for your APIs and generate clients from OpenAPI specs.

This seems like a nice set of tools for doing so in Node/Typescript but the general pattern of using OpenAPI contracts is language agnostic.

The difference being that each language-specific framework has their own way of implementing the specs with the problem that some are done well and some are done half-assed. But then again, this problem exists with many specifications (OpenAPI is not alone). I look at it like this: the spec should inform the framework which should inform the (language specific) implementation. If the language specific implementation is it's own framework then so be it, but counter to the intentions of the layer above it. So why do all this? Enterprises have many teams that have many capabilities. How one (Python) team implements OpenAPI is drastically different than how another (Typescript) team implements OpenAPI, which causes a lot of friction when moving engineers around to different teams. If a spec desires to have mass adoption then providing at least heuristics for implementation will only increase that adoption.
Congrats, this looks really nice! I recently finished a side project using Fastify and the Fastify Swagger plugin (which extends the built-in request and response validation and can dynamically generate an Open API definition) which was a good experience overall but the addition of request mocking and the solid documentation site shown here could tempt me to revisit it.
Cheers! Going from code first to schema first is definitely worth it in my experience! Especially when working in a team.

The nice thing is you already have an openapi spec, so it’s pretty trivial to eject from fastify swagger and switch to openapi-backend if you want!

Here’s an example of openapi-backend running on Fastify

https://github.com/openapistack/openapi-backend/tree/main/ex...

Awesome job! I really liked that you built it in a way that it's framework-agnostic and you also have parameter validation built in!

This might be the best OpenAPI Typescript-based project I've seen so far. The ones I've used often tie you into a specific framework and do not perform any validation on the parameters at all.

Thank you for the kind words!
Congrats on the launch!

How does this compare to other solutions, like https://openapi-ts.pages.dev/?

Good idea to add to the comparisons page!

The openapi-typescript package is a library for generating types from openapi spec, similar to the openapicmd typegen command provided by openapi-stack.

The new openapi-typescript-fetch seems similar to openapi-client-axios, but using fetch instead of axios for a more lightweight approach.

Both libraries are great and well aligned with goals of openapi-stack!

In addition to type generation and consuming APIs, https://openapistack.co provides tools for building and mocking API backends with the openapi-backend library, as well as a general cli tool to work with openapi files and invoke APIs via the command line.

The core mission of https://openapistack.co is to bring together tools for an end to end full stack toolkit for building and consuming typesafe REST APIs.

Is it just me, or do these "API contracts and type safety guarantees" all call back to SOAP?
The core idea of SOAP was good. The ability to generate client and server code stubs from a single declarative file was great. I worked with SOAP extensively in early 2000s.

The problem that killed SOAP, to my mind, was growing complexity that eventually started to outweigh the benefits of the protocol. Not every implementation supported every new complication that, say, MS kept inventing, it hurt interoperability.

I think a modern sort-of-replacement for the SOAP's use case is GRPC. It eschews the heavyweight XML and tries to keep things simple, while staying reasonably typesafe, declarative, discoverable, and extendable.

Speaking from painful experience, gRPC is an absolutely terrible choice for client-server interactions. Google (its creator) discourages its use except for server-to-server, and Improbable labs' grpc-web library is handcuffed to brittle and rapidly-aging web tooling.
GRPC is compact; in some cases it's important.

What would you suggest, beyond the standard "ReST" and GraphQL, with OpenAPI as a declarative description?

For a compact message format being sent to browsers, you might look at messagepack, eg with https://github.com/msgpack/msgpack-javascript – it's essentially binary JSON, so it'd be compatible with OpenAPI specs.

I think the ser/des is slower than JSON in most browsers, but the message format is smaller.

Oftentimes, using a query parameter like `?exclude[]=…` or `?include[]=…` or similar to say "only get me these response fields, not the whole object" can be useful for this too (and then you still get JSON back).

Yes it's an interesting trend. I integrate with a good handful of systems in my current work and the most important ones we integrate with are SOAP based. The concepts behind a WSDL are great in theory, but they end up incorrect or unmaintained in practice (at least with the systems I integrate with). There's something freeing and flexible about a plain REST API that makes developing your own wrapper nice and simple, but if all communication is between internal systems, a contract based approach seems great.
All the way back to Apollo Domain.
Yes! SOAP wsdl, but json
WSDL
There's no winning at this layer and every attempt is as doomed as medieval alchemists trying to transmute lead to gold.

The key here is overall speed, but you can't trade the time spent learning/understanding the API contract for these crazy complicated tools. The developer still has to look at the operations and data models exposed by the API, understand them, amd plan for how to integrate them into the larger application.

The big bonus of the human documentation approaches today is that time is somewhat combined with building the client. The downside of course is that it's a human doing it. My point here is that you can't take the time a human is going to spend understanding the API concepts and then tack on a bunch of time learning, configuring, and running the client gen code and associated infrastructure as well and expext that your solution is going to win.

The more complicated these codegen setups get, the less comicated just reading some docs and spinning up a few lines of client code is. And guess which I'm going to choose as a dev? Whatever is overall fastest and gets me on to the problems I'm really trying to solve.

This project is precisely for devs who dislike codegen and want to just ship products fast.

1. Design your API in yaml 2. Run a mock API 3. Develop the App, iterate mock API 4. Implement backend and ship

No code generation needed. Generate types if you want.

How is designing an API in YAML “fast”? Isn’t using code much, much faster for the average dev?
Speaking personally as a full stack engineer, unless the backend is completely trivial to implement I find it much faster and more efficient to write and iterate JSON schema while implementing frontend features than actually implement backend logic first.
That is why we now step back to SSR-apps. No api necessary, no extra layers, no extra bloat and it is fast and scalable enough for the most apps.
Who's we?

Most apps nowadays have a mobile app, so you need the API anyways...

Strong disagree.

The barrier you presume is that OpenAPI specs are hard to write. Raw oAPI in yaml is indeed a pain, but there are good DSL's out there.

I personally love Zod->OpenAPI, via https://ts-rest.com which uses https://www.npmjs.com/package/@anatine/zod-openapi. https://github.com/asteasolutions/zod-to-openapi is another alternative for Zod.

> The big bonus of the human documentation approaches today is that time is somewhat combined with building the client.

This is wild to me; human documentation is absurdly error-prone and it's almost always and immediately out of date. (Zod or other DSL) -> OpenAPI -> generated docs (and types! and clients! and mocks!) are always going to be better; always accurate, and faster. The upfront cost is slightly higher, but the ROI is _significant_.

OpenAPI specs lend themselves to excellent docs, ala Mintify or Docusaurus. Even interactive ones, like Swagger UI. The vast majority of API browsers & tooling understands OAPI, so why re-create (an often incomplete) version of the truth when using those tools?

> Whatever is overall fastest and gets me on to the problems I'm really trying to solve.

You may start (slightly) faster, but you'll incur significant cost when you move past the "trivial implementation" stage.

For instance:

- Do you do request & response validation on the server? That'll often need duplication on the client (e.g, error messages, and once out of sync, client-side validation mismatches server-side response)

- Typescript on client & server? Then you're already doing the manual work (often more than once) that oAPI->types would get you for free.

- Implementing client-side XHR calls manually, and getting typing right, is a pretty significant undertaking. Multiply that by the number of client-side stacks the API will be consumed by. Or, just generate them via OAPI (or real-time infer via something like ts-rest)

- TS on client, but another BE stack? OpenAPI, when used right, ensures the "contract" is 1:1. When BE changes, client needs changing -- or it breaks. You want this safety.

- Manually mocking API responses is wasteful; write good oAPI specs and auto-generate mocks (e.g, MSW).

- Do you test the real API implementation? OpenAPI specs can help you do that automatically.

At this stage of my career, I would turn down a job offer from a company/team that wasn't willing to use OpenAPI or equivalent single-source-of-truth (*unless I'm in a truly desperate situation)

GraphQL is more future proof than rest.

Rest is tied to HTTP forever - GraphQL can be separated, easily.

Rest communicates inputs and outputs in too many ways for my liking any more: headers, paths, bodies, query params, and dare I say method.

One complaint for GQL is that potentially they had a chance to standardize paging, filtering, etc... instead every schema is customized.

I guess we'll have to create a new standard at some point. We'll never get this shit right.

I don’t really agree GraphQL is somehow more ”future proof” because it doesn’t use as many HTTP features.

It’s not like HTTP is going to go away in a while.

I find that AsyncAPI is a nice extension of OpenAPI/REST ideas if you need to go beyond request / response.

If I'm understanding coding123's comment correctly, I think AsyncAPI would need to be extended to allow specifying a GraphQL schema and not just a JSON schema as the message payload and (I guess implied?) response types
When I see API tools, I am bummed to see the diminished focused on API clients. Particularly client experience (not the libs).

Its all about REST vs GraphQL vs gRPC vs Async API vs native SDKs. It's all about how to make it easier or faster for developers to build these APIs.

Never about, how to make this API best for the API client. That is what matters the most. Focus on clients and can they get the job done. If it does, no need to over engineer!

Did you ever play with Realm.io? It had a ton of promise, and I loved the idea of “OOP API”, where objects write themselves to the database from your app.

My opinion is more nuanced now, but still it’s super cool.

More a comment on the main page.

Show some code or small example on that main page. I have no idea or sense to compare it to graphql without some small toy example.

Thanks for the feedback!

There is a full interactive sandbox example on the front/landing page https://openapistack.co

But point taken, will make sure code examples are visible on the overview page

It’s a really good solution.

Wish it would use undicci/fetch instead of axios though.

How do you make sure that the spec is in sync with the implementation?
The spec is used in runtime for routing and validation.

https://openapistack.co/docs/examples/building-apis/

Thanks. I missed this part https://openapistack.co/docs/openapi-backend/typescript/ so the types are generated for the backend to use. We’ve used API first approach before, but it was very painful to keep the types in sync. Now we’re transitioning to FastAPI which allows to more or less create the OpenAPI types in code (via pydantic which can export the python types into OpenAPI schema).

We now follow a kind of hybrid approach in which the routes and types are created in code first, without actual implementation (just return 404). This auto generates the spec.yaml as well as any vendor code we might need (via client generator). I think this is quite a productive workflow as well because the type generation in code is more convenient than typing yaml by hand and everything is always in sync

The code first approach is surely enticing and miles better than manually keeping the openapi spec up to date.

But I’d always advocate for going schema first. This has huge benefits for collaboration between frontend and backend engineers and results in better designed APIs and generally better software in my experience.

Wrote a full blog post some time ago explaining why the API First approach works: https://dev.to/epilot/why-we-design-apis-first-e85

Maybe I didn’t explain the process correctly, we do generate the spec first, it’s just that the tool we use for creating the spec is the code that will later contain the implementation.
Ah nice! Mind sharing what you’re using to generate the spec?
FastAPI which is a python framework
GraphQL is RESTful. Just treat GraphQL Result as the resource..
> While REST APIs don't generally provide the same level of control to clients as GraphQL, many times this could be seen as a benefit especially in scenarios where strict control over data access and operations is crucial.

REST is more secure, cacheable, and more performant on the server side as field resolution doesn't need to happen like it does with GraphQL. I'm told it is easier to develop against on the client side, and this is a trade-off, but I favor REST applications over GraphQL ones as a DevOps engineer. They are much easier to administer infrastructure-wise. I can cache the requests, I don't need to stand up an Apollo router, and WAF support for GraphQL is still pretty nacent. My coworker found a GraphQL query only about five layers deep that could tip over a service. Please don't put cycles in your graphs.

Data at our company suggests that several small requests actually do better performance-wise than one large one. We switched to GraphQL a year and a half ago or so, but this piece of data seems to suggest that we might have been better off (performance wise on the client side) just sticking with REST. My suggestion to that effect was not met with optimism either on the client or server side. Apparently there are server-side benefits as well, allowing for more modular development or something like that.

I have used OpenAPI using connexion[1]. It was hard to understand at first, but I really liked that the single source of truth was one schema. It also made it really easy to develop against the API because it came with a UI that showed the documentation for all the REST end points and even had test buttons.

1: https://connexion.readthedocs.io/en/latest/

> My coworker found a GraphQL query only about five layers deep that could tip over a service.

I find arguments like this a bit odd. That's a pretty deep query in REST as well.

I don't have extensive experience with GraphQL, but the complaints I see about it often seem like things you just shouldn't be doing anyways - or, if you do, they're going to be rough in REST as well.

I think the point is, graphql apis tend to be so flexible that it’s easy to accidentally ship an API that allows clients to craft excessively heavy nested queries in a single request.

Supporting nested queries isn’t really a common thing in REST, and it’s simpler to rate limit clients by resource than query complexity.

I concur. I usually implement a specific rest resource for use cases which is usually easier to do than implement a GraphQL backend.
> Many organizations choose REST over GraphQL due to more established conventions, simplicity, and the ability to leverage standard HTTP features directly.

I strongly disagree with this. REST APIs are usually a complete mess and follow no conventions at all. Even if OpenAPI is being used, the majority of OAS documents have errors or are entirely broken. I'm Working on tooling to automatically parse OAS and transform it to GraphQL schemas. There's not a single day where we don't find another broken OAS. Compare that to GraphQL where we now have powerful linters for schemas etc... But even without, the average GraphQL API is definitely in better shape than the average REST API, and that's simply because the tooling enforces better boundaries. You might have a correct OAS vs every GraphQL server actually follows the GraphQL spec.

> The average GraphQL API is definitely in better shape than the average REST API

Totally agree.

GraphQL tooling is generally just better. It enforces a Schema first workflow and thus more emphasis on conventions and design.

OpenAPI tends to be an after thought with teams building REST APIs. Swagger started out as a way to add docs and generate SDKs.

That was exactly my motivation for building openapi-stack. GraphQL-like tooling, but for teams using REST.

That's fantastic and I hope to see more of this.
We're pretty pleased with using trpc. With trpc-openapi, you can set up REST-like APIs. I like this method because it uses TypeScript and not YAML.
Love tRPC! For full stack typescript teams it’s the obvious way to go. Super simple and straightforward. OpenAPI or GraphQL are great though if you’re investing into APIs as a product