ERPNext.com Frappe Cloud Support Partners Foundation Frappe School

Frappe tools for API design

Hi,
I need to code an API on top of frappe and don’t want to use the provided REST API for several reasons.
I am searching for some utils that will help me in designing REST API with frappe.

  1. route easily the request to the right method such as @app.route in flask. (parameter support)
  2. build json response easily
  3. redirect
    i know that i can customize the frappe.local.response but maybe there are some higher levels API’s i don’t know

Thanks

Can you perhaps say a bit more about what you’re trying to do? I can’t really imagine why you would want to implement a comprehensive RESTful interface on top of the one that already exists.

If you’re looking to implement more complex procedural calls, you can expose arbitrary Python methods with the whitelist decorator.
https://frappeframework.com/docs/user/en/guides/integration/rest_api#rpc

Is that the kind of thing you’re looking for?

Hi,
no RPC is not what i am looking for.
For exemple i want to implement the following endpoints:

  1. resource order:
    a. GET /order retrieve list of order (Already supported with built in API)
    b. GET /order/{order-id} retrieve specific order (Already supported with built in API)
    c. GET /order/{order-id}/tracking retrieve tracking of specific order (not supported, i need to redesign the order doc to add tracking link to it which is not available at order creation)
    d. if i want the call to triger some action i have to us RPC what i don’t want
    i also want to choose which fields i want the API user to see. i also need to aggregate information from other doctype etc

Option 1: proxy specific routes as you need. Setup a proxy server that takes routes that you mentioned and forward it to Frappe routes internally.

Option 2: use frappe as python library in your rest api Serverless function using Frappe Framework

Thanks for your reply.
i will try your options.
I post here what i implemented currently:
I defined one whitelisted method that will be in charge of “routing” the request to the right handler handle_request. I defined a base handler that has four methods: handle_get, handle_post, handle_put, handle_delete
then according to the frappe.request.path path i am routing the request to an handler that inherit from the base handler.
The downside of my solution is that i need to implement all the abstraction / utils of standards backend frameworks but i can use the frappe API to handle easily the requests.
Using framework like FastAPI or Flask will solve my problem but i will not be able to use the frappe API and will have to define tons of whitelisted methods. This is the trade-off i think
Tell me if i’m wrong
Thanks

After reviewing the Option 2 i was wrong, this solve my problem elegantly.
Thanks @revant_one!

In case of option 2 on a dedicated server you’ll anyway need to use nginx.
If that is the case then just use option 1 with nginx.

Example for reference

(DO NOT COPY-PASTE)

upstream backend {
    server sitename:8000;
}

server {
    listen 8080;
    server_name localhost;
    root /var/www/html;

    location /masked/route {
        try_files $uri $uri/ @backend;
    }

    location @backend {
        proxy_redirect off;
        proxy_http_version 1.1;
        proxy_pass http://backend/api/method/dot.path.to.function;
        proxy_set_header Host $host ;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Sorry but i still not understand.
What i want to do now is that:
Deploy a fastapi app within the bench that uses data of an existing site by connecting to it.
could you tell me if the below step are the right ones

  1. installing the fastapi frappe app
  2. run unvicorn command with the right SITE_NAME and SITES_PATH on port 3000 (for exemple)
  3. edit the nginx.cnf within the config dir of the bench and add a new server that listen to port 3000

if it’s right i have one question, how to do the step 2 ? i can run the uvicorn command via ssh and persist it after exit or there is a more stable way ?

sorry if i lack knowledge devops is not my domain but i want to learn ! :slight_smile:
Thanks a lot @revant_one

add it as part of supervisor config.

supervisor is used by frappe, check the ~/frappe-bench/config/supervisor.conf for reference, check how the socketio and gunicorn runs. Make one more file that runs your app and place it in /etc/supervisor/conf.d/fast.conf. Restart supervisor after that.

Thanks @revant_one for your help i follow your advise and i think i managed it to work i am sharing here the content of the .conf file i created within the /etc/supervisor/conf.d dir

[program:frappe-fastapi]
command=/home/frappe/frappe-bench/env/bin/uvicorn {YOUR_APP}.main:app --port 3000
autostart=true
autorestart=true
stdout_logfile=/home/frappe/frappe-bench/logs/frappe-fastapi.log
stderr_logfile=/home/frappe/frappe-bench/logs/frappe-fastapi.error.log
priority=1
user=frappe
environment=SITE_NAME={YOUR SITE NAME},SITES_PATH=/home/frappe/frappe-bench/sites
directory=/home/frappe/frappe-bench/sites

[group:fastapi]
programs=frappe-fastapi

now i have to edit the conf file of nginx but i have the same problem as above, how to persist the changes even after runing bench setup nginx or same king of commands that override the nginx.conf

Thanks

bench setup nginx is not configurable for advance setups like these.

You can either write a bench command or simple bash script that does the patching and run that command e.g. bench setup nginx && {patch script}

Sorry but i don’t know how to edit the nginx.conf. i am trying to use the above exemple you sent but as you said it’s for the first solution not the one i want to use.
I want to redirect incoming request to my api endpoint (let’s say) api.domain.com to the api app that’s run on port 3000 on the server. i am using the below conf

upstream api_app {
        server  localhost:3000;
}

server {
        listen 80;
        server_name api.domain.com;
	ssl_certificate      /etc/letsencrypt/live/api.domain.com/fullchain.pem;
	ssl_certificate_key  /etc/letsencrypt/live/api.domain.com/privkey.pem;
        location / {
		try_files $uri $uri/ @backend;
        }
}

What am i doing wrong ?

Thanks

I finally found how to solve my two last problem (nginx conf persistance and the conf itself).
I simple created the follow conf file in the /etc/nginx/conf.d dir then restart nginx

upstream backend {
    server 127.0.0.1:3000;
}


server {
	listen 443 ssl;
	

	server_name
		api.domain.com
		;

	root /home/frappe/frappe-bench/sites;

	
	
	proxy_buffer_size 128k;
	proxy_buffers 4 256k;
	proxy_busy_buffers_size 256k;

	
	ssl_certificate      /etc/letsencrypt/live/api.domain.com/fullchain.pem;
	ssl_certificate_key  /etc/letsencrypt/live/api.domain.com/privkey.pem;
	ssl_session_timeout  5m;
	ssl_session_cache shared:SSL:10m;
	ssl_session_tickets off;
	ssl_stapling on;
	ssl_stapling_verify on;
	ssl_protocols TLSv1.2 TLSv1.3;
	ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
	ssl_ecdh_curve secp384r1;
	ssl_prefer_server_ciphers on;

	location / {
		proxy_set_header X-Forwarded-For $remote_addr;
		proxy_set_header X-Forwarded-Proto $scheme;
		proxy_set_header X-Frappe-Site-Name $site_name_brwlxdj;
		proxy_set_header Host $host;
		proxy_set_header X-Use-X-Accel-Redirect True;
		proxy_read_timeout 120;
		proxy_redirect off;

		proxy_pass  http://backend;
	}         
}

# http to https redirect
	server {
		listen 80;
		server_name
			api.domain.com
			;

		return 301 https://$host$request_uri;
	}

Thans a lot revant for your help !