> In 24 hours?

No, not in 24 hours. In about 2 hours. The laundry list of things - api validation, error handling, db migrations, good naming - it's pretty routine. A person writing production apis will have no trouble integrating all of it in a very short period of time. This is after all what we do everyday. If a candidate thinks not having sql injection in the code is an explicit requirement or is going to take a lot of time, then that is not the person for the job.

Here is a pretty simple flask implementation of your laundry list - api validation, db migration, no sql injections, no n+1 queries, good naming, swagger ui...

from flask import Flask, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_apispec import use_kwargs, marshal_with, MethodResource, FlaskApiSpec
from marshmallow import Schema, fields, validate, ValidationError
from sqlalchemy.orm import joinedload

# setup

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///dev.db'
db = SQLAlchemy(app)
migrate = Migrate(app, db)
docs = FlaskApiSpec(app)

# Return validation errors as JSON
@app.errorhandler(422)
@app.errorhandler(400)
def handle_error(err):
headers = err.data.get("headers", None)
messages = err.data.get("messages", ["Invalid request."])
if headers:
return jsonify({"errors": messages}), err.code, headers
else:
return jsonify({"errors": messages}), err.code

# models

POST_TITLE_LENGTH_MAX = 80
POST_CONTENT_LENGTH_MAX = 5000

class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(POST_TITLE_LENGTH_MAX), nullable=False)
content = db.Column(db.String(POST_CONTENT_LENGTH_MAX), nullable=False)

comments = db.relationship('Comment', back_populates='post')

COMMENTER_LENGTH_MAX = POST_TITLE_LENGTH_MAX
COMMENT_LENGTH_MAX = POST_CONTENT_LENGTH_MAX

class Comment(db.Model):
id = db.Column(db.Integer, primary_key=True)
commenter = db.Column(db.String(COMMENTER_LENGTH_MAX), nullable=False)
comment = db.Column(db.String(COMMENT_LENGTH_MAX), nullable=False)

post_id = db.Column(db.ForeignKey('post.id'))
post = db.relationship('Post', uselist=False, back_populates='comments')

# schemas

class CommentSchema(Schema):
id = fields.Int(required=True, dump_only=True)
commenter = fields.Str(
required=True, validate=validate.Length(max=COMMENTER_LENGTH_MAX))
comment = fields.Str(
required=True, validate=validate.Length(max=COMMENT_LENGTH_MAX))

class PostSchema(Schema):
id = fields.Int(required=True, dump_only=True)
title = fields.Str(required=True, validate=validate.Length(
max=POST_TITLE_LENGTH_MAX))
content = fields.Str(required=True, validate=validate.Length(
max=POST_CONTENT_LENGTH_MAX))
comments = fields.Nested(CommentSchema, many=True, dump_only=True)

# dal

def get_post_list():
return Post.query.all()

def get_post(post_id):
return Post.query.options(joinedload(Post.comments)).get(post_id)

def create_post(title, content):
post = Post(title=title, content=content)
db.session.add(post)
db.session.commit()
return post

def add_comment_to_post(post_id, commenter, comment):
comment = Comment(commenter=commenter, comment=comment, post_id=post_id)
db.session.add(comment)
db.session.commit()
return comment

# views

class PostListResource(MethodResource):
@marshal_with(PostSchema(many=True))
def get(self):
return get_post_list()

@marshal_with(PostSchema)
@use_kwargs(PostSchema)
def post(self, title, content):
return create_post(title, content)

class PostResource(MethodResource):
@marshal_with(PostSchema)
def get(self, post_id):
return get_post(post_id)

class PostCommentResource(MethodResource):
@marshal_with(CommentSchema)
@use_kwargs(CommentSchema)
def post(self, post_id, commenter, comment):
return add_comment_to_post(post_id, commenter, comment)

app.add_url_rule('/posts', view_func=PostListResource.as_view('post_list'))
docs.register(PostListResource, endpoint='post_list')

app.add_url_rule('/posts/<int:post_id>',
view_func=PostResource.as_view('post'))
docs.register(PostResource, endpoint='post')

app.add_url_rule('/posts/<int:post_id>/comments',
view_func=PostCommentResource.as_view('post_comment'))
docs.register(PostCommentResource, endpoint='post_comment')