REST란, "웹에 존재하는 모든 자원(이미지, 동영상, DB 자원)에 고유한 URI를 부여해 활용"하는 것으로, 자원을 정의하고 자원에 대한 주소를 지정하는 방법론을 의미.
따라서 Restful API는 REST 특징을 지키면서 API를 제공하는 것을 의미한다. (일종의 Coding Convention)
REST: Architectural Style When accessing the resources
by Roy Fielding
If every Web API was built using the same common guiding principles, then it would be so easy for everybody to work together and be able to use different APIs quickly, easily, and efficiently.
REpresentational State Transfer
URI shows which resource provided
HTTP request
->
Client(Browser) <- Server
response
Request using http language
HTTPS: Secured
FTP: File Transfer Protocol
REST API: url format
URI: resource + structure
SOAP: previous norm
GraphQL: Next Generation
URL은 Uniform Resource Locator로 인터넷 상 자원의 위치를 의미합니다. 자원의 위치라는 것은 결국 어떤 파일의 위치를 의미합니다. 반면에 URI는 Uniform Resource Identifier로 인터넷 상의 자원을 식별하기 위한 문자열의 구성으로, URI는 URL을 포함하게 됩니다. 그러므로 URI가 보다 포괄적인 범위라고 할 수 있습니다.
URI is a resource, written in a noun form.
GET /members/show/1 (x)
GET /members/1 (o)
GET: read
POST: create new info
PUT: update all
PATCH: update including index
DELETE: delete including index
API reference : Document. A technical writer might help the developer to build it. Developers said most time consuming part of using API is reading docs.
params: ?
adding: &
Developer Console
Testing Tools:
Insomenia REST Client
POSTman
Remote Work Friendly Cafe API
GET is allowed by default on all routes. No need to specify as like this @app.route("/random", methods=["GET"])
because our server is now acting as an API, we want to return a JSON containing the necessary data. Just like real public APIs.
Turn SQLAlchemy Object into a JSON.
Flask has a built-in helper: jsonify()
- Our server is now acting as an API, we want to return a JSON containing the necessary data.
#1.
return jsonify(
username=user.username,
email=user.email,
#2.
return jsonify(
cafe={
"name": random_cafe.name,
"map_url": random_cafe.map_url,
})
You could also structure the response by omitting some properties like id.
You could also group the Boolean properties into a subsection called amenities.
#3.
return jsonify(cafe={
#Omit the id from the response
# "id": random_cafe.id,
"name": random_cafe.name,
"map_url": random_cafe.map_url,
"img_url": random_cafe.img_url,
"location": random_cafe.location,
#Put some properties in a sub-category
"amenities": {
"seats": random_cafe.seats,
"has_toilet": random_cafe.has_toilet,
"has_wifi": random_cafe.has_wifi,
"has_sockets": random_cafe.has_sockets,
"can_take_calls": random_cafe.can_take_calls,
"coffee_price": random_cafe.coffee_price,
}
})
So another method of serialising our database row Object to JSON is by first converting it to a dictionary and then using jsonify() to convert the dictionary (which is very similar in structure to JSON) to a JSON.
#4.
class Cafe(db.Model):
id = db.Column(db.Integer, primary_key=True)
~~
def to_dict(self):
# Method 1.
dictionary = {}
# Loop through each column in the data record
for column in self.__table__.columns:
#Create a new dictionary entry;
# where the key is the name of the column
# and the value is the value of the column
dictionary[column.name] = getattr(self, column.name)
return dictionary
# Method 2. Altenatively use Dictionary Comprehension to do the same thing.
return {column.name: getattr(self, column.name) for column in self.__table__.columns}
@app.route("/all")
def get_all_cafe():
cafes = db.session.query(Cafe).all()
return jsonify(cafes=[cafe.to_dict() for cafe in cafes])
Parameters are passed in the URL with a ?
query_location = request.args.get("loc")
@app.route("/search")
def get_cafe_at_location():
query_location = request.args.get("loc")
cafe = db.session.query(Cafe).filter_by(location=query_location).first()
if cafe:
return jsonify(cafe=cafe.to_dict())
else:
return jsonify(error={"Not Found": "Sorry, we don't have a cafe at that location."})
POST: Updating entire entry to replace the previous one.
@app.route("/add", methods=["POST"])
def post_new_cafe():
new_cafe = Cafe(
name=request.form.get("name"),
map_url=request.form.get("map_url"),
img_url=request.form.get("img_url"),
location=request.form.get("loc"),
has_sockets=bool(request.form.get("sockets")),
has_toilet=bool(request.form.get("toilet")),
has_wifi=bool(request.form.get("wifi")),
can_take_calls=bool(request.form.get("calls")),
seats=request.form.get("seats"),
coffee_price=request.form.get("coffee_price"),
)
db.session.add(new_cafe) #it will be staged for creation
db.session.commit() #saves the change
return jsonify(response={"success": "Successfully added the new cafe."})
PATCH: only Sending piece of data that needs to be updated.
## HTTP PUT/PATCH - Update Record
@app.route("/update-price/<int:cafe_id>", methods=["PATCH"])
def update_coffee_price(cafe_id):
coffee_price = request.args.get("new_price") #/update-price?new_price=1.2 -> get hold of url value into coffee_price value
cafe_to_update = db.session.query(Cafe).get(cafe_id) #pick the cafe by id
if cafe_to_update:
cafe_to_update.coffee_price = coffee_price
db.session.commit()
return jsonify(response={"success": "Successfully updated a new price."})
else:
return jsonify(error={
"Not Found": "Sorry a cafe with that id was not found in the database."
})
@app.route("/report-closed/<int:cafe_id>", methods=["DELETE"])
def delete_cafe(cafe_id):
api_key = request.args.get("api-key")
if api_key == "TopSecretAPIKey":
cafe = db.session.query(Cafe).get(cafe_id)
if cafe:
db.session.delete(cafe)
db.session.commit()
return jsonify(response={"success": "Successfully deleted the cafe from the database."}), 200
else:
return jsonify(error={"Not Found": "Sorry a cafe with that id was not found in the database."}), 404
else:
return jsonify(error={"Forbidden": "Sorry, that's not allowed. Make sure you have the correct api_key."}), 403