Skip to content

Adding a Route

Decide: view or API?

Every new endpoint is either a view (returns HTML) or an API (returns JSON). They live in different files, have different decorators, and have different return conventions.

ViewAPI
File locationorchid/views/orchid/api/
Returnsrender_template() or redirect()jsonify()
URL prefixe.g. /admin/, /user/e.g. /api/admin/, /api/user/
HTTP methodsMostly GETMostly POST

Step-by-step

  1. Find the right blueprint file

    Views are organized by feature. Pick the file that matches your feature:

    FeatureFile
    Admin case managementorchid/views/admin_case_management.py
    Admin contactsorchid/views/admin_contacts.py
    Admin matchingorchid/views/admin_matching.py
    User portalorchid/views/user.py
    Provider portalorchid/views/provider.py
    Forms/intakeorchid/views/forms.py
    Reportsorchid/views/reports.py

    If none fit, create a new file (see below).

  2. Add the route function

    @admin_case_management_views.route('/admin/case-management/my-new-page/<int:case_id>')
    @authcheck(user_types=authgroups.admin)
    def my_new_page(case_id):
    case = models.Case.query.filter_by(id=case_id).first_or_404()
    return render_template(
    'admin/case_management/my_new_page.html',
    case=case
    )
  3. Create the template

    Create templates/admin/case_management/my_new_page.html. See Jinja Macros for available macros to use.

  4. Test

    Navigate to the URL and verify the page renders. Check that:

    • The auth decorator correctly blocks unauthorized users
    • Template variables are populated
    • No 500 errors in the console

Auth decorators

Always add an auth decorator to every route. The right decorator depends on the route type:

# For HTML view routes — redirects to login on failure
@authcheck(user_types=authgroups.admin)
@authcheck(user_types=authgroups.user)
@authcheck(user_types=authgroups.all)
# For API routes — returns 403 JSON on failure
@authcheck_api(user_types=authgroups.admin)
@authcheck_api(user_types=authgroups.user)
# For confidential cases (restricts to assigned team)
@confidential_authcheck

authgroups is defined in orchid/utils/auth.py:

authgroups.admin = ('admin', 'admin-master', 'admin-legal', ...)
authgroups.user = ('donor', 'surrogate', 'IP', 'IP rep', 'sperm_donor')
authgroups.all = (combined)

Creating a new blueprint (when necessary)

If no existing blueprint file fits your feature, create a new one. Only do this for genuinely new feature areas — adding routes to existing files is almost always the right call.

  1. Create orchid/views/my_feature.py:
from flask import Blueprint, render_template
from orchid.utils.auth import authcheck, authgroups
from orchid import models
my_feature_views = Blueprint('my_feature_views', __name__)
@my_feature_views.route('/my-feature/')
@authcheck(user_types=authgroups.admin)
def my_feature_index():
return render_template('my_feature/index.html')
  1. Register it in flaskapp.py:
from orchid.views.my_feature import my_feature_views
# ...
app.register_blueprint(my_feature_views)