Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Loafang! An alternative to GrapQl?
Adwaith Rajesh
Adwaith Rajesh

Posted on

     

Loafang! An alternative to GrapQl?

Full disclaimer, I madeloafang as a way to kill my time and is still in development.
The current implementation that we will be discussing is written in python. And the example shown will be in python 3.9.
So, let's begin.

What does it aim to solve.

Well, technically it's more about readability and being more verbose in terms of how the query is written and also removing the need to use an external library to send a request from the client's perspective. The way loafang achieves this is by making use of JSON syntax as it's one of those file types that is supported by a lot of languages out of the box.

And it's way too much fun to do something like this. So.....

A sample query

{"GET:get-user":{"user --name ad":["username","email"],"posts --user-id ad34":["latest"]}}
Enter fullscreen modeExit fullscreen mode

Let's talk about the parts of the query.


"GET:get-user":{"user --name ad":["username","email"],"posts --user-id ad34":["latest"]}
Enter fullscreen modeExit fullscreen mode

This is an execution block in terms of loafang terminology. You can have multiple execution blocks in the same loafang query (obviously)

GET:get-user
Enter fullscreen modeExit fullscreen mode

This is the block header, It specifies three things,

  • A Request method, in this case, it'sGET (The schema is case sensitive),
  • A unique id that represents the block. (get-user)
  • A block property key. (It's optional)

These three things are arranged in the following order.
METHOD:id:property-key


"user --name ad":["username","email"]
Enter fullscreen modeExit fullscreen mode

This is a query that needs to be executed from the server. And it has three parts.

  • The head, here its user (this can be a database name or something like that).
  • The args (--name)
  • contents, here["username", "email"]

Now, the contents type must be a list forGET andDELETE queries.
And, must be a dict forPOST,PUT, andPATCH, as these three methods require you to update or add things.

Let's write some code.

Let's make a simple example use case for loafang withfastapi andtinydb

We will make a simple example where you canADD,UPDATE, andGET data from the DB.
I won't let you remove your data from MY database.

Stuff we need

pip3 install loafang fastapi uvicorn tinydb
Enter fullscreen modeExit fullscreen mode
# app.pyfrom__future__importannotationsfromtypingimportAny,UnionfromargparseimportNamespace# type hinting purposes onlyfromfastapiimportFastAPI,HTTPExceptionfromloafangimportparse,Methods,MethodsError,QueryBuilderfromtinydbimportTinyDB,Query# what our query looks likequery=QueryBuilder("database")query.add_argument("uname",type=str)classRequestMethods(Methods):# class to handle/create a response for queries in a blockdef__init__(self)->None:# you can have different query parser for different request methodsself.get_query_parser=queryself.post_query_parser=queryself.put_query_parser=querydefget(self,args:Namespace,contents:list[str])->Any:# our database only has fields for name, username and email.ifnotall(iin["name","email"]foriincontents):raiseMethodsError(400,"Bad contents arguments")User=Query()db=TinyDB(args.database)db_ret=db.search(User.username==args.uname)ifnotdb_ret:raiseMethodsError(404,"User does not exists")else:user=db_ret[0]return{i:user[i]foriincontents}defpost(self,args:Namespace,contents:dict[str,Any])->Any:ifnotall(iincontentsforiin["name","email"]):raiseMethodsError(400,"Bad contents arguments")User=Query()db=TinyDB(args.database)ifdb.search(User.username==args.uname):raiseMethodsError(403,"User already exists")else:db.insert({"username":args.uname,**contents})return{"msg":f"user{args.uname} added."}defput(self,args:Namespace,contents:dict[str,Any])->Any:ifnotall(iincontentsforiin["name","email"]):raiseMethodsError(400,"Bad contents arguments")User=Query()db=TinyDB(args.database)user=db.search(User.username==args.uname)ifnotuser:raiseMethodsError(404,"User already exists")db.update(contents,User.username==args.uname)return{"msg":f"User updated successfully.{args.uname}"}request_methods=RequestMethods()# a vague representation of the loafang queryQueryType=Union[dict[str,Any],list[str]]# initialize the serverapp=FastAPI()@app.get("/")defread_root():return{"ping":"pong"}@app.post("/loaf")defloaf(data:QueryType):# the parsers gives you three things# The parsed data with all the output (None, if something goes wrong)# err code (only if something goes wrong, else None)# err message (only if something goes wrong else None)parsed_data,err,msg=parse(request_methods,data)returnparsed_dataifparsed_dataelseHTTPException(status_code=err,detail=msg)
Enter fullscreen modeExit fullscreen mode

Launch the server

uvicorn app:app
Enter fullscreen modeExit fullscreen mode

Now let's send a sample query to theloaf endpoint and see what happens.

The query
{"GET:get-user:pe":{"db.json --uname adwaith":["name","email"]},"POST:add-user":{"db.json --uname adwaith":{"name":"ad","email":"adwaithrajesh3180@gmail.com"}},"PUT:update-user":{"db.json --uname adwaith | update-ad":{"name":"ad2","email":"new-email@example.com"},"after":"get-user"}}
Enter fullscreen modeExit fullscreen mode
The response
{"add-user":{"db.json --uname adwaith":{"msg":"user adwaith added."}},"update-user":{"update-ad":{"msg":"User updated successfully. adwaith"},"after":{"db.json --uname adwaith":{"name":"ad2","email":"new-email@example.com"}}}}
Enter fullscreen modeExit fullscreen mode

Let's analyze what the hell just happened here,

In the first execution block, you can see that there is ape property key (currently that is the only one). It tells the parser to skip the block or "prevents the execution" of the block, the block can now be called from other blocks.

The second block is aPOST block and has one query in it that adds a user with the name adwaith to the DB 'db.json'.

The third is aPUT block that has one query that updates a user with the name adwaith. But there is something special with that block. It has anafter key. It tells the parser that after it executes the current block it needs to execute the block specified in theafter.

Theafter key can only point to a block that haspe as its property key.
A block withpe as its property cannot have anafter key

You might also notice that there is a pipe| in between the query. It specifies that anything after the pipe is an alias to the query. As you can see in the result, the key to a query's values is the query itself. So it might be hard at times to retrieve data using the query as the key.

So, that a brief intro toloafang, feel free to ask questions, use the package and if you have any errors, pls report it or ask me ondiscord.

GitHub:https://github.com/Adwaith-Rajesh/loafang

Full Docs:https://adwaith-rajesh.github.io/loafang/

Have a nice day.

Happy Coding.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

A programmer by hobby. Loves tech and opensource. Python enthusiast.
  • Location
    Kannur Kerala
  • Education
    Sophomore
  • Work
    Freelancer
  • Joined

More fromAdwaith Rajesh

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp