Package: caterpillar-api
CaterpillarA light-weight, fast and scalable api solution for Django. Uses a comfy set of functionality that isn't boilerplatey or opinionated.Don't disregard Caterpillar because it's cute, it's scrapy to; with built in type conversion, session management, dead simple file handling, self documenting reports and error management. Caterpillar makes writing APIs fun.Install Caterpillarpip3installcaterpillar-apiDefine your APIInside your django project, create a function with a@Cocoonwrapper that defines your paramaters.fromcaterpillar_apiimportCocoon,monarch@Cocoon(param_req=(('a',int),('b',int),))defadd(request,a,b):return{"c":a+b}Add your endpoint to DjangoInside url.py.urlpatterns=[path('add/',views.add),...]Test out your APINow using curl, you can post data to the API and get a JSON response.curl-d"a=17&b=35"-XPOSThttp://127.0.0.1:8000/add/;echo{"c":52,"successful":true}Why Caterpillar?Minimum boiler-plateReadable syntaxCrazy fastEasy to useUnopnionatedClient librariesCaterpillar doesn't leave you hanging. We have an ever growing list of client libraries to speed up your development efforts. Although you don't need a custom client side library, it sure does feel good using one.React fetch_jsQt C++/QML interfaceRecipesRequired POST argumentspost_req takes a tuple of tuples. The tuple entries define (variable name, python type). You can trust the parameter will be the expected type. If a parameter is missing from a post_req, Caterpillar will respond with an error before the endpoint is called.@Cocoon(post_req=(('name',str),('age',int),))defadd_user(request,name,age):usr=User.objects.create(name=name,age=age)return{'id':usr.id}Optional POST argumentspost_opt uses the same format as post_req. Fields can exist or not. If a field isn't present, the default paramater value or None is used.@Cocoon(post_req=(('uid',str),),post_opt=(('name',str),('age',int),('type',str,'ADMIN'),))defmodify_user(request,uid,name,age,type):usr=User.objects.get(uid=uid)ifnameisnotNone:usr.name=nameifageisnotNone:usr.age=ageusr.type=typeusr.save()returnmonarch.succ(request,{})Optional POST using kwargspost_opt argument checking using if statements does work, but it's a little ugly. It also creates a lot of code. The same operation can be done with kwargs.@Cocoon(post_req=(('uid',str),),post_opt=(('name',str),('age',int),('type',str,'ADMIN'),))defmodify_user(request,uid,**kwargs):usr=User.objects.get(uid=uid)forkeyin('name','age','type'):ifkwargs[key]isnotNone:usr.__setattr__(key,kwargs[key])usr.save()returnmonarch.succ(request,{})Meta data@Coccon can provide meta data as an argument. This is handy because it can further reduce duplicate logic. Parameters are defined in one place only. Take the above example, now using meta data.@Cocoon(post_req=(('uid',str),),post_opt=(('name',str),('age',int),('type',str),),meta="meta")defmodify_user(request,uid,meta,**kwargs):usr=User.objects.get(uid=uid)forkey,_inmeta.post_opt:ifkwargs[key]isnotNone:usr.__setattr__(key,kwargs[key])usr.save()returnmonarch.succ(request,{})Authenticate user and check login statusCaterpillar also helps you work with session information. Session data can be used to authenticate a user and confirm their login state.fromdjango.formsimportmodel_to_dict@Cocoon(param_req=(('uid',str),('password',str),))deflogin(request,uid,password):if(usr:=getByUid(User,uid))isNone:return"Couldn't find user"ifusr.password!=password:return"Invalid password"# Store the user's info into the session, this effectively logs the user inrequest.session['usr']=model_to_dict(usr)return{}@Cocoon(sess_req=[('usr',dict)])deflogout(request,usr):delrequest.session['usr']returnmonarch.succ(request,{})# sess_req['usr'] will only allow this endpoint to be called by users that are logged in@Cocoon(sess_req=[('usr',dict)])defuser_details(request,usr):usr=getByUid(User,usr['uid'])returnmonarch.succ(request,model_to_dict(usr))If optional session checking is required, you can always use sess_opt@Cocoon(sess_opt=[('usr',dict)])defis_online(request,usr):returnmonarch.succ(request,{'online':usrisnotNone})GET data from the URLThe same way POST can have required and optional arguments, GET variables can be accessed with @Cocoon.@Cocoon(get_req=[('video_code',str)])defget_video(request,usr,video_code):#http://server.com/?video_code=XXXprint(video_code)# Prints XXXreturnmonarch.succ(request,{'video_info':'info'})Read a fileCaterpillar seamlessly handles file uploads as parameters. Files, like GET/POST, can be required or optional. The contents of the uploaded file can be read by calling .data(). If a hash of the data is required .hash() will return a sha256 hash.@Cocoon(sess_req=[('usr',dict)],file_req=["logo"])defupload_logo(request,usr,logo):ifnots3.put_data(logo.data(),usr['uid']):returnmonarch.fail(request,"Couldn't upload to S3")returnmonarch.succ(request,{})Need to call an endpoint directly?Not a problem; Caterpillar will happily get out of your way. Passing None for the request parameter will tell Caterpillar you are using the functions directly. Instead of a HttpResponse, you'll get the dict of the response.@Cocoon(post_req=(('a',int),('b',int),))defadd(request,a,b):returnmonarch.succ(request,{"c":a+b})add(None,4,3)# Returns { "c": 12 }add(None,"cat","fish")# Returns { "c": "catfish" } # Type checking isn't done when calling directlyWhere are PUT and DELETE?They didn't make the cut. PUT and DELETE don't add any new functionality over update and remove endpoints. PUT/DELETE aren't fundimental to Django and since Caterpillar aims to be as lightweight as possible, this feature is out of scope.@CocoonCocoon is a function decorator that defines endpoint arguments and data types.sess_opt- Optional session parameters. Array/Tuple of tuples. ('name', type) or ('name', type, default)sess_req- Required session parameters. Array/Tuple of tuples. ('name', type).param_opt- Optional POST/GET parameters. Array/Tuple of tuples. ('name', type) or ('name', type, default).param_req- Required POST/GET parameters. Array/Tuple of tuples. ('name', type).post_opt- Optional POST parameters. Array/Tuple of tuples. ('name', type) or ('name', type, default).post_req- Required POST parameters. Array/Tuple of tuples. ('name', type).get_opt- Optional GET parameters. Array/Tuple of tuples. ('name', type) or ('name', type, default).get_req- Required GET parameters. Array/Tuple of tuples. ('name', type).file_opt- Optional file parameters. Returns the ApiFile object with helper functions data() and hash() Array/Tuple of names. ('name').file_req- Required file parameters. Returns the ApiFile object with helper functions data() and hash() Array/Tuple of names. ('name').files- 'name' which recieves an array of Apifile classes. Handy for uploading arrays of unnamed filess.meta- 'name' which recieves a CaterpillarMeta object. All of the Cocoon parameters are represented along with an 'args' dictionary which holds all data passed to this endpoint.@Cocoon Typesstrintfloatbool - Can take true/false strings or true/false values or ints, 0 !0dict - A dictionary of key value pairs. JSONlist - An array of parameters. Can take a string of delenated elements.CaterpillarMeta classname- The name of the variable.args- dict of all posted data.param_opt- Contents of @Cocoon value for this param.param_req- Contents of @Cocoon value for this param.sess_opt- Contents of @Cocoon value for this param.sess_req- Contents of @Cocoon value for this param.post_opt- Contents of @Cocoon value for this param.post_req- Contents of @Cocoon value for this param.get_opt- Contents of @Cocoon value for this param.get_req- Contents of @Cocoon value for this param.file_opt- Contents of @Cocoon value for this param.file_req- Contents of @Cocoon value for this param.files- Contents of @Cocoon value for this param.monarch functionsMonarch functions provide handy success failed responses. If the standard response format isn't flexible enough, you can create your own using util.raw()fromcaterpillar_apiimportCocoon,monarchmonarch.succA successful response. 'successful' = true is added to the response and then an HttpResponse is generated.request - The request variable passed by Django.response - A dict key/value pair.return{'key':'value'}# Return successreturnmonarch.succ(request,{'key':'value'})monarch.failA fail response. 'successful' = false is added to the response and then an HttpResponse is generated.request - The request variable passed by Django.reason - A string "reason" why the error occurred.code="" - An optional error code.extra={} - A dict of any other information that should be passed.return"Invalid access"# Return error messagereturnmonarch.fail(request,"Invalid access",code="ERR_CODE_A")returnmonarch.fail(request,"Invalid access",extra={'info':'data'})util.rawutil.raw provides HttpResponse logic monarch.succ and monarch.fail use to communicate with Django. This function should only be used in exceptional cases.objs - String of response.status - Status codecontent - content_type of responsecallback=None - Optional JSON-P supportreturnutil.raw('{"key": 8}',200,content='application/json')JSON-P support?Yes. Caterpillar provides JSON-P support out of the box by passing a GET variable callback=xxx. If the callback is passed as POST or cannot use the name 'callback' to pass the callback name, a custom resp/err function set should be created.# http://xxx.com/endpoint?callback=ham_eggs# Yields a response with JSON-Pham_eggs({"key":917})Common errorsNo one likes bugs in their code, but Caterpillars are bugs and sometimes it encounters other bugs.TypeError: xxx() got an unexpected keyword argument 'xxx'Caterpillar works by injecting named variables directly into functions. If the variable name doesn't exist in the paramaters of the function, you'll see a TypeError.@Cocoon(post_req=(('a',int),('b',int),))defadd(request,a):# TypeError Missing variable 'b'returnmonarch.succ(request,{"c":a})There are two possible solutions. Add all variables or add **kwargs at the end of your parameters.# Solution of adding all variables@Cocoon(post_req=(('a',int),('b',int),))defadd(request,a,b):returnmonarch.succ(request,{"c":a+b})# Solution adding **kwargs@Cocoon(post_req=(('a',int),('b',int),))defadd(request,a,**kwargs):returnmonarch.succ(request,{"c":a+kwargs['b']}){"successful": false, "reason": "Missing required argument(s): GET[] POST['b'] SESS[] FILE[]", "code": ""}If required parameters are missing, the endpoint will not be called. A message similar to the above will be sent instead. Caterpillar attempts to provide detailed information for any GET / POST / SESS / FILE data that is required and missing.TestingCaterpillar has limited testing support. Please see the contribution section to get involved.ContributeIf you love Caterpillar as much as we do, there are lots of ways to get involved.Self documenting reportsCaterpiller could use reflections to generated API reports. If you're interested in giving Caterpillar it's documentation butterfly wings, please contact us.Extended type checking for dict argumentsWhen passing a 'dict' argument type to Cocoon, there needs to be the ability to enforce an expectation of structure.Client libraries for all the major librariesLove Angular? Flutter? Vue? Bootstrap? Android? IOS? Caterpillar needs native client libraries for them all.Website or graphic design?We'd love to have a real website. If you want to help give Caterpillar a personality, please contact us.Maybe it's something really cool we don't even know aboutDo you have ideas for new awesome features? Please contact lukedupin about becoming a contributer.