✨ A peek at the upcoming version


#1

Types and validation were problematic for a few reasons:

  • Overriding base types such as int and overriding __new__ was a bit hacky, and meant that it wasn’t very obvious how we should properly handle nullable types.
  • Users had to either use the lowercase variant for inline declarations, or Uppercase variant for class declarations, which is confusing.
  • Overriding using class declarations is error prone - no way to get an error message if you use min_len = 3 on the class declaration instead of min_length = 3, unlike in instantiations where keyword arguments are checked.
  • Native types such as Date, that can be serialized as strings, have been missing.

Here’s what we have now…

from apistar import types, validators

class Product(types.Type):
    name = validators.String(max_length=100)
    rating = validators.Integer(minimum=0, maximum=10, allow_null=True, default=None)
    expiry = validators.DateTime()

# Different ways to instantiate the Type...
product = Product(instance)
product = Product({...}}
product = Product(name=..., rating=..., expiry=...)

# Rating can be omitted as it has a default, other fields are required. 
Product(name=..., expiry=...)

# Nice reprs...
print(product)
# prints: <Product(name='...', rating=..., expiry=...)>

# Access native types or string representations...
product.expiry  # Returns a datetime
product['expiry']  # Returns a string
dict(product)  # Everything serialized into basic primitive types

Components were problematic too…

  • We had an unnecessary explosion of different components, you had to pull lots of different types into a handler in order to do quite basic stuff.
  • Components were parameterized by global settings, which was easy to get wrong, difficult to check against, and awkward to unittest against.
  • The dependency injection resolution was quite complicated, and was overused.

The basic building blocks are no longer treated as components, so…

# Instantiation of some basic functionality
templates = Templates(template_dir='templates', template_apps=['apistar'])

# Pull `App` into a handler to call into the basics with a configured app.
app.render_template()
app.reverse_url()

Creating components now looks like…

from apistar.server import Component

class MyComponent(Component):
    def resolve(self, ...dependencies...) -> WhateverProvidedType:
        ...

app = App(components=[MyComponent()])

Something that’s nice about that is that components can be parameterized on instantiation. No more global settings thank you. We’ll end up with this sort of thing instead…

components = [
    SQLAlchemyComponent(database_url=...)
]

I’d also strongly recommend taking a look through the 0.4 branch to see just how much nicer everything looks on the inside. (Ignore the README for now as it’s not yet up to date, but take a look through apistar/server/*.py and apistar/*.py)

Things to be aware of if looking through the codebase at this point in time are: I still need to do some work around Routes vs Documents, context managers in components so we can eg. have managed database sessions, adding command line handlers back in, and adding the async variant back in.

Also worth noting that there will be no more dependency on coreapi or coreschema - the client libraries and API schema primitives instead just become part of apistar itself.

This one’s a big overhaul, but given the pre-1.0 status I’m not going to make any apologies for that - there was a lot that needed overhauling. I’m really pleased with how everything’s looking from here! :smile:


#2

:100:% agree, thanks for all you do, looking forward to my libs to newer version, I still have a production service using apistar v0.2 :scream:


#3

Very nice! Thank you!


#4

This looks awesome!

Can’t wait to see the updated README! :slight_smile:


#5

Great that you managed to find some time again Tom as it’s a great project so far.

You mentioned pushing out support for ORMs, is the intention to just provide the interface and then let others develop eg a django orm plugin or sql alchemy? Will you push out what you’ve already had here in a seperate repo?

Not sure what you meant by the todo ‘Third party async, flask, django, variants’ is this plugging in channels? Hmm probably not as I can’t see how that would work.

You mentioned previously that you hoped to be able to tie in the third party ecosystem for django, flask etc… and be able to build on that, is that still the intention or any idea how that might work?