HTTP gateways are incredibly interesting. For instance, you can easily create a Hyperlambda HTTP gateway that adds business logic to Supabase or Hasura, to intercept the original endpoint, and inject business logic before or after the invocation to your originally wrapped GraphQL or PostgREST endpoint. This has use cases such as for instance.
- Add business logic to GraphQL endpoints, such as those exposed by Hasura
- Add business logic to PostgREST endpoints, such as those exposed by Supabase
- Add HTTP caching to optimise your original HTTP endpoint
- Apply authorisation to endpoints originally not having authorisation
- Log error invocations
- Etc, etc, etc
With for instance Hasura, the preferred method of implementing business logic is one of the following according to their FAQ.
- Event Triggers
- REST APIs
- GraphQL APIs
- Stored procedures or functions in your database
Ignoring event triggers and stored procedures, plus functions, due to that these methods arguably are the very definition of bad architecture resulting in the “big ball of mud” – This leaves us with GraphQL APIs and REST APIs. Since the purpose of Hasura is to simplify things for you, the GraphQL API and REST APIs gives you little or nothing. However, there exists a 5th method that is not documented at Hasura’s website; HTTP gateways!
This allows you to intercept the original HTTP endpoint, forward the payload to “whatever”, and inject your own business logic either before or after invoking the Hasura GraphQL endpoint, or Supabase PostgREST endpoint. Some use cases for this might be for instance.
- Validate data before allowing the GraphQL endpoint to be invoked
- Send emails as some GraphQL endpoint or PostgREST endpoint is invoked
- Apply logging if the GraphQL or PostgREST endpoint invocation fails
- Apply either server side cache, or HTTP cache for your endpoints, to optimise execution of slow queries
- Etc, etc, etc
The by far easiest way to achieve such a thing, is to create your HTTP gateway using Hyperlambda. In the following video I am demonstrating the idea. Notice, you can find the code I am creating in the video at the bottom of this article.
This allows you to easily add business logic to Supabase or Hasura, through interceptors, resulting in that you can now easily “inject” business logic, both before and after invoking your original endpoint. No need for Supabase’s “edge functions”, learn TypeScript, or NodeJS. No need to add stored procedures to your database to put business logic into it. No need for slow HTTP REST endpoints invoked by your Hasura GraphQL implementation as “extension methods”, etc, etc, etc. And, this allows you to validate data before passing it into either Supabase as PostgREST arguments, or Hasura as a GraphQL payload.
As an additional bonus, this allows you to “transform” the weird (at best) HTTP POST requirements Hasura forces you to apply due to GraphQL typically forces you to implement your read invocations as POST invocations. So you can turn your GET GraphQL endpoints into
GET endpoints, resulting in better semantics for your GraphQL endpoints, better obeying by the HTTP standard.
In addition, if you create a strongly typed Hyperlambda endpoint, you’re also able to generate meta data on your GraphQL endpoints, creating CRUD endpoints, that you can all of a sudden generate an Angular frontend for using Aista. To understand what I’m talking about, realise the following is easily achievable for you using Aista for both your Hasura and your PostgREST servers, due to Hyperlambda’s ability to use meta data to generate Angular frontends.
I will go into depths of the last point in later articles, but realise that as long as you can correctly “type declare” your Hyperlambda endpoints, Aista can automatically generate a CRUD frontend for you. You can try one such frontend here. So all of a sudden, you can now generate frontends 100% automatically for your Supabase and Hasura projects.
Of course, at this point the natural question becomes as follows; If you’re using Hyperlambda and Aista as an HTTP gateway, and you’re creating strongly typed HTTP APIs, wrapping your existing Hasura and Supabase projects, and you’re applying all your business logic in Hyperlambda – Why not switch entirely to the automatically generated CRUD API that Magic generates? To understand the latter, please read up more about how Aista automatically generates CRUD APIs for you, wrapping your existing or a new database.
However, sometimes when you’re stuck in “legacy land”, extreme measures needs to be applied to slowly change over time. And for a period, the above Hyperlambda HTTP Gateway solution might just be exactly what you need, to slowly transfer from legacy GraphQL code or legacy PostgREST code, to a modern and scalable stack such as Hyperlambda.
Below you can find the code I use in the above YouTube video.
.arguments name:string auth.ticket.verify:root strings.concat .:"Hello " get-value:x:@.arguments/*/name unwrap:x:+/* return result:x:@strings.concat
.arguments name:string strings.concat .:"Holla there " get-value:x:@.arguments/*/name unwrap:x:+/* return result:x:@strings.concat
.arguments:* // Add all arguments into [http.post] invocation! add:x:./*/http.post/*/payload get-nodes:x:@.arguments/* validators.string:x:@.arguments/*/name min:5 max:10 // Invoking original endpoint we are now wrapping. http.post:"https://tiger-polterguy.gb.aista.com/magic/modules/faq/gateway-original-2" convert:true payload if and mte:x:@http.post .:int:200 lt:x:@http.post .:int:300 .lambda // Example of how to send an email using Hyperlambda. .mail.smtp.send message to Thomas Hansen:email@example.com from Testing Gmail:firstname.lastname@example.org subject:This is subject entity:text/plain content:This is body else lambda2hyper:x:@http.post log.error:x:- // Returning result of original endpoint invocation to client. add:x:+ get-nodes:x:@http.post/*/content/* return
.arguments:* // Validating length of name argument validators.string:x:@.arguments/*/name min:5 max:10 request.headers.get:Authorization // Add all arguments into [http.post] invocation! add:x:./*/http.post/*/payload get-nodes:x:@.arguments/* // Invoking original endpoint we are now wrapping. http.post:"https://tiger-polterguy.gb.aista.com/magic/modules/faq/gateway-original-1" convert:true headers Authorization:x:@request.headers.get Content-Type:application/json payload // Sanity checking the result of our invocation. if and mte:x:@http.post .:int:200 lt:x:@http.post .:int:300 .lambda // Returning result of original endpoint invocation to client. add:x:+ get-nodes:x:@http.post/*/content/* return else // Logging an error! lambda2hyper:x:@http.post log.error:x:- throw:Something went wrong as you invoked your HTTP endpoint status:500 public:true
Thank you for reading, I hope it was helpful to you :)