Spider is a minimal framework for building web APIs with Python. Based off the Express.js 4.x API, Spider provides numerous of the quality-of-life features and abstractions that make building your app faster and easier.
- Include route parameters with
:paramname
and access then throughreq.params["paramname"]
- Automatic body parsing (supports json and urlencoded)
- Automatic query parsing
- Easily send responses with
res.send
andres.json
methods (both automatically set content length and latter sets content-type) - Chain response methods, i.e.
res.status(200).send("hello")
from Spider import Router, Request, Response
def root_get(req: Request, res: Response):
res.status(200).send(f'hello, {req.client_address}')
router = Router()
router.get("/", root_get)
router.listen(3000)
router = Router()
def root_get(req: Request, res: Response):
# Same as before...
router.get("/", root_get)
def root_put(req: Request, res: Response):
res.status(201).json({
"username": "blah",
"password": 123
})
router.put("/", root_put)
# And so on...
def root_get(req: Request, res: Response):
# ...
router.get("/", root_get)
def root_put(req: Request, res: Response):
# ...
router.put("/", root_get)
def docs_get(req: Request, res: Response):
# ...
router.get("/docs", docs_get)
def docs_upload_by_id(req: Request, res: Response):
# ...
router.post("/docs/:id", docs_upload_by_id)
Valid characters for a route are
- word characters: [a-zA-Z0-9_]
- backslashes, '\'
- the characters, ?, +, *, and ()
- the character, ':' to denote parameters
If a route contains characters not this set, it will not be compiled to regex and will instead be interpreted literally, meaning an exact match will be needed.
A route name is processed using the following steps
- Prepend the character '^' to the beginning of the string and '$' to the end
- Compile the string as a regex using
re.compile()
- Check if the current route matches the compiled pattern
Routes are consulted in the order they're listed, meaning the route "*"
will
process every request your server receives if it's listed first. If you list
the route "*"
after all other routes, it will match every route that doesn't
match one of those routes.
The Request
object represents the HTTP request and provides useful instance
variables and methods for parsing and retrieving request information.
A dict
containing key-value pairs of data submitted in the request body.
Is None
by default, but can be configured using
Router.use([parse method])
.
def cart_add(req: Request, res: Response):
req.body["itemName"] # E.g. "carrot"
req.body["details"]["code"] # and so forth...
Contains server's hostname.
Returns client's ip address.
A string representing the request method/command: GET, POST, PUT, etc.
A dict
containing key-value pairs of data sent in place of parameters in the
specified path.
# "/books/:category/:popularityRank"
def get_book_by_popularity(req: Request, res: Response):
category = req.params["category"] # E.g. 'nonfiction'
rank = req.params["popularityRank"] # E.g. 47
router.get("/books/:category/:popularityRank", get_book_by_popularity)
The .
characters is interpreted literally, which makes it easy to put params
next to each other.
# "/dict.:category.:sub_category"
def get_object_properties(req: Request, res: Response):
category = req.params["category"]
sub = req.params["sub_category"]
res.json(dictionary[category][sub])
Contains a string representation of the path part of the request url. For
example, a request sent to "/course?id=a123123&date=2012"
will have
req.path
equal to "/course"
.
Contains the request protocol string: either "http"
or "https"
.
Note: this property currently a constant with value "http"
.
A dict
containing the result of parsing the request query via
urllib.parse.parse_qs
.
def convert_query_to_json(req: Request, res: Response):
res.json(req.query)
# E.g., if the request url is "/course?id=a123123&date=2012",
# req.query is {"id": "123123", "date": "2012"}
A boolean containing the value of req.protocol == 'https'
.
Note: this property currently a constant with value False
.
A Boolean property that is true if the request’s X-Requested-With header field is “XMLHttpRequest”.
Retrieves the value of the specified http header. Same as
req.headers.get(field)
.
The Request
class also copies the following instance variables from
BaseHTTPRequestHandler
for troubleshooting: client_address
, server
, requestline
, command
,
path
, headers
, server_version
, sys_version
, error_content_type
, and
protocol_version
.
The Response
object represents an in-progress HTTP response and provides
methods and properties for creating a sending a response.
Ends the response process. Used to quickly send a response without any data.
def get_profile(req: Request, res: Response):
# ...
if not authorized:
res.status(404).end()
Returns the HTTP response header specified by field. The match is case-insensitive.
Sends a JSON response. This method sends a response (with the correct
content-type) that is the parameter converted to a JSON string using
json.dumps()
.
The parameter can be any JSON type, including dict, list, str, bool, int, float, or None.
# dict
res.json({
"filter": "height",
"value": 60,
"unit": "cm"
})
# --> '{"filter": "height", "value": 60, "unit": "cm"}'
# list
res.json(["milk", "cereal", "oranges", "apples"])
# --> '["milk", "cereal", "oranges", "apples"]'
# str
res.json("Hello World!")
# --> '"Hello World!"'
# bool
res.json(False)
# --> 'false'
# int
res.json(10000)
# --> '1000'
# float
res.json(3.14)
# --> '3.14'
# None
res.json(None)
# --> 'null'
Redirects to the URL derived from the specified path, with specified status, a positive integer that corresponds to an HTTP status code . If not specified, status defaults to “302 “Found”.
Sends the HTTP response.
The body parameter can be a bytes object, a string, an dict, bool, or a list.
This method automatically sets the Content-Type and Content-Length HTTP headers, unless previously set.
res.send parameter type |
Content-Type header |
---|---|
list, dict | application/json |
bytes | application/octet-stream |
str | text/html |
bool | text/plain |
Sets the response HTTP status code to statusCode and sends the registered status message as the text response body. If an unknown status code is specified, the response body will just be the code number.
Sets the response’s HTTP header field
to value
. To set multiple fields at
once, pass a dict as the parameter.
res.set("Content-Type", "application/json")
res.set({
"Content-Type", "application/json",
"Content-Length", 10
})
Sets the HTTP status for the response. Is chainable!
res.status(404).send("These aren't the droids you're looking for")
The Router
object's provides functionality for your application to listen for
HTTP requests and match request paths to appropriate callback functions.
This method is just like the router.METHOD() methods, except that it matches all HTTP methods (verbs).
Provides routing functionality. METHOD corresponds to one of the HTTP commands:
GET
, POST
, PUT
, HEAD
, DELETE
, CONNECT
, OPTIONS
, TRACE
, and
PATH
. The actual function names are the lowercase version of these commands.
def say_hi(req: Request, res: Response):
req.send("Hiiii")
router.get("/hello", say_hi)
This function will be used for middleware in future version, so don't overwrite it.
Specify how the Router
should parse incoming request the incoming request
body, globally.
parse_method
can be 'json'
or 'urlencoded'
.
Note: In the future, this will be replaced with middleware to allow parsing different api endpoints with different/custom parsers.
router.parse('json')
router.listen(3000) # Incoming request bodies parsed with json.load